为什么我的 java 同步方法会出现这个错误?
why is this mistake happening in my java synchronized method?
我是java大一新生,学习了java中线程与wait() notify()的通信,发生了一些蠢事:
事情是这样的:我想通过多线程不断地设置人类和获取人类,这意味着结果是(杰克男,玛丽女,杰克男,玛丽女......)
这是我的代码:
class Human {
private String name;
private String sex;
private boolean b = false;
public synchronized void set(String name, String sex) {
if (b) {
try {
this.wait();
} catch (InterruptedException e) {}
}
this.name = name;
this.sex = sex;
b = true;
this.notify();
}
public synchronized void get() {
if (!b) {
try {
this.wait();
} catch (InterruptedException e) {}
}
System.out.println(name+" "+sex);
b = false;
this.notify();
}
}
class SetHuman implements Runnable {
private Human h;
SetHuman(Human h) {
this.h = h;
}
public void run() {
int x = 0;
while(true) {
if (x==0) {
h.set("Jack","male");
} else {
h.set("Mary","female");
}
x = (x+1)%2;
}
}
}
class GetHuman implements Runnable {
private Human h;
GetHuman(Human h) {
this.h = h;
}
public void run() {
while (true) {
h.get();
}
}
}
class HumanDemo {
public static void main(String[]args) {
Human h = new Human();
SetHuman sh = new SetHuman(h);
GetHuman gh = new GetHuman(h);
Thread t1 = new Thread(sh);
Thread t2 = new Thread(gh);
t1.start();
t2.start();
}
}
当我 运行 HumanDemo 时,它起作用了:result
然后,我在我的同步函数set()和get()中添加了一个else判断,这件事发生了:
public synchronized void set(String name, String sex) {
if (b) {
try {
this.wait();
} catch (InterruptedException e) {}
} else {
this.name = name;
this.sex = sex;
b = true;
this.notify();
}
}
public synchronized void get() {
if (!b) {
try {
this.wait();
} catch (InterruptedException e) {}
} else {
System.out.println(name+" "+sex);
b = false;
this.notify();
}
}
new result
这是为什么?有人请告诉我为什么吗?谢谢^-^!
在您的第一个工作示例中,您的 set/get() 方法是互斥的。 this.wait() 将强制他们等待对方才能完成工作。
在您的第二个示例中,您使用 else 打破了它,因为无法保证在释放锁后哪个线程将获得 'this' 上的锁。这样,任意数量的 'set()' 可能会迷失到等待状态,永远不会将它们的值设置为 'this.name' 和 'this.sex'.
示例(gt=获取线程 st=设置线程):
main-method 启动线程 st
st: h.set("Jack","male"); b 为假 -> this.name = "Jack"; b=真; (现在无法保证 st 是否会再次执行,或者 gt 是否已经创建并会通过同步的 get() 方法在 'this' 上获得锁定。这次让 st 获得锁定。)
?main-method 启动线程 gt? (可能会晚些)
st: h.set("Mary","female"); b 为真 -> this.wait(); (现在 st 正在等待有人释放 'this' 上的锁。由于在 else 语句中设置了 this.name 和 this.sex,因此它永远不会设置其当前值,并且此调用使用 "Marry" 和 "female" 会丢失。所以下一个 gt 将执行。)
?main-method 启动线程 gt? (可能已经发生)
gt: b 为真 -> System.out.println(姓名+" "+性别); b = false ...(在方法结束时,gt 将释放 'this' 上的锁,现在 st 将离开等待状态并尝试为此获取锁。gt 也在尝试获取 'this' 再次。再次不能保证哪个线程将获得锁并现在可以执行。)
长话短说:
由于 set 方法中的 else,您正在丢弃 'random' 数量的 set 调用(因此不太可能存在交替顺序)。如果 set 方法中没有 else,它应该可以工作。尽管这会浪费对 get 的方法调用,只是 运行 进入等待状态 return 而没有完成任何工作。
对 wait()
的调用必须始终处于 while 循环中,如 the documentation 中所述。你正在这样做,这很好。但是你弄错的部分是 while 循环需要 inside synchronized
块才能线程安全:
while (!condition) {
// Wrong --- another thread might call notify or notifyAll
// when the program is at this point, where this thread
// will not detect it.
synchronized (h) {
h.wait();
}
}
while 循环必须始终在同步块内以确保线程安全:
synchronized (h) {
while (!condition) {
h.wait();
}
}
此外,中断是向您的线程发出的信号,表示有人希望它终止。不要忽视它。
到目前为止,最简单的方法是将整个循环放在 try/catch 中,这样 InterruptedException 将自动结束循环:
try {
synchronized (h) {
while (!condition) {
h.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
最后,永远不要写一个空的 catch
块。隐藏异常会使您的代码故障排除变得极其困难。
我是java大一新生,学习了java中线程与wait() notify()的通信,发生了一些蠢事:
事情是这样的:我想通过多线程不断地设置人类和获取人类,这意味着结果是(杰克男,玛丽女,杰克男,玛丽女......)
这是我的代码:
class Human {
private String name;
private String sex;
private boolean b = false;
public synchronized void set(String name, String sex) {
if (b) {
try {
this.wait();
} catch (InterruptedException e) {}
}
this.name = name;
this.sex = sex;
b = true;
this.notify();
}
public synchronized void get() {
if (!b) {
try {
this.wait();
} catch (InterruptedException e) {}
}
System.out.println(name+" "+sex);
b = false;
this.notify();
}
}
class SetHuman implements Runnable {
private Human h;
SetHuman(Human h) {
this.h = h;
}
public void run() {
int x = 0;
while(true) {
if (x==0) {
h.set("Jack","male");
} else {
h.set("Mary","female");
}
x = (x+1)%2;
}
}
}
class GetHuman implements Runnable {
private Human h;
GetHuman(Human h) {
this.h = h;
}
public void run() {
while (true) {
h.get();
}
}
}
class HumanDemo {
public static void main(String[]args) {
Human h = new Human();
SetHuman sh = new SetHuman(h);
GetHuman gh = new GetHuman(h);
Thread t1 = new Thread(sh);
Thread t2 = new Thread(gh);
t1.start();
t2.start();
}
}
当我 运行 HumanDemo 时,它起作用了:result
然后,我在我的同步函数set()和get()中添加了一个else判断,这件事发生了:
public synchronized void set(String name, String sex) {
if (b) {
try {
this.wait();
} catch (InterruptedException e) {}
} else {
this.name = name;
this.sex = sex;
b = true;
this.notify();
}
}
public synchronized void get() {
if (!b) {
try {
this.wait();
} catch (InterruptedException e) {}
} else {
System.out.println(name+" "+sex);
b = false;
this.notify();
}
}
new result
这是为什么?有人请告诉我为什么吗?谢谢^-^!
在您的第一个工作示例中,您的 set/get() 方法是互斥的。 this.wait() 将强制他们等待对方才能完成工作。
在您的第二个示例中,您使用 else 打破了它,因为无法保证在释放锁后哪个线程将获得 'this' 上的锁。这样,任意数量的 'set()' 可能会迷失到等待状态,永远不会将它们的值设置为 'this.name' 和 'this.sex'.
示例(gt=获取线程 st=设置线程):
main-method 启动线程 st
st: h.set("Jack","male"); b 为假 -> this.name = "Jack"; b=真; (现在无法保证 st 是否会再次执行,或者 gt 是否已经创建并会通过同步的 get() 方法在 'this' 上获得锁定。这次让 st 获得锁定。)
?main-method 启动线程 gt? (可能会晚些)
st: h.set("Mary","female"); b 为真 -> this.wait(); (现在 st 正在等待有人释放 'this' 上的锁。由于在 else 语句中设置了 this.name 和 this.sex,因此它永远不会设置其当前值,并且此调用使用 "Marry" 和 "female" 会丢失。所以下一个 gt 将执行。)
?main-method 启动线程 gt? (可能已经发生)
gt: b 为真 -> System.out.println(姓名+" "+性别); b = false ...(在方法结束时,gt 将释放 'this' 上的锁,现在 st 将离开等待状态并尝试为此获取锁。gt 也在尝试获取 'this' 再次。再次不能保证哪个线程将获得锁并现在可以执行。)
长话短说:
由于 set 方法中的 else,您正在丢弃 'random' 数量的 set 调用(因此不太可能存在交替顺序)。如果 set 方法中没有 else,它应该可以工作。尽管这会浪费对 get 的方法调用,只是 运行 进入等待状态 return 而没有完成任何工作。
对 wait()
的调用必须始终处于 while 循环中,如 the documentation 中所述。你正在这样做,这很好。但是你弄错的部分是 while 循环需要 inside synchronized
块才能线程安全:
while (!condition) {
// Wrong --- another thread might call notify or notifyAll
// when the program is at this point, where this thread
// will not detect it.
synchronized (h) {
h.wait();
}
}
while 循环必须始终在同步块内以确保线程安全:
synchronized (h) {
while (!condition) {
h.wait();
}
}
此外,中断是向您的线程发出的信号,表示有人希望它终止。不要忽视它。
到目前为止,最简单的方法是将整个循环放在 try/catch 中,这样 InterruptedException 将自动结束循环:
try {
synchronized (h) {
while (!condition) {
h.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
最后,永远不要写一个空的 catch
块。隐藏异常会使您的代码故障排除变得极其困难。