线程通信及唤醒机制
线程间通信问题:
-
不同种类的线程间针对同一资源的操作
- 例:通过设置线程(生产者)和获取线程(消费者)针对同一个学生对象进行操作
- 分析
-
资源类:Student
-
设置学生数据:SetThread(生产者)
-
获取学生数据:GetThread(消费者)
-
测试类:A生产者消费者问题
- 线程安全问题
-
1:是否多线程环境
-
2:是否有共享数据
-
3:是否有多条语句操作共享数据
- 解决方案
-
加锁
-
注意:不同种类的线程都要加锁
-
不同种类的线程加的锁必须是同一把锁
- 线程通信正常思路:
-
A:生产者
-
先看是否有数据,有就等待,没有就生产,生产完之后通知消费者来消费
-
B:消费者
-
先看是否有数据,有就消费,没有就等待,通知生产者生产数据
-
为了处理这种问题,java就提供了一种机制,等待唤醒机制 。
例:
public class StudentDemo{
public static void main(String[] args) {
//创建资源
Student s=new Student();
//设置和获取的类
SetThread st=new SetThread(s);
GetThread gt=new GetThread(s);
//线程类
Thread t1=new Thread(st);
Thread t2=new Thread(gt);
//启动线程
t1.start();
t2.start();
}
}
class Student{
String name;
int age;
}
class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s) {
this.s=s;
}
public void run() {
while(true) {
synchronized(s) {
if(x%2==0) {
s.name=“小程”;
s.age=21;
}else {
s.name=“小黄” ;
s.age=20;
}
x++;
}
}
}
}
class GetThread implements Runnable{
private Student s;
public GetThread(Student s) {
this.s=s;
}
public void run() {
while(true){
synchronized(s) {
System.out.println(s.name+"------"+s.age);
}
//不加锁
// 小黄------20
// 小程------20
// 小程------20
// 小程------21
// 小黄------20
// 小黄------20
// 小黄------21//姓名年龄不匹配,线程的随机性,需要加锁
//加锁后
// 小黄------20
// 小黄------20
// 小黄------20
// 小程------21
// 小程------21
}
}
}
等待唤醒机制:
-
Object类中提供了三个方法(其他方法了解)
-
wait():等待 有个小特点,当前线程等待了,就会释放锁
-
notify():唤醒单个线程
-
notifyAll():唤醒全部线程
-
为什么这些方法不定义在Thread类中呢?
-
这些方法必须通过锁对象调用,而我们刚才用的锁对象时任意锁对象
-
所以这些方法必须定义在Object中
例:
public class StudentDemo {
public static void main(String[] args) {
//创建资源
Student s=new Student();
//设置和获取的类
SetThread st=new SetThread(s);
GetThread gt=new GetThread(s);
//线程类
Thread t1=new Thread(st);
Thread t2=new Thread(gt);
//启动线程
t1.start();
t2.start();
}
}
class Student{
String name;
int age;
boolean flag;//默认false
}
class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s) {
this.s=s;
}
public void run() {
while(true) {
synchronized(s) {
//判断有没有(如果是true就是有)
if(s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(x%2==0) {
s.name=“小程”;
s.age=21;
}else {
s.name=“小黄” ;
s.age=20;
}
x++;
//修改标记
s.flag=true;
//唤醒线程
s.notify();
}
}
}
}
class GetThread implements Runnable{
private Student s;
public GetThread(Student s) {
this.s=s;
}
public void run() {
while(true){
synchronized(s) {
if(!s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(s.name+"------"+s.age);
//修改标记
s.flag=false;
//唤醒线程
s.notify();
//加入唤醒机制后
// 小程------21
// 小黄------20
// 小程------21
// 小黄------20
// 小程------21
// 小黄------20
}
}
}
}
总结的一张线程转换图:
推荐阅读
-
线程通信及唤醒机制
-
Java多线程之线程通信生产者消费者模式及等待唤醒机制代码详解
-
java多线程等待唤醒机制、 Lambda表达式、单例设计模式
-
27_多线程_第27天(线程安全、线程同步、等待唤醒机制、单例设计模式)_讲义
-
Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON
-
Java多线程中断机制三种方法及示例
-
java线程间通信的通俗解释及代码示例
-
浏览器UI多线程及对JavaScript单线程底层运行机制详解
-
Android消息机制原理,仿写Handler Looper源码解析跨线程通信原理--之仿写模拟Handler(四)
-
Java多线程Part3:线程(Thread)的状态和等待唤醒机制