我正在使用应用程序逻辑线程和数据库访问线程创建Java应用程序。 它们都在应用程序的整个生命周期中持续存在,并且两者都需要同时运行(一个与服务器通信,一个与用户通信;当应用程序完全启动时,我需要两个他们工作)。
但是,在启动时,我需要确保最初应用程序线程等待,直到db线程准备就绪(当前通过轮询自定义方法dbthread.isReady()
确定)。
我不介意app线程阻塞,直到db线程准备就绪。
Thread.join()
看起来不像解决方案 - 数据库线程仅在应用程序关闭时退出。
while (!dbthread.isReady()) {}
有点工作,但空循环会消耗很多处理器周期。
还有其他想法吗?感谢。
答案 0 :(得分:132)
使用计数器为1的CountDownLatch。
CountDownLatch latch = new CountDownLatch(1);
现在在app线程中执行 -
latch.await();
在db线程中,完成后,执行 -
latch.countDown();
答案 1 :(得分:126)
我建议您在开始进行多线程的神奇世界之前先阅读Sun's Java Concurrency之类的教程。
还有很多好书(google为“Java中的并发编程”,“Java Concurrency in Practice”。
要得到答案:
在必须等待dbThread
的代码中,您必须具有以下内容:
//do some work
synchronized(objectYouNeedToLockOn){
while (!dbThread.isReady()){
objectYouNeedToLockOn.wait();
}
}
//continue with work after dbThread is ready
在dbThread
的方法中,您需要执行以下操作:
//do db work
synchronized(objectYouNeedToLockOn){
//set ready flag to true (so isReady returns true)
ready = true;
objectYouNeedToLockOn.notifyAll();
}
//end thread run method here
我在这些示例中使用的objectYouNeedToLockOn
最好是您需要从每个线程同时操作的对象,或者您可以为此目的创建单独的Object
(我不建议制作方法本身同步):
private final Object lock = new Object();
//now use lock in your synchronized blocks
为了进一步理解:
还有其他(有时更好的)方法来完成上述工作,例如:使用CountdownLatches
等。从Java 5开始,java.util.concurrent
包和子包中有很多漂亮的并发类。你真的需要在网上找到材料来了解并发性,或者获得一本好书。
答案 2 :(得分:22)
要求::
- 等待执行下一个线程,直到上一个完成。
- 下一个线程必须在前一个线程停止之前才能启动,无论时间消耗如何。
- 必须简单易用。
醇>
答案::
@See java.util.concurrent.Future.get()doc。
future.get()如果需要等待计算完成,然后检索其结果。
工作完成!!见下面的例子
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;
public class ThreadTest {
public void print(String m) {
System.out.println(m);
}
public class One implements Callable<Integer> {
public Integer call() throws Exception {
print("One...");
Thread.sleep(6000);
print("One!!");
return 100;
}
}
public class Two implements Callable<String> {
public String call() throws Exception {
print("Two...");
Thread.sleep(1000);
print("Two!!");
return "Done";
}
}
public class Three implements Callable<Boolean> {
public Boolean call() throws Exception {
print("Three...");
Thread.sleep(2000);
print("Three!!");
return true;
}
}
/**
* @See java.util.concurrent.Future.get() doc
* <p>
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*/
@Test
public void poolRun() throws InterruptedException, ExecutionException {
int n = 3;
// Build a fixed number of thread pool
ExecutorService pool = Executors.newFixedThreadPool(n);
// Wait until One finishes it's task.
pool.submit(new One()).get();
// Wait until Two finishes it's task.
pool.submit(new Two()).get();
// Wait until Three finishes it's task.
pool.submit(new Three()).get();
pool.shutdown();
}
}
该程序的输出::
One...
One!!
Two...
Two!!
Three...
Three!!
你可以看到在完成任务之前需要6秒,这比其他线程更大。所以Future.get()会等到任务完成。
如果你不使用future.get(),它不会等待完成并执行基于时间的消耗。
使用Java并发运气好运。
答案 3 :(得分:8)
public class ThreadEvent {
private final Object lock = new Object();
public void signal() {
synchronized (lock) {
lock.notify();
}
}
public void await() throws InterruptedException {
synchronized (lock) {
lock.wait();
}
}
}
然后使用这个类:
创建一个ThreadEvent:
ThreadEvent resultsReady = new ThreadEvent();
在方法中,这是等待结果:
resultsReady.await();
在创建所有结果后创建结果的方法中:
resultsReady.signal();
修改强>
(很抱歉编辑这篇文章,但这段代码的竞争状况非常糟糕,我没有足够的声誉来评论)
如果您100%确定在await()之后调用signal(),则只能使用此方法。这就是为什么你不能使用像Java这样的Java对象的一个重要原因。 Windows事件。
如果代码按此顺序运行:
Thread 1: resultsReady.signal();
Thread 2: resultsReady.await();
然后线程2将永远等待。这是因为Object.notify()只唤醒当前正在运行的线程之一。稍后等待的线程不会被唤醒。这与我期望事件工作的方式非常不同,事件发出信号直到a)等待或b)显式重置。
注意:大多数情况下,您应该使用notifyAll(),但这与上面的“永远等待”问题无关。
答案 4 :(得分:7)
从java.util.concurrent
包中提取CountDownLatch类,它提供了更高级别的同步机制,远比任何低级别的东西都更不容易出错。
答案 5 :(得分:6)
您可以使用两个线程之间共享的Exchanger对象来执行此操作:
private Exchanger<String> myDataExchanger = new Exchanger<String>();
// Wait for thread's output
String data;
try {
data = myDataExchanger.exchange("");
} catch (InterruptedException e1) {
// Handle Exceptions
}
在第二个帖子中:
try {
myDataExchanger.exchange(data)
} catch (InterruptedException e) {
}
正如其他人所说,不要采取这种轻松的,只是复制粘贴代码。先做一些阅读。
答案 6 :(得分:6)
很多正确的答案,但没有一个简单的例子..这是一种简单易懂的方法CountDownLatch
:
//inside your currentThread.. lets call it Thread_Main
//1
final CountDownLatch latch = new CountDownLatch(1);
//2
// launch thread#2
new Thread(new Runnable() {
@Override
public void run() {
//4
//do your logic here in thread#2
//then release the lock
//5
latch.countDown();
}
}).start();
try {
//3 this method will block the thread of latch untill its released later from thread#2
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//6
// You reach here after latch.countDown() is called from thread#2
答案 7 :(得分:4)
java.lang.concurrent
包中的Future接口旨在提供对在另一个帖子中计算的结果的访问权。
查看FutureTask和ExecutorService,了解现成的做法。
我强烈建议任何对并发和多线程感兴趣的人阅读Java Concurrency In Practice。它显然集中在Java上,但是对于任何使用其他语言的人来说都有很多东西。
答案 8 :(得分:2)
这适用于所有语言:
您希望拥有一个事件/侦听器模型。您创建一个等待特定事件的侦听器。该事件将在您的工作线程中创建(或发出信号)。这将阻止线程,直到接收到信号而不是不断轮询以查看是否满足条件,如您目前的解决方案。
您的情况是导致死锁的最常见原因之一 - 确保您发出其他线程的信号,而不管可能发生的错误。示例 - 如果您的应用程序抛出异常 - 并且从不调用该方法来通知另一个事情已完成。这将使其他线程永远不会“醒来”。
我建议您在实施案例之前,先了解使用事件和事件处理程序的概念,以便更好地理解这个范例。
或者,您可以使用互斥锁使用阻塞函数调用 - 这将导致线程等待资源空闲。要做到这一点,您需要良好的线程同步 - 例如:
Thread-A Locks lock-a
Run thread-B
Thread-B waits for lock-a
Thread-A unlocks lock-a (causing Thread-B to continue)
Thread-A waits for lock-b
Thread-B completes and unlocks lock-b
答案 9 :(得分:2)
如果你想要快速和脏的东西,你可以在你的while循环中添加一个Thread.sleep()调用。如果数据库库是您无法更改的,那么实际上没有其他简单的解决方案。轮询数据库直到准备好等待时间不会导致性能下降。
while (!dbthread.isReady()) {
Thread.sleep(250);
}
几乎没有什么可以称为优雅代码,但完成工作。
如果您可以修改数据库代码,那么使用其他答案中建议的互斥锁会更好。
答案 10 :(得分:2)
您可以从一个线程中的阻塞队列中读取并在另一个线程中写入它。
答案 11 :(得分:1)
自
join()
已被排除您可以考虑其他选择:
ExecutorService
的invokeAll(Collection<? extends Callable<T>> tasks)
执行给定的任务,在完成所有任务后返回持有其状态和结果的Futures列表。
Executors
的ForkJoinPool或newWorkStealingPool(自Java 8发布以来)
使用所有可用处理器作为目标并行级别创建工作窃取线程池。
答案 12 :(得分:-1)