2015-03-08 16 views
-1

有一个变量(seqJ)需要由线程更新两次。当两个线程需要更新变量时如何锁定或同步变量?

有这一个线程执行的这个变量,也就是两个动作:

都被执行的方法

  1. IncrementAndGet(seqJ)

  2. 更新(seqJ)按照这里提到的确切顺序。

    第二个线程在更新方法被调用后应该能够调用IncrementAndGet。我试图在这些方法中使用同步(锁定),但我推理说,一旦线程释放其中一个方法的锁定,那么另一个线程就可以通过其他方法访问seqJ,这会给我带来虚假的结果。

    如何解决此问题?

    MainClass{ 
    public int incrementSeqJ() { 
        return seqJ.incrementAndGet(); 
    } 
    
    public int getSeqJ() { 
        return seqJ.get(); 
    } 
    
    public void updateSeqJ(int seqJ) { 
        if (this.seqJ.get() < seqJ) 
         this.seqJ.set(seqJ); 
    } 
    } 
    

    线程我在哪里使用这些方法:

    public void run() { 
        try { 
    
         //socks.setSoTimeout(500); 
         totalOrder = gma.totalOrder; 
    
         ObjectInputStream ois = new ObjectInputStream(socks.getInputStream()); //Get reading stream 
         ObjectOutputStream sendSSN = new ObjectOutputStream(socks.getOutputStream()); 
    
         int i = 0; 
         while (i < 2) { 
          recObj = ois.readObject(); 
          //Determine the type of message object received and do actions according to toString values. Use Switch cases. 
          switch (Integer.parseInt(recObj.toString())) { 
    
           //Receive MessageObject and send SuggestedSequenceNumber object to sending AVD 
           case 1: { 
            MessageObject mo = (MessageObject) recObj; 
            updateUI = mo; 
    
            int seqJ; 
            /*if(mo.processNum == Integer.parseInt(gma.portStr)) 
             seqJ = gma.getSeqJ(); 
            else*/ 
             seqJ = gma.incrementSeqJ(); 
    
            mo.status = false; 
            mo.sSN = seqJ; 
            mo.sSNProcNum = Integer.parseInt(gma.portStr); 
    
            Log.d(ReceiverTask.class.getSimpleName(), "Case 1 Adding"); 
            totalOrder.addQ(mo); 
    
            Object msg = new SuggestedSequenceNumber(mo.mID, seqJ, mo.processNum, mo.sSNProcNum); 
            sendSSN.writeObject(msg); 
    
            break; 
           } 
    
           case 3: { 
            FinalMessage fm = (FinalMessage) recObj; 
    
            MessageObject mo = totalOrder.findInQ(fm.mID, fm.processNum); 
    
            if (mo == null) 
             throw new NoSuchElementException(); 
            else { 
             mo.sSN = fm.sSN; 
             mo.sSNProcNum = fm.sSNProcNum; 
             mo.status = true; 
            } 
    
            gma.updateSeqJ(fm.sSN); 
    
            Log.d(ReceiverTask.class.getSimpleName(), "Case 2 Adding"); 
            totalOrder.addQ(mo); 
    
            totalOrder.makeDeliverable(gma.getContentResolver()); 
    
            gma.runOnUiThread(
              new Runnable() { 
               public void run() { 
                publishProgress(gma, ReceiverTask.this.updateUI); 
               } 
              }); 
    
            ois.close(); 
            sendSSN.close(); 
            socks.close(); 
            break; 
           } 
          } 
          i++; 
         } 
        }catch(SocketTimeoutException ste){ Log.d(ReceiverTask.class.getSimpleName(), "Socket Timed Out"); } 
        catch(ClassNotFoundException cnfe){ Log.d(ReceiverTask.class.getSimpleName(), "MessageObject not found"); } 
        catch(IOException e){ Log.d(ReceiverTask.class.getSimpleName(), e.toString()); } 
    } 
    

    }

    假设线程1是incrementSeqJ方法:它增加值并将它放回SeqJ。现在在代码的第二部分中,必须在thread1调用updateSeqJ方法之后,thread2才能访问incrementSeqJ方法。

+0

也许[''AtomicInteger''](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html)会解决你的问题吗? – NikolayKondratyev 2015-03-08 17:12:05

+1

请添加您的代码,您最后一段中的“错误”对我来说并不清楚。 – Smutje 2015-03-08 17:12:36

+0

@Smutje添加了代码。 – MessyCoder 2015-03-08 22:05:31

回答

0

为了确保线程1已完成更新seqJ线程2开始之前,你可以使用的CountDownLatch具有计数1:

CountDownLatch latch = new CountDownLatch(1); 

在线程2调用递增函数之前,有:

latch.await(); 

而在thread1中,在它调用更新函数之后,有:

latch.countDown(); 

await()是一个阻塞函数,它等待CountDownLatch向下计数到0.在这种情况下,CountDownLatch只有1的计数,所以一旦countDown()被调用一次,它将从1到0和await()将返回。

确保两个线程都指的是CountDownLatch的同一个实例。

+0

假设Thread1处于incrementSeqJ方法中:它将该值递增并将其存储回SeqJ。现在在代码的第二部分中,只有当thread1调用updateSeqJ方法时,thread2才会访问incrementSeqJ方法。 现在将这个代码工作? – MessyCoder 2015-03-09 14:04:49

+0

您可以使用CountDownLatch。当thread1完成执行updateSeqJ并让thread2等待锁存器倒数,然后调用incrementSeqJ之前,计数锁存器。在线查看Java的CountDownLatch示例。 – 2015-03-09 14:30:20

+0

请参阅我的更新回答,以及如何使用CountDownLatch解决您的问题的示例。 – 2015-03-09 14:52:59

2

只是想:如果你的逻辑规定必须按顺序调用这两个方法 - 为什么你的公共接口中有两种方法?

您可以只提供一种方法到您的客户端代码。该方法提取外观,然后调用其他两个(内部的,可能是私有的)方法;并释放锁定。

如果你认为这两种方法“属于”在一起,那么你不应该提供和接口,使它们可以分开调用它们。

相关问题