Java 循环同步死锁
我有以下Java类Java 循环同步死锁,java,concurrency,thread-safety,deadlock,Java,Concurrency,Thread Safety,Deadlock,我有以下Java类 public class Counter { private int value; public Counter(int value) { this.value = value; } public void setValue(int value) { this.value = value; } public void decrement() { this.value--;
public class Counter {
private int value;
public Counter(int value) {
this.value = value;
}
public void setValue(int value) {
this.value = value;
}
public void decrement() {
this.value--;
}
public int getValue() {
return this.value;
}
}
public class Cell extends Thread {
private Object sync;
private Counter counter;
public Cell(Object sync, Counter counter) {
this.sync = sync;
this.counter = counter;
}
public void run() {
for (int r=0; r<Simulation.ROUND_NUM; r++) {
// do something
synchronized(counter) {
counter.decrement();
counter.notifyAll();
}
synchronized(sync) {
try {
sync.wait();
}
catch (Exception ex) {}
}
}
}
}
public class Simulation extends Thread {
public static final int THREAD_NUM = 5;
public static final int ROUND_NUM = 5;
public Object sync = new Object();
private Counter counter = new Counter(THREAD_NUM);
public void run() {
for (int i=0; i<THREAD_NUM; i++) {
Cell c = new Cell(sync,counter);
c.start();
}
for (int i=0; i<ROUND_NUM; i++) {
synchronized(counter) {
while(counter.getValue() != 0) {
try {
counter.wait();
}
catch (Exception ex) {}
}
counter.setValue(THREAD_NUM);
}
synchronized(sync) {
sync.notifyAll();
}
}
}
}
公共类计数器{
私有int值;
公共计数器(int值){
这个值=值;
}
公共无效设置值(int值){
这个值=值;
}
公共无效减量(){
这是价值观;
}
public int getValue(){
返回此.value;
}
}
公共类单元扩展线程{
私有对象同步;
私人柜台;
公共单元格(对象同步、计数器){
this.sync=sync;
this.counter=计数器;
}
公开募捐{
对于(int r=0;r首先,您可以使用该类而不是所创建的计数器类。AtomicInteger类是线程安全的,因此您可以使用原子操作,如decrementAndGet和incrementAndGet
要实现等待每个单元线程完成的功能,您可以使用前面评论中提到的类似方法,甚至可以使用并发对象,在所有单元线程连接到屏障上之前暂停执行。通过其中一些并发对象,应该更容易控制多个线程。使用纯同步doe在你的代码中,似乎无法保证当执行sync.notifyAll()
时,所有单元格线程都会执行sync.wait()
。这是指最后一个单元格线程(在你的示例中是第五个)这需要抓取sync
的锁才能等待。但是模拟线程也在尝试同样的事情,而不确保每个人都在等待。这种竞争条件使得模拟有时在最后一个单元能够执行相同的操作并等待之前抓取锁
因为最后一个单元没有等待,所以没有得到通知,所以整个过程都被卡住了。
您可以通过添加System.out.println()作为每个synchronized(sync)
块的第一行,并相应地写入“等待同步”和“通知同步”来测试这一点。当您通知它时,您将看到只有4个线程在等待同步
为了确保模拟器通知时每个人都在等待,请将两个同步块嵌套在Cell\run()
中:
public class Counter {
private int value;
public Counter(int value) {
this.value = value;
}
public void setValue(int value) {
this.value = value;
}
public void decrement() {
this.value--;
}
public int getValue() {
return this.value;
}
public static void main(String[] args) {
new Simulation().start();
}
}
class Cell extends Thread {
private Object sync;
private Counter counter;
public Cell(Object sync, Counter counter) {
this.sync = sync;
this.counter = counter;
}
public void run() {
for (int r = 0; r < Simulation.ROUND_NUM; r++) {
// do something
synchronized (sync) {
synchronized (counter) {
counter.decrement();
counter.notifyAll();
}
try {
sync.wait();
} catch (Exception ignored) {}
}
}
}
}
class Simulation extends Thread {
public static final int THREAD_NUM = 900;
public static final int ROUND_NUM = 30;
public Object sync = new Object();
private Counter counter = new Counter(THREAD_NUM);
public void run() {
for (int i = 0; i < THREAD_NUM; i++) {
Cell c = new Cell(sync, counter);
c.start();
}
for (int i = 0; i < ROUND_NUM; i++) {
synchronized (counter) {
while (counter.getValue() != 0) {
try {
counter.wait();
} catch (Exception ex) {
}
}
counter.setValue(THREAD_NUM);
}
synchronized (sync) {
sync.notifyAll();
}
}
}
}
公共类计数器{
私有int值;
公共计数器(int值){
这个值=值;
}
公共无效设置值(int值){
这个值=值;
}
公共无效减量(){
这是价值观;
}
public int getValue(){
返回此.value;
}
公共静态void main(字符串[]args){
新建模拟().start();
}
}
类单元格扩展线程{
私有对象同步;
私人柜台;
公共单元格(对象同步、计数器){
this.sync=sync;
this.counter=计数器;
}
公开募捐{
对于(int r=0;r
您的代码可能会死锁,因为您无法保证在notifyAll发生时,单元线程实际处于wait()块中。以下是可能导致此问题的一系列事件:
模拟启动所有线程,并阻止等待0值
序列中的每个线程调用减量,然后调用counter.notifyAll,然后丢失其时间片
主线程已被通知、唤醒、发现计数器位于0、调用sync.notifyAll、循环到顶部并无限期等待
序列中的每个线程都有一个时间片,前进到wait()并无限期地等待
很好的例子!这不是死锁,因为根据定义,只有当一个线程同时持有多个锁,而另一个线程试图以不同的顺序获取相同的锁时,才会发生死锁。
我怀疑这里的问题是由单元格对象中发生的虚假唤醒引起的(如果模拟对象中发生虚假唤醒,则不会产生任何影响,因为在循环中调用wait(),将导致重新输入等待)。
单元中的虚假唤醒将导致额外的减量。这反过来将使测试while(counter.getValue()!=0)
被跳过。
将该条件更改为while(counter.getValue()>=0)
,则“死锁”应消失。请告知它是否有效。这不是死锁。您的主线程可能会错过计数器上的通知,并将卡在计数器上。wait()在0处读取后。使用jstack JDK工具分析线程在这种情况下所做的工作。旁注:如果您不明确需要使用线程
,等待
和通知
(并且您使用的是Java5或更高版本),那么您最好使用一个