目录
- 什么是等待通知机制
- 等待通知机制的实现
- notify不会立即释放锁对象
- interrupt会中断线程的等待
- notify与notifyAll的区别
- wait(Long)的使用
什么是等待通知机制
在单线程中,要执行的操作需要满足一定条件才能执行,可以把这个操作放在if语句块中。
在多线程编程中,可能A线程的条件没有满足只是暂时的,稍后其他的线程B可能会更新条件使得A线程的条件得以满足,可以将A线程暂停,直到它的条件得到满足之后再将A线程唤醒
Atomic{
while(条件不成立)
{
等待
}
条件满足后,当前线程被唤醒
}
等待通知机制的实现
object类中的Wait方法可以使当前线程的代码暂停执行,直到接到通知或者被中断为止
注意:
(1)wait方法只能再同步代码块中由锁对象调用
(2)调用wait方法,当前线程会释放锁
public class Text16_5 {
public static void main(String[] args) throws InterruptedException {
String text="hello";
System.out.println("同步前代码块");
synchronized (text)
{
System.out.println("同步代码块开始");
text.wait();
System.out.println("同步代码块结束");
}
System.out.println("全部结束");
}
}
因为调用了锁对象的wait方法,会释放锁对象,处于等待的状态,没有被唤醒就会一直等待下去。
object类的notify方法可以唤醒线程,该方法也必须同步在代码块中,由锁对象调用,没有使用锁对象调用wait/notify会报出IIegalMonuitorStateExeption异常,如果由多个等待的线程,notify方法只能唤醒其中的一个,在同步代码块中调用notify方法后,并不会立即释放锁对象,需要等当前同步代码块执行完后才会释放锁对象,一般将notify放在同步代码块最后。
synchronized(锁对象)
{
//执行修改保护条件的代码
//唤醒其他线程
锁对象.notify();
}
public class TextNotify {
public static void main(String[] args) throws InterruptedException {
String text="hello";
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized (text)
{
System.out.println("同步代码块开始");
try {
text.wait();//线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("同步代码块结束");
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
synchronized (text)
{
System.out.println("线程开始唤醒");
text.notify();
System.out.println("线程结束唤醒");
}
}
});
t1.start();//开启t1线程 t1等待
Thread.sleep(3000);//睡眠3秒 确保t1处于等待状态
t2.start();//唤醒t1线程
}
}
notify不会立即释放锁对象
案例:
import java.util.ArrayList;
import java.util.List;
public class NotifyText2 {
public static void main(String[] args) throws InterruptedException {
List<String> strings=new ArrayList<>();
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized (strings)
{
System.out.println("线程1开始等待");
try {
strings.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1被唤醒");
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
synchronized (strings)
{
for (int i = 0; i <10 ; i++) {
strings.add("data"+i);
System.out.println("线程2添加了"+(i+1));
if(strings.size()==5)
{
strings.notify();
System.out.println("线程2被唤醒");
}
}
}
}
});
t1.start();
Thread.sleep(1000);
t2.start();
}
}
线程2的代码还没有执行完毕,锁没有立即释放依然在执行,需要等到所有代码块全部执行完毕才释放
interrupt会中断线程的等待
public class InterruptText {
private static final String name=new String();
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized (name)
{
try {
System.out.println("同步代码块开始");
name.wait();
System.out.println("同步代码块结束");
} catch (InterruptedException e) {
System.out.println("wait被中断"+e);
}
}
}
});
t1.start();
Thread.sleep(2000);
t1.interrupt();
}
}
原来锁对象需要执行完同步代码块才能释放锁对象,在执行过程如果遇到异常也会导致线程终止,释放锁对象。调用wait方法也会释放锁对象。
notify与notifyAll的区别
notify一次只能唤醒一个,如果有多个线程都在等待,只能随机唤醒其中的一个,想要唤醒所有等待线程需要调用notifyAll。
public class InterruptText {
private static final String name=new String();
public static void main(String[] args) throws InterruptedException {
String str=new String();
NotifyAll notifyAll=new NotifyAll(str);
NotifyAll notifyAl2=new NotifyAll(str);
NotifyAll notifyAll3=new NotifyAll(str);
notifyAll.setName("线程一");
notifyAl2.setName("线程二");
notifyAll3.setName("线程三");
notifyAll.start();
notifyAl2.start();
notifyAll3.start();
Thread.sleep(2000);//休眠两秒
synchronized (str)
{
//str.notify();只能随机唤醒一个
str.notifyAll();//唤醒全部线程
}
};
static class NotifyAll extends Thread
{
private String name;
private NotifyAll(String name)
{
this.name=name;
}
@Override
public void run() {
synchronized (name)
{
try {
System.out.println(Thread.currentThread().getName()+"同步代码块开始");
name.wait();
System.out.println(Thread.currentThread().getName()+"同步代码块结束");
} catch (InterruptedException e) {
System.out.println("wait被中断"+e);
}
}
}
}
}
如果只调用一次notify()之恶能唤醒其中的一个线程,其他等待线程依然处于等待状态,就错过了通知信号,这种现象称之为信号丢失。
wait(Long)的使用
带有参数的Wait(Long)方法,在指定时间内没有操作会被自动唤醒