使用 wait() 等待的 java 线程可以通知自己吗?
Can a java thread waiting with wait() ,notify itself?
我从一些网站上看到了以下实现自定义挂起和等待的示例。
// Suspending and resuming a thread the modern way.
class NewThread implements Runnable {
String name; // name of thread
Thread t;
boolean suspendFlag;
NewThread(String threadname) {
name = threadname;
t = new Thread(this, name);
System.out.println("New thread: " + t);
suspendFlag = false;
t.start(); // Start the thread
}
// This is the entry point for thread.
public void run() {
try {
for (int i = 15; i > 0; i--) {
System.out.println(name + ": " + i);
Thread.sleep(200);
synchronized(this) {
while (suspendFlag) {
wait();
}
}
}
} catch (InterruptedException e) {
System.out.println(name + " interrupted.");
}
System.out.println(name + " exiting.");
}
void mysuspend() {
suspendFlag = true;
}
synchronized void myresume() {
suspendFlag = false;
notify();
}
}
class SuspendResume {
public static void main(String args[]) {
NewThread ob1 = new NewThread("One");
NewThread ob2 = new NewThread("Two");
try {
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Suspending thread One");
Thread.sleep(1000);
ob1.myresume();
...................
我更关心 ob1.mysuspend() 和 ob1.myresume() 调用。当我的暂停被调用时,ob1 将被放入与其正在使用的可运行对象关联的阻塞队列中。当ob1调用myresume时,它是如何工作的,因为ob1已经在等待同一个对象的队列中,等待对象是否可以进入另一个同步方法,然后向自己发出信号通知?这是如何工作的?我错过了什么?
线程被编写为当 NewThread
的一个实例是 运行 时,另一个线程可以调用 mysuspend
来挂起那个 运行 线程。同样,挂起线程以外的线程调用 myresume
来恢复挂起线程。
似乎还存在数据争用,因为 mysuspend
在没有任何同步的情况下写入 suspendFlag
。这意味着,需要暂停的线程可能不会立即看到该写入。 mysuspend
必须声明 synchronized
,或者 suspendFlag
必须是可变的。
此代码已完全损坏。
直线上升:JMM 违规
mysuspend
方法(顺便说一句,应该命名为 mySuspend
)更新一个字段,然后从另一个线程读取该字段,并且不同步。 这是一个错误 - 一个非常讨厌的错误,因为你无法可靠地测试它是一个错误。 Java 内存模型 (JMM) 声明任何对字段的写入都可能 或不可见 ,由 JVM 实现自行决定,除非所谓的 Happens-Before/Happens-After 关系被建立(有很多方法可以做到这一点;通常你可以通过 synchronized、volatile 或其他基于这些原语构建的并发工具来实现,例如 java.util.concurrent
包中的锁存器和队列)。
你没有在这里建立这样的关系,意思是,suspendFlag = true
导致一个 schroedingers cat 变量:读取这个字段的另一个线程可能读取 true 或 false,JVM 决定你看到什么.因此:一个错误,并且无法测试。 不好。任何被多线程 read/written 的字段都需要非常小心地编写。
将该方法标记为同步,这是很好的第一步。
等待并通知
你把它颠倒过来了:当你在 x 上调用等待时,你 必须 实际上持有 x 上的同步锁(这里,x 是 this
) .
要调用 x.wait()
(实际上是在调用 this.wait()
),您必须首先位于 synchronized(x)
块中。一旦等待 'goes through',代码释放锁(其他 synchronized(x)
块可以 运行)。要调用 x.notify()
,您必须 还 持有该锁。
wait
不会 return 直到重新建立锁。
换句话说:
public void foo() {
wait();
}
将在 运行 时失败。尝试一下。保证异常。与此同时,这:
public void foo() {
synchronized (this) {
// code before wait
wait();
// code after wait
}
}
执行起来好像是这样写的:
public void foo() {
synchronized (this) {
// code before wait
release_lock(this);
this.wait();
acquire_lock(this);
// code after wait
}
}
其中 acquire_lock 保证实际需要一段时间(因为根据定义,任何调用 notify()
来唤醒你的东西目前都在等待它!所以等待总是两击:你需要在您的代码继续之前被通知并且需要重新获取锁)。当然,除了 acquire_lock 和 release_lock 不存在之外,与这个假设的代码不同,wait() 比那个更原子化。
我从一些网站上看到了以下实现自定义挂起和等待的示例。
// Suspending and resuming a thread the modern way.
class NewThread implements Runnable {
String name; // name of thread
Thread t;
boolean suspendFlag;
NewThread(String threadname) {
name = threadname;
t = new Thread(this, name);
System.out.println("New thread: " + t);
suspendFlag = false;
t.start(); // Start the thread
}
// This is the entry point for thread.
public void run() {
try {
for (int i = 15; i > 0; i--) {
System.out.println(name + ": " + i);
Thread.sleep(200);
synchronized(this) {
while (suspendFlag) {
wait();
}
}
}
} catch (InterruptedException e) {
System.out.println(name + " interrupted.");
}
System.out.println(name + " exiting.");
}
void mysuspend() {
suspendFlag = true;
}
synchronized void myresume() {
suspendFlag = false;
notify();
}
}
class SuspendResume {
public static void main(String args[]) {
NewThread ob1 = new NewThread("One");
NewThread ob2 = new NewThread("Two");
try {
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Suspending thread One");
Thread.sleep(1000);
ob1.myresume();
...................
我更关心 ob1.mysuspend() 和 ob1.myresume() 调用。当我的暂停被调用时,ob1 将被放入与其正在使用的可运行对象关联的阻塞队列中。当ob1调用myresume时,它是如何工作的,因为ob1已经在等待同一个对象的队列中,等待对象是否可以进入另一个同步方法,然后向自己发出信号通知?这是如何工作的?我错过了什么?
线程被编写为当 NewThread
的一个实例是 运行 时,另一个线程可以调用 mysuspend
来挂起那个 运行 线程。同样,挂起线程以外的线程调用 myresume
来恢复挂起线程。
似乎还存在数据争用,因为 mysuspend
在没有任何同步的情况下写入 suspendFlag
。这意味着,需要暂停的线程可能不会立即看到该写入。 mysuspend
必须声明 synchronized
,或者 suspendFlag
必须是可变的。
此代码已完全损坏。
直线上升:JMM 违规
mysuspend
方法(顺便说一句,应该命名为 mySuspend
)更新一个字段,然后从另一个线程读取该字段,并且不同步。 这是一个错误 - 一个非常讨厌的错误,因为你无法可靠地测试它是一个错误。 Java 内存模型 (JMM) 声明任何对字段的写入都可能 或不可见 ,由 JVM 实现自行决定,除非所谓的 Happens-Before/Happens-After 关系被建立(有很多方法可以做到这一点;通常你可以通过 synchronized、volatile 或其他基于这些原语构建的并发工具来实现,例如 java.util.concurrent
包中的锁存器和队列)。
你没有在这里建立这样的关系,意思是,suspendFlag = true
导致一个 schroedingers cat 变量:读取这个字段的另一个线程可能读取 true 或 false,JVM 决定你看到什么.因此:一个错误,并且无法测试。 不好。任何被多线程 read/written 的字段都需要非常小心地编写。
将该方法标记为同步,这是很好的第一步。
等待并通知
你把它颠倒过来了:当你在 x 上调用等待时,你 必须 实际上持有 x 上的同步锁(这里,x 是 this
) .
要调用 x.wait()
(实际上是在调用 this.wait()
),您必须首先位于 synchronized(x)
块中。一旦等待 'goes through',代码释放锁(其他 synchronized(x)
块可以 运行)。要调用 x.notify()
,您必须 还 持有该锁。
wait
不会 return 直到重新建立锁。
换句话说:
public void foo() {
wait();
}
将在 运行 时失败。尝试一下。保证异常。与此同时,这:
public void foo() {
synchronized (this) {
// code before wait
wait();
// code after wait
}
}
执行起来好像是这样写的:
public void foo() {
synchronized (this) {
// code before wait
release_lock(this);
this.wait();
acquire_lock(this);
// code after wait
}
}
其中 acquire_lock 保证实际需要一段时间(因为根据定义,任何调用 notify()
来唤醒你的东西目前都在等待它!所以等待总是两击:你需要在您的代码继续之前被通知并且需要重新获取锁)。当然,除了 acquire_lock 和 release_lock 不存在之外,与这个假设的代码不同,wait() 比那个更原子化。