简体   繁体   English

Syncronisize两个循环Java-Thread

[英]Syncronisize two looping Java-Thread

Two threads are running parallel: 两个线程并行运行:

Thread1: 线程1:

while(...) {
  <-- wait until thread2 is not in update()
  doWork();
}

Thread2: 线程2:

while(...) {
  doWork();
  <-- wait until thread1 is not in work()
  update();
}

I think the examples above explain what I try to do, but I have not really an idea how to do this synchronization. 我认为上面的例子解释了我尝试做的事情,但我真的不知道如何进行这种同步。 The update() -method of thread2 is critical an while it is executed thread2 have to wait. thread2update()方法是关键的,执行时thread2必须等待。

Edit: 编辑:

Thanks so far for the answers. 谢谢你的回答。 More than one is working well. 不止一个人运作良好。 I was asked what I am trying to do and I want to give an short update for that. 我被问到我要做什么,我想给出一个简短的更新。

Based on the currentState thread2 calculates the nextState and swap both before it repeats calculating endless. 基于currentState thread2计算nextState并在重复计算无穷大之前交换它们。 thread1 displays the 'currentState' in the gui for the user. thread1在gui中为用户显示'currentState'。

Thread1 should not display the currentState while the swaping is currently in progress. 当交换当前正在进行时,Thread1不应显示currentState

Thats all. 就这样。

Using a Lock would be the easiest approach. 使用Lock是最简单的方法。 Here's a demo of how simple it is. 这是一个简单的演示。 We create two Runnable and start them running under two threads. 我们创建了两个Runnable并在两个线程下运行它们。 We then wait for 30 seconds and then interrupt them and wait for them to finish. 然后我们等待30秒然后打断它们并等待它们完成。

// Lock shared between both threads.
final Lock lock = new ReentrantLock();
// Random numbers.
final Random random = new Random();

public void test() throws InterruptedException {
    // Process 1 is a runnable.
    Runnable p1 = new Runnable() {

        @Override
        public void run() {
            while (true) {
                try {
                    // Grab the lock.
                    lock.lock();
                    // Do my work.
                    doWork();
                } catch (InterruptedException ex) {
                    System.out.println("P1 Interrupted!");
                    break;
                } finally {
                    // Release the lock in a `finally` to ensure it can never be left locked.
                    lock.unlock();
                }
            }
        }

        private void doWork() throws InterruptedException {
            long wait = random.nextInt(2000);
            System.out.println("P1 Working ... " + wait);
            // Wait up to 2 seconds.
            Thread.sleep(wait);
            System.out.println("P1 Work done");
        }

    };

    Runnable p2 = new Runnable() {

        @Override
        public void run() {
            while (true) {
                try {
                    // Do my work.
                    doWork();
                    // Grab the lock.
                    lock.lock();
                    // Do my update.
                    update();
                } catch (InterruptedException ex) {
                    System.out.println("P2 Interrupted!");
                    break;
                } finally {
                    lock.unlock();
                }
            }
        }

        private void doWork() throws InterruptedException {
            long wait = random.nextInt(2000);
            System.out.println("P2 Working ... " + wait);
            // Wait up to 2 seconds.
            Thread.sleep(wait);
            System.out.println("P2 Work done");
        }

        private void update() throws InterruptedException {
            long wait = random.nextInt(2000);
            System.out.println("P2 Update ... " + wait);
            // Wait up to 2 seconds.
            Thread.sleep(wait);
            System.out.println("P2 Update done");
        }

    };

    // Create the two threads.
    Thread t1 = new Thread(p1);
    Thread t2 = new Thread(p2);
    // Start them up.
    t1.start();
    t2.start();
    // Wait 30 seconds - with narrative.
    for (int i = 0; i < 30; i++) {
        Thread.sleep(1000);
        System.out.println("Tick");

    }
    // Stop them.
    t1.interrupt();
    t2.interrupt();
    // Wait for them to stop.
    t1.join();
    t2.join();

}

Running this should demonstrate one of the problems with your design. 运行此应该可以证明您的设计存在一个问题。 Notice that the P2 Update rarely gets called. 请注意, P2 Update很少被调用。 This is because P1 spends very little time with the lock released and so starves the other thread. 这是因为P1在释放锁时花费的时间非常少,因此另一个线程也会挨饿。 See how in p1 it will lock.unlock() and then almost immediately do a lock.lock() again as it starts the loop again. 看看如何在p1 lock.unlock()然后几乎立即再次执行lock.lock() ,因为它再次启动循环。

One way would be to use locks (look at the java.util.concurrent.locks package) but I'd first think about whether the approach in general could be improved, eg with lock-free algorithms etc. 一种方法是使用锁(查看java.util.concurrent.locks包)但我首先考虑一般的方法是否可以改进,例如使用无锁算法等。

Another way could be the use of synchronized methods/blocks on a shared object. 另一种方法可以是在共享对象上使用synchronized方法/块。 That all depends on what you're actually trying to achieve, however. 然而,这完全取决于你实际想要实现的目标。

Example for using a synchronized block: 使用synchronized块的示例:

T1: T1:

while(...) {
  synchronized( SomeClass.class ) {
    doWork();
  }
}

T2: T2:

while(...) {
  doWork();
  synchronized( SomeClass.class ) {
    update();
  }
}

Here you'd synchronize on the same instance of Class<SomeClass> which would work as long you're using the same classloader. 在这里,您将同步Class<SomeClass>的同一个实例,只要您使用相同的类加载器就可以工作。

Keep in mind that you should make the synchronized blocks as small as possible in order to not add unnecessary blocking. 请记住,您应该使同步块尽可能小,以免添加不必要的阻塞。

Besides that, note that the synchronized block in T1 might make it hard for T2 to break in between two iterations. 除此之外,请注意T1中的同步块可能使T2很难在两次迭代之间中断。 The question, however, would be why it is designed that way. 然而,问题是它以这种方式设计的原因。

Edit 编辑

As an alternative to synchronizing the entire doWork() call you might want to think about what actually needs to be synchronized. 作为同步整个doWork()调用的替代方法,您可能需要考虑实际需要同步的内容。

As an example, take the following pseudo code: 例如,请使用以下伪代码:

  WorkResult doWork(SharedObject so) {
    Data data = so.loadData();
    WorkResult wr = doSomeLengthyWork(data);
    return wr;
  }

  void update(WorkResult wr, SharedObject so) {
    so.updateFromWorkResult( wr );
  }

If your situation is like this, you could just synchronize the calls to so.loadData() and so.updateFromWorkResult() and let the lengthy operation work on a copy of the data provided by so.loadData() . 如果您的情况是这样的,您可以将调用同步到so.loadData()so.updateFromWorkResult()并让冗长的操作对so.loadData()提供的数据副本so.loadData()

Edit 2: 编辑2:

Alternatively you could use a ReadWriteLock implementation: 或者,您可以使用ReadWriteLock实现:

T1:

while(...) {
  Lock readlock = readWriteLock.readLock();
  readlock.lock();

  doWork();

  readlock.unlock();  
}

T2: T2:

while(...) {
  doWork();

  Lock writelock= readWriteLock.writeLock();
  writelock.lock();

  update();

  writelock.unlock();
}

Note that I left exception handling etc. out for simplicity. 请注意,为了简单起见,我保留了异常处理等。

What you're basically doing here is aquire a read lock during work and a write lock while updating. 你在这里做的基本上是在工作期间获取读锁定,在更新时获取写锁定。 Multiple threads can work in parallel unless there's a write lock (multiple read locks are allowed) and updates would wait until all read locks have been released. 除非存在写锁定(允许多个读锁定),否则多个线程可以并行工作,并且更新将等到所有读锁定都被释放。 To achieve this you could use a ReentrantReadWriteLock in fair mode which should issue the locks in the order they are requested, ie when T2 asks for a lock while T1 is still reading it will get the lock next, despite T1 immediately asking for the lock again. 为了达到这个目的,你可以在公平模式下使用ReentrantReadWriteLock ,它应该按照它们被请求的顺序发出锁定,即T2在T1仍在读取时要求锁定时,它将接下来锁定,尽管T1立即再次要求锁定。

It's a pseudocode, really, but I hope the idea is understandable. 它确实是伪代码,但我希望这个想法是可以理解的。

ExecutorService es = ...;
Future t1work = new Future(...);
Future t2work = new Future(...);
Future t2update = new Future(...);

es.submit(t1work);
es.submit(t2work);

while (1) {
  if (t1work.isDone() && t2work.isDone())   es.submit(t2update);
  if (t1work.isDone() && t2update.isDone()) es.submit(t1work);
  if (t2work.isDone() && t2update.isDone()) es.submit(t2work);
  Thread.sleep(100);
}

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

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