简体   繁体   English

同步:线程以相同的顺序执行两个关键部分

[英]synchronization : Threads execute two critical sections in same order

I have the following kind of code: 我有以下类型的代码:

synchronized block1 {
    //only one thread in the block
}

{lot of code where synchronization not necessary}

synchronized block2 {
    //only one thread in the block. 
    //All the threads that executed block1 before this thread should have already executed this block.
}

Each thread first executes block1, non synchronized block, and block2 in that same order. 每个线程首先以相同的顺序执行block1,非同步块和block2。

If thread T1 executes block1 before thread T2, then T1 should execute block2 before T2. 如果线程T1在线程T2之前执行block1,则T1应该在T2之前执行block2。 There are more than two threads. 有两个以上的线程。

Is there a way to achieve this in java? 有没有一种方法可以在Java中实现呢?

This basically creates a queue that threads will wait in until their number comes up. 基本上,这将创建一个队列,线程将等待直到队列编号出现。 [UPDATED] [更新]

private AtomicInteger place = new AtomicInteger(0);
private AtomicInteger currentPlaceInQueue = new AtomicInteger(0);
private ReentrantLock lock = new ReentrantLock();
private Condition notNext = lock.newCondition();

public void method() {

   ThreadLocal position = new ThreadLocal();

   synchronized(this) {
      //Your code
      position.set(place.getAndIncrement());
   }

   // More code

   lock.lock();
   while ((int) currentPlaceInQueue.get() != position.get()) {
      notNext.await();
   }
    // More code
   lock.unlock();
   currentPlaceInQueue.getAndIncrement();
   notNext.notifyAll();
 }

As I understand Critical Section #2 MUST be executed in the same order as Critical Section #1 据我了解,关键部分#2的执行顺序必须与关键部分#1相同

If thread T1 executes block1 before thread T2, then T1 should execute block2 before T2. 如果线程T1在线程T2之前执行block1,则T1应该在T2之前执行block2。 There are more than two threads. 有两个以上的线程。

Then a Queue might be used to ensure the order of execution. 然后可以使用队列来确保执行顺序。

private Object lock = new Object();
private Queue<Thread> threadQueue = new ArrayDeque<>();

// https://stackoverflow.com/questions/32353283/synchronization-threads-execute-two-critical-sections-in-same-order
public void executeCriticalSectionsInOrder() throws InterruptedException {
    // Critical Section #1
    synchronized (lock){
        // synchronized code #1

        // Add self to queue
        threadQueue.add(Thread.currentThread());
    }

    // {lot of code where synchronization not necessary}

    // Critical Section #2
    synchronized (lock) {
        //All the threads that executed block1 before this thread should have already executed this block.
        // Wait turn
        Thread t = threadQueue.element(); // Do not remove until it is self
        while (t != Thread.currentThread()) {
            lock.wait();
            // After sleep try again
            t = threadQueue.element();
        }
        // Verified own turn. Update status
        threadQueue.remove();

        // synchronized code #2

        lock.notifyAll(); // Awake any waiting thread after exiting section.
    }

However If one thread dies/exits without removing itself from the queue, then following threads will be blocked indefinetely. 但是,如果一个线程死掉/退出而没有将自己从队列中删除,那么随后的线程将被不确定地阻塞。 Maybe add a finally block to do the housekeeping? 也许添加一个finally块来做家务?

Note : In Nicholas Robinson's answer a position order was suggested instead of a queue, which seems slightly more efficient. 注意 :在尼古拉斯·罗宾逊(Nicholas Robinson)的答案中 ,建议使用排名顺序而不是队列,这似乎更有效率。

The synchronized blocks in your example are a red herring. 您的示例中的synchronized块是红色鲱鱼。 Your problem is, you have N threads, and you have two blocks of code, and you want to make sure that none of the threads enters the second block until all of them have finished the first block. 您的问题是,您有N个线程,并且有两个代码块,并且您想确保在所有线程都完成第一个块之前,没有一个线程进入第二个块。

You use a CyclicBarrier for that. 为此,请使用CyclicBarrier http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html

You should be able to use a Lock which you take before calling block1 and release after calling block2 . 您应该能够使用在调用block1之前使用的Lock ,并在调用block1之后释放block2

static Lock lock = new ReentrantLock();
Random random = new Random();

public void block1() throws InterruptedException {
    System.out.println("Enter block 1");
    Thread.sleep(random.nextInt(500));
    System.out.println("Leave block 1");
}

public void block2() throws InterruptedException {
    System.out.println("Enter block 2");
    Thread.sleep(random.nextInt(500));
    System.out.println("Leave block 2");
}

private class BlockTester implements Runnable {

    long start = System.currentTimeMillis();

    @Override
    public void run() {
        while (System.currentTimeMillis() < start + 10000) {
            lock.lock();
            try {
                System.out.println("Thread: " + Thread.currentThread().getName());
                block1();
                block2();
            } catch (InterruptedException ex) {
                System.out.println("Interrupted");
            } finally {
                lock.unlock();
            }
        }
    }
}

public void test() throws InterruptedException {
    Thread[] blockTesters = {
        new Thread(new BlockTester()),
        new Thread(new BlockTester()),
        new Thread(new BlockTester()),
        new Thread(new BlockTester()),
        new Thread(new BlockTester())
    };
    for (Thread t : blockTesters) {
        t.start();
    }
    for (Thread t : blockTesters) {
        t.join();
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM