? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 线程通信? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?个人博客:www.xiaobeigua.icu
1.1 等待/通知机制
1.1.1 什么是等待通知机制
????????在单线程编程中,要执行的操作需要满足一定的条件才能执行,可 以把这个操作放在 if语句快中。
????????在多线程编程中,可能 A 线程的条件没有满足只是暂时的, 稍后其 他的线程 B 可能会更新条件使得 A 线程的条件得到满足. 可以将 A 线 程暂停,直到它的条件得到满足后再将 A线程唤醒,这就是通知/等待机制
它的伪代码:
atomics{ //原子操作
while( 条件不成立 ){
等待
}
当前线程被唤醒条件满足后,继续执行下面的操作
}
1.1.2 等待/通知机制的实现
????????Object 类中的 wait()方法可以使执行当前代码的线程等待,暂停执 行,直到接到通知或被中断为止.
????????注意:
????????????????1) wait()方法只能 在同步代码块中由锁对象调用
????????????????2) 调用 wait()方法,当前线程会释放
????????其伪代码如下:
//在调用 wait()方法前获得对象的内部锁
synchronized( 锁对象 ){
while( 条件不成立 ){
//通过锁对象调用 wait()方法暂停线程
锁对象.wait();
}
//线程的条件满足了继续向下执行
}
????????Object 类的 notify()或者notifyAll()可以唤醒线程,该方法也必须在同步代码块中 由锁对象调 用 。没 有 使 用 锁 对 象 调 用 wait()/notify() 会抛出 IlegalMonitorStateExeption 异常。
????????如果有多个等待的线程,notify()方法 只能唤醒其中的一个. 在同步代码块中调用 notify()方法后,并不会立即释放锁对象,需要等当前同步代码块执行完后才会释放锁对象,一般 将 notify()方法放在同步代码块的最后. 它的伪代码如下:
synchronized( 锁对象 ){
//执行修改保护条件 的代码
//唤醒其他线程
锁对象.notify();
}
例子:
????????定义两个线程 线程1用于模拟等待? ? 线程2用于激活线程1
package com.xiaobeigua.one;
/**
* 作者:小北呱
*/
public class test3 {
public static void main(String[] args) {
//定义一个引用变成作为synchronized (lock)的参数
String lock="lock";
//线程1 等待
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
System.out.println("线程1开始等待.....");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1被线程2激活,结束等待!");
}
}
}).start();
//线程2 激活
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
System.out.println("线程2开始激活线程1....");
lock.notify();
System.out.println("线程2激活结束!");
}
}
}).start();
}
}
结果:
????????
?注意:从运行结果可以看出 使用notify()方法后不会立即释放锁对象,而是要执行完这个同步代码块里的语句后才会释放锁对象。

1.1.3?interrupt()方法会中断 wait()
????????当线程处于 wait()等待状态时, 调用线程对象的 interrupt()方法会 中断线程的等待状态, 会产生 InterruptedException 异常
代码:
package com.xiaobeigua.one;
/**
* 作者:小北呱
*/
public class test3 {
public static void main(String[] args) throws InterruptedException {
//定义一个引用变成作为synchronized (lock)的参数
String lock="lock";
//线程1 等待
Thread t1= new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
System.out.println("线程1开始等待.....");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1等待状态被interrupt()中断了,结束等待!");
}
}
});
//开启线程1 让其进去等待状态
t1.start();
Thread.sleep(1000);
//调用其interrupt()方法
t1.interrupt();
}
}
结果:
????????
1.1.4? notify()与 notifyAll()
????????notify()一次只能唤醒一个线程,如果有多个等待的线程,只能随机 唤醒其中的某一个; 想要唤醒所有等待线程,需要调用 notifyAll()。
?例子:创建 4个线程? 线程 0 1 2 开启等待? ?线程3 使用 notify()
package com.xiaobeigua.one;
/**
* 作者:小北呱
*/
public class test3 {
public static void main(String[] args){
MyRunable1 myRunable1=new MyRunable1();
MyRunable2 myRunable2=new MyRunable2();
Thread t1=new Thread(myRunable1);
Thread t2=new Thread(myRunable1);
Thread t3=new Thread(myRunable1);
Thread t4=new Thread(myRunable2);
t1.start();
t2.start();
t3.start();
t4.start();
}
//内部静态类 创建等待和激活两个方法 供两个线程类调用
static class MyLock{
static String lock="lock";
public static void beWith() throws InterruptedException {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"开始等待...");
lock.wait();
System.out.println(Thread.currentThread().getName()+"被激活,结束等待");
}
}
public static void enWith() throws InterruptedException {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"开始激活...");
lock.notify();
System.out.println(Thread.currentThread().getName()+"激活结束!");
}
}
}
//创建等待线程类
static class MyRunable1 implements Runnable{
@Override
public void run() {
try {
MyLock.beWith();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//创建notify线程类
static class MyRunable2 implements Runnable{
@Override
public void run() {
try {
MyLock.enWith();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果:
????????
?和预想一样,线程 0 1 2都进入了等待状态 线程3开始激活 调用 notify()方法 只激活的线程0
?将代码中的notify()方法 改为 notifyAll()结果:
????????
?可以看到 线程3调用 notifyAll()方法 全部激活了线程
1.1.5?wait(long)的使用
wait(long)带有 long 类型参数的 wait()等待,如果在参数指定的时间 内没有被唤醒,超时后会自动唤醒。
package com.xiaobeigua.one;
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public synchronized void run() {
System.out.println("开始进入等待状态....");
try {
//等待2秒
this.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("时间到 退出等待");
}
}).start();
}
}
结果:

注意:
????????线程 wait()等待后,可以调用 notify()唤醒线程, 如果 notify()唤醒的 过早,在等待之前就调用了 notify()可能会打乱程序正常的运行逻
1.1.6?生产者消费者模式
????????在 Java 中,负责产生数据的模块是生产者,负责使用数据的模块是 消费者. 生产者消费者解决数据的平衡问题,即先有数据然后才能使用,没有数据时,消费者需要等待
?例子:到店里次牛排? ?生产者生产牛排 最多 10块? 消费者吃牛排? 全部吃完再通知生产者再生产
????????
注意:这里的类全部以 内部类形式出现
package com.xiaobeigua.one;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 作者:小北呱
*/
public class test3 {
public static void main(String[] args) {
MyRunable1 myRunable1 = new MyRunable1();
MyRunable2 myRunable2 = new MyRunable2();
//创建生产者
Thread t0 = new Thread(myRunable1);
//创建消费者
Thread t1 = new Thread(myRunable2);
t0.start();
t1.start();
}
//产品类
static class MyProduct {
//设置list集合容量为10 相当于 桌子只能放十块牛排
static List list = new ArrayList();
//生产牛排
public static void setPro() throws InterruptedException {
//当桌面上没有牛排时,生产者开始生产牛排
synchronized (list) {
while (list.size() <= 10) {
String pro = "牛排" + new Random();
list.add(pro);
System.out.println(Thread.currentThread().getName() + "生产了牛排" + pro);
//当牛排满10块时,停止生产
}
list.notify();
}
}
//消费牛排
public static void getpro() throws InterruptedException {
synchronized (list) {
while (list.size() != 0) {
System.out.println(Thread.currentThread().getName() + "吃了" + list.remove(list.size() - 1));
}
list.wait();
}
}
}
//创建生产线程类
static class MyRunable1 implements Runnable {
test3.MyProduct myProduct = new test3.MyProduct();
@Override
public void run() {
try {
myProduct.setPro();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//创建消费线程类
static class MyRunable2 implements Runnable {
test3.MyProduct myProduct = new test3.MyProduct();
@Override
public void run() {
try {
myProduct.getpro();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
?结果:

?这里只是简单的实现了生产者与消费者模式,主要是理解它的设计思想为我所用
|