线程间通信(生产消费者问题):不同类型线程针对同一个资源的操作
举例:1.系统不仅要卖票还要入票
2.不仅要卖肉夹馍还要生产肉夹馍
今天主要讲解单生产单消费模式。
如果我们现在利用单生产单消费模式对学生进行设置姓名年龄,获取学生姓名年龄该怎么办呢?
首先,线程间通讯:
资源:Student(将学生作为资源进行操作)
设置数据线程:SetThread
获取数据线程:GetThread
测试类:StudentDemo
先考虑几个问题:
1.怎么保证资源唯一?(让设置和获取到的是同一资源,否则获取到的只能是null,0)
解决方法:当在不同的类中,需要使用同一个数据的时候,可以考虑将数据利用构造方法进行传参
2.数据重复问题?数据错乱问题?
解决方法:同步代码块或者同步方法都可以解决数据问题,加锁。
①设置线程和获取线程需要全部做一个限制,相当与两个线程都需要加锁
②设直线程和获取线程,加的锁必须是同一个,才可以保证数据安全
package com.edu_04;
//Student类
public class Student {
String name;
int age;
}
package com.edu_04;
//设置线程
public class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s){
this.s = s;
}
@Override
public void run() {
//给学生对象设置姓名和年龄
//Student s = new Student();
while (true) {
synchronized (s) {
if (x%2==0) {
s.name = "张三";
s.age = 50;
}else {
s.name = "李四";
s.age = 35;
}
x++;
}
}
}
}
package com.edu_04;
//获取线程
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}
@Override
public void run() {
//获取线程,获取学生对象的姓名和年龄
//Student s = new Student();
while (true) {
synchronized (s) {
System.out.println(s.name+"--"+s.age);
}
}
}
}
package com.edu_04;
//测试类
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();
}
}
这样一来虽然保证了数据的安全性,资源唯一性,但是并没有实现礼让效果。怎么让数据变得和谐礼让呢?也就是说生产一个,获取一个,有理有序?
解决方法:使用等待唤醒机制改进,实现礼让效果
将上述问题全部解决之后,就得到了单生产单消费模式的整个流程,代码如下:
方式一:利用同步代码块
package com.edu_05;
//学生类
public class Student {
String name;
int age;
boolean flag;//在这里可以作为对象的一个标记,如果是false说明该对象没有数据,如果是true说明该对象有数据
}
package com.edu_05;
//设置线程
public class SetThread implements Runnable{
private Student s;
private int x = 0;
public SetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
//判断该对象此时有没有数据
if (s.flag) {
//等待
try {
s.wait();//设置线程等待,释放锁s
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (x%2==0) {
s.name = "张三";
s.age = 50;
}else {
s.name = "李四";
s.age = 35;
}
x++;//x=1
//此时对象有数据了
s.flag = true;
s.notify();//如果有等待的线程就唤醒,如果没有等待的线程,则没有任何效果
}//在此时释放锁对象s
}
}
}
package com.edu_05;
//获取线程
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
//判断对象有没有数据
if (!s.flag) {
//等待设置线程给对象设置数据
try {
s.wait();//获取线程处于等待状态,释放锁对象s,在哪里跌倒在哪里爬起来
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(s.name+"--"+s.age);//张三--50
//李四--35
//张三--50
//当获取线程从学生对象中获取了数据之后,我们就默认他已经没有数据了,此时我们应该
//继续让设置线程继续给学生对象设置信息
s.flag = false;
s.notify();
}
}
}
}
package com.edu_05;
//测试
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();
}
}
//张三--50
//李四--35
//张三--50
方式二:利用同步方法
package com.edu_06;
//学生类,并将设置获取方法封装
public class Student {
//块编辑(alt+shift+a):在使用块编辑的时候,一定要将输入法切换到英文输入法,不然会出问题
private String name;
private int age;
private boolean flag;//在这里可以作为对象的一个标记,如果是false说明该对象没有数据,如果是true说明该对象有数据
//提供公共的方法设置信息
public synchronized void setInfo(String name,int age){
if (this.flag) {
//等待
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//没有值的话,在这里给对对象设置数据
this.name = name;
this.age = age;
//更改标记,唤醒获取线程获取数据
this.flag = true;
this.notify();
}
//提供公共的方法获取信息
public synchronized void getInfo(){
if (!this.flag) {
//没有值
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//有数据,取数据
System.out.println(this.name+"--"+this.age);
//取完数据之后,就没有数据了
this.flag = false;
this.notify();
}
}
package com.edu_06;
//设置线程
public class SetThread implements Runnable{
private Student s;
private int x = 0;
public SetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
if (x%2==0) {
s.setInfo("张三", 50);
}else {
s.setInfo("李四", 35);
}
x++;//x=1
}
}
}
package com.edu_06;
//获取线程
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
s.getInfo();
}
}
}
package com.edu_06;
//测试
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();
}
}