线程与锁和条件同步
Thread synchronization with Locks and Conditions
我在理解 Java 中的锁和条件时遇到问题,我不明白为什么我的代码会陷入死锁。
我的程序由一个主线程和一个子线程组成,子线程是主线程的成员。两个线程 运行 在一个无限循环中,子线程的循环应该在它从主线程接收到 startCond 信号后立即执行一次迭代。 Mainthread 应该等待 finishCond 信号继续。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
LockTest lt = new LockTest();
Mainthread m1 = lt.new Mainthread();
m1.start();
}
public class Mainthread extends Thread {
private Subthread sub = new Subthread();
public void run(){
System.out.println("Main start");
sub.start();
while(!isInterrupted()) {
try {
sub.getStartLock().lock();
sub.getStartCond().signal();
sub.getStartLock().unlock();
sub.getFinishLock().lock();
sub.getFinishCond().await();
sub.getFinishLock().unlock();
System.out.println("Main done");
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Subthread extends Thread {
private Lock startLock = new ReentrantLock();
private Lock finishLock = new ReentrantLock();
private Condition startCond = startLock.newCondition();
private Condition finishCond = finishLock.newCondition();
public Lock getStartLock() {
return startLock;
}
public Lock getFinishLock() {
return finishLock;
}
public Condition getStartCond() {
return startCond;
}
public Condition getFinishCond() {
return finishCond;
}
public void run() {
System.out.println("Sub start");
while(!isInterrupted()) {
try {
startLock.lock();
startCond.await();
startLock.unlock();
finishLock.lock();
finishCond.signal();
finishLock.unlock();
System.out.println("Sub done");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
我的预期输出是:
主要完成
子完成
(在循环中执行多少次就重复多少次)。
有没有更容易解决这个问题的方法?
主线程启动,它创建新的子线程并启动它,但是在线程上调用 start 并不意味着该线程会立即接收处理器并且它的代码将被实际执行。
主要调用sub.getStartCond().signal();但此时子线程仍未 运行ning 所以它错过了这个信号。
主要,等待 finishCond。
Sub 开始执行它的 运行 方法,它进入开始条件并永远等待它。
僵局。
信号仅唤醒当前等待的线程,它不会 'remember' 之前的调用。
改用信号量http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html
它具有 'counting the permits' 的语义。
可能有更可靠的方法来做到这一点。我建议使用 CountDownLatch
初始化计数为 1,而不是条件。主线程和 child 线程都可以共享同一个闩锁实例(因为主线程拥有 child 这应该很容易)。 child 将调用 await()
,当您需要向 child 发送信号时,主程序将调用 countDown()
。我建议你制作闩锁 private
和 final
.
class ChildThread extends Thread {
private final CountDownLatch signal;
public ChildThread(CountDownLatch signal) {
this.signal = signal;
}
public void run() {
// The loop is necessary in case we get interrupted.
while (true) {
try {
signal.await();
break;
} catch(InterruptedException ignored) {
}
}
// do the work...
}
}
class MainThread extends Thread {
private final ChildThread child;
private final CountDownLatch signalToChild;
public MainThread() {
signalToChild = new CountDownLatch(1);
child = new ChildThread(signalToChild);
}
public void run() {
// I can start the child right away but I'd rather make sure it
// starts if the main thread has started.
child.start();
// prework
// let's signal the child
signalToChild.countDown();
// now the child is working, let's go on with the main thread work
}
}
这是可行的,因为主线程和 child 线程实际上共享状态,即锁存器。主线程是否在 child 线程实际启动之前递减闩锁并不重要,因为 child 将检查此共享状态以了解它是否可以启动。
我在理解 Java 中的锁和条件时遇到问题,我不明白为什么我的代码会陷入死锁。
我的程序由一个主线程和一个子线程组成,子线程是主线程的成员。两个线程 运行 在一个无限循环中,子线程的循环应该在它从主线程接收到 startCond 信号后立即执行一次迭代。 Mainthread 应该等待 finishCond 信号继续。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
LockTest lt = new LockTest();
Mainthread m1 = lt.new Mainthread();
m1.start();
}
public class Mainthread extends Thread {
private Subthread sub = new Subthread();
public void run(){
System.out.println("Main start");
sub.start();
while(!isInterrupted()) {
try {
sub.getStartLock().lock();
sub.getStartCond().signal();
sub.getStartLock().unlock();
sub.getFinishLock().lock();
sub.getFinishCond().await();
sub.getFinishLock().unlock();
System.out.println("Main done");
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Subthread extends Thread {
private Lock startLock = new ReentrantLock();
private Lock finishLock = new ReentrantLock();
private Condition startCond = startLock.newCondition();
private Condition finishCond = finishLock.newCondition();
public Lock getStartLock() {
return startLock;
}
public Lock getFinishLock() {
return finishLock;
}
public Condition getStartCond() {
return startCond;
}
public Condition getFinishCond() {
return finishCond;
}
public void run() {
System.out.println("Sub start");
while(!isInterrupted()) {
try {
startLock.lock();
startCond.await();
startLock.unlock();
finishLock.lock();
finishCond.signal();
finishLock.unlock();
System.out.println("Sub done");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
我的预期输出是:
主要完成
子完成
(在循环中执行多少次就重复多少次)。
有没有更容易解决这个问题的方法?
主线程启动,它创建新的子线程并启动它,但是在线程上调用 start 并不意味着该线程会立即接收处理器并且它的代码将被实际执行。
主要调用sub.getStartCond().signal();但此时子线程仍未 运行ning 所以它错过了这个信号。
主要,等待 finishCond。
Sub 开始执行它的 运行 方法,它进入开始条件并永远等待它。
僵局。
信号仅唤醒当前等待的线程,它不会 'remember' 之前的调用。
改用信号量http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html
它具有 'counting the permits' 的语义。
可能有更可靠的方法来做到这一点。我建议使用 CountDownLatch
初始化计数为 1,而不是条件。主线程和 child 线程都可以共享同一个闩锁实例(因为主线程拥有 child 这应该很容易)。 child 将调用 await()
,当您需要向 child 发送信号时,主程序将调用 countDown()
。我建议你制作闩锁 private
和 final
.
class ChildThread extends Thread {
private final CountDownLatch signal;
public ChildThread(CountDownLatch signal) {
this.signal = signal;
}
public void run() {
// The loop is necessary in case we get interrupted.
while (true) {
try {
signal.await();
break;
} catch(InterruptedException ignored) {
}
}
// do the work...
}
}
class MainThread extends Thread {
private final ChildThread child;
private final CountDownLatch signalToChild;
public MainThread() {
signalToChild = new CountDownLatch(1);
child = new ChildThread(signalToChild);
}
public void run() {
// I can start the child right away but I'd rather make sure it
// starts if the main thread has started.
child.start();
// prework
// let's signal the child
signalToChild.countDown();
// now the child is working, let's go on with the main thread work
}
}
这是可行的,因为主线程和 child 线程实际上共享状态,即锁存器。主线程是否在 child 线程实际启动之前递减闩锁并不重要,因为 child 将检查此共享状态以了解它是否可以启动。