文章目录
前言
我们根据之前所学知识,能够使用java中多线程机制模拟出电影院买票流程,但是通过下面的图我们可以看出,三个线程是属于同一类型的线程,它们都是在执行买票这个操作。那么java中又是如何处理不同种类线程间的通信问题的呢?那么就涉及到java多线程间的等待唤醒机制。
知识点:等待唤醒机制
1. 需求:实现生产线与消费线之间的通信,实现效果是生产者生产一个,消费者消费一个。
- 生产者角度:有资源尚未被消费,就等待不生产并且通知消费者消费
- 消费者角度,如果有资源就消费,如果没有资源就等待并通知生产者生产
2. Oject类中
- void notify ()
唤醒在此对象监视器上等待的单个线程。 - void notifyAll ()
唤醒在此对象监视器上等待的所有线程。 - void wait ()
在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。 - void wait (long timeout)
在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。
3. 代码示例
- 条件:
1.生产者线程
2.消费者线程
3.流通资源
public class 线程间等待唤醒机制 {
public static void main(String[] args) {
//创建共享资源
Student student = new Student();
//将资源传递给线程,所以线程之间是共享资源的
SetThread th1= new SetThread(student);
GetThread th2 = new GetThread(student);
th1.start();
th2.start();
}
}
//1.生产线程
class SetThread extends Thread{
private Student student;
int i=0;
public SetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true){
synchronized (student){
//能够进入该代码,说明有资源,生产线程就应该等待
if(student.flag){
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i % 2 == 0) {
student.name = "张三";
student.age = 19;
} else {
student.name = "李四";
student.age = 23;
}
//更改标记
student.flag=true;
//通知消费线程去消费
student.notify();
}
i++;
}
}
}
//2.消费线程
class GetThread extends Thread{
private Student student;
public GetThread(Student student) {
this.student=student;
}
@Override
public void run() {
while (true){
//既然对象是一个,我们可以将其作为锁对象。这样保持两个同步代码块的锁一致
synchronized (student){
//如果没有资源,进入程序等待
if(!student.flag){
try {
student.wait();//线程一旦调用等待wait()方法,就会释放锁,即线程没出同步代码块提前释放锁。下次被唤醒 ,从此处唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(student.name + "," + student.age);
//消费完资源后,更改标记,通知生产者生产
student.flag=false;
student.notify();
}
}
}
}
//资源
class Student{
public String name;
public int age;
//定义一个标记
public boolean flag=false;//false表示没有资源,true表示有资源
}
执行效果图:
4.sleep()方法和wait()方法的区别
- 共同:都可以让线程处于一种阻塞状态
- wait()方法:导致当前线程处于阻塞状态,直到被另一个线程调用notify ()或notifyAll ()来唤醒,调用该方法,必须要获得锁对象,所以该方法只能在同步方法和同步代码块中用。(notify ()也是需要在同步方法或同步代码块中调用)。wait()方法一旦执行会释放锁,可以使用notify ()直接唤醒。
- sleep()方法:一旦休眠不会释放锁(必须设置时间量),时间不到只能调用Interreput()强行打断。
作业
-
运用所学知识,编写代码。
需求:写两个线程,一个线程打印1-52,一个线程打印A-Z,打印结果是12A34B56C78D-----5152Z(即两个数字一个字母)。 -
代码答案
public class day23作业1 {
public static void main(String[] args) {
//创建共享资源
PrintFunc printFunc = new PrintFunc();
//将共享资源传入两个子线程中
DigitalThread th1 = new DigitalThread(printFunc);
LetterThread th2 = new LetterThread(printFunc);
th1.start();
th2.start();
}
}
//1.数字线程
class DigitalThread extends Thread{
int i=1;
private PrintFunc printFunc;
public DigitalThread(PrintFunc printFunc) {
this.printFunc = printFunc;
}
@Override
public void run() {
while (i<=52){
synchronized (printFunc){
//进入这段代码,代表打印了数字
if(printFunc.flag){
try {
printFunc.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(i);
i++;
System.out.print(i);
//打印了数字,更改标志
printFunc.flag=true;
//通知字母线程打印
printFunc.notify();
}
i++;
}
}
}
//2.字母线程
class LetterThread extends Thread{
char ch='A';
private PrintFunc printFunc;
public LetterThread(PrintFunc printFunc) {
this.printFunc = printFunc;
}
@Override
public void run() {
while (ch<=90){
synchronized (printFunc){
//进入该代码,表示尚未打印数字
if(!printFunc.flag){
try {
printFunc.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//打印了数字,开始打印字母
System.out.print(ch);
//更改标志
printFunc.flag=false;
printFunc.notify();
}
ch++;
}
}
}
//3.共享资源
class PrintFunc{
//定义一个标签
boolean flag=false;//false代表尚未打印数字
}
- 结果