Java多线程-04-线程协作
目录
一.线程简介
二.线程创建
三.线程状态
四.线程同步
五.线程协作
五.线程协作
生产者消费者问题
为什么需要线程通信:为了解决生产者和消费者问题

问题分析

解决线程通信的方法

解决方式1:管程法

解决方式2:信号灯法
通过一个标志位来判断

管程法
例子:
生产者:厨师制作炸鸡
消费者:吃炸鸡
缓冲区:取餐台(容器,用来放炸鸡)
如果不使用管程法,就只能让厨师先做完100只炸鸡,然后顾客吃掉100只炸鸡,这显然不合理
- wait() 会让当前持有对象锁的线程等待并释放锁
A.wait() 会让当前持有A对象锁的线程等待并释放锁
-
notifyAll() 唤醒正在等待此对象锁的所有线程
A.notifyAll() 唤醒正在等待A对象锁的所有线程
我说:
1.谁来调用两个方法?
被消费者和生产者操作的对象,即缓冲区
2.生产者何时等待何时被唤醒?
缓冲区满时生产者自行等待
生产者消费后唤醒生产者
3.消费者何时等待何时被唤醒?
缓冲区空时消费者自行等待
生产者生产后唤醒消费者
package 四线程协作;
//生产者和消费者问题:利用缓冲区解决(管程法)
//例子
//生产者:厨师制作炸鸡
//消费者:吃炸鸡
//缓冲区:取餐台(容器,用来放炸鸡)
public class MonitorMethod {
public static void main(String[] args) {
PC_Container container = new PC_Container();
new Productor("|厨师|", container).start();
new Consumer("||顾客||", container).start();
}
}
//生产者线程:厨师制作炸鸡
class Productor extends Thread{
public PC_Container container;
public Productor(String name, PC_Container container){
super(name);
this.container = container;
}
@Override
public void run() {
//一共制作100只炸鸡
for (int i = 1; i <= 100; i++) {
//制作一只炸鸡并放到前台
container.push(new Chicken(i));
System.out.println(this.getName() + "-->生产了第"+ i + "只炸鸡");
}
}
}
//消费者线程:吃炸鸡
class Consumer extends Thread{
public PC_Container container;
public Consumer(String name, PC_Container container){
super(name);
this.container = container;
}
@Override
public void run() {
//一共吃100只炸鸡
for (int i = 1; i <= 100; i++) {
//从前台取一只炸鸡
Chicken chicken = container.pop();
System.out.println(this.getName() + "-->消费了第"+ chicken.id + "只炸鸡");
}
}
}
//炸鸡
class Chicken {
//炸鸡编号
public int id;
public Chicken(int id){
this.id = id;
}
}
//缓冲区:取餐台(容器,用来放炸鸡)
//会被生产者和消费者修改(多个线程操作同一个对象)
class PC_Container {
//容器大小:前台最多能放10只炸鸡
public Chicken[] chickens = new Chicken[10];
//容器计数器
public int count = 0;
//放入炸鸡
//该方法涉及修改PC_Container,使用同步方法(监视器是this),即锁PC_Container
public synchronized void push(Chicken chicken){
//容器满了-前台已经放满10只炸鸡了
if(count == chickens.length){
//让生产者等待
try {
//wait()会让当前持有PC_Container对象锁的线程等待并释放锁
//持有锁并且跑push方法的只能是生产者线程,所以这里的this.wait()会让生产者线程等待
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以生产
//这里不需要写else if,因为如果生产者处于等待,那么下面的代码就不会执行了
//没有满,丢入炸鸡
chickens[count] = chicken;
count++;
//现在前台有炸鸡了,需要唤醒消费者
//notifyAll()唤醒正在等待此对象监视器锁的所有线程
//notifyAll()可以唤醒生产者和消费者,但此时生产者本来就是醒,所以旨在唤醒消费者来吃炸鸡
this.notifyAll();
}
//放入炸鸡
//该方法涉及修改PC_Container,使用同步方法(监视器是this),即锁PC_Container
public synchronized Chicken pop(){
//容器空了-前台没有炸鸡了
if(count == 0){
//让消费者等待
try {
//跑pop方法的只能是消费者线程,所以这里的this.wait()会让消费者线程等待
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
//取走炸鸡
count--;//注意这里要先减,count下标指向的是数组中下一个空白的位置,count-1才是最新放入的炸鸡
Chicken chicken = chickens[count];
//因为消费者取走了炸鸡所以现在前台一定不是满的
//需要通知生产者生产
this.notifyAll();
return chicken;
}
}
信号灯法
例子
生产者:演员表演节目
消费者:观众收看节目
演员和生产者都操作同一个电视机
演员通过 电视机上的信号灯 判断自己的状态
1.等待
2.表演节目,之后提醒观众观看
观众通过 电视机上的信号灯 判断自己的状态
1.等待
2.收看节目,之后提醒演员表演
package 四线程协作.生产者消费者问题;
import java.security.PublicKey;
//生产者和消费者问题:利用标志位解决(信号灯法)
//例子
//生产者:演员表演节目
//消费者:观众收看节目
public class SignalLight {
public static void main(String[] args) {
TV tv = new TV();
new PLayer(tv).start();
new Watcher(tv).start();
}
}
//生产者:演员
class PLayer extends Thread{
public TV tv;
public PLayer(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(i%2==0) this.tv.play("地球脉动");
else this.tv.play("COSMOS");
}
}
}
//消费者:观众
class Watcher extends Thread{
public TV tv;
public Watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
this.tv.watch();
}
}
}
//电视机TV
class TV {
//节目名字
private String programName;
//信号灯标志位
//演员表演中,观众等待 ture
//观众收看中,演员等待 false
private boolean flag = true;
//演员表演节目
public synchronized void play(String programName){
if(!flag){
try {
//观众收看中 演员等待
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//观众等待中 演员表演
this.programName = programName;
System.out.println("演员表演了-->" + programName);
//演员表演完了
this.flag = !this.flag;//信号灯反转
//通知唤醒观众观看
this.notifyAll();
}
//观众收看节目
public synchronized void watch(){
if(flag){
try {
//演员表演中 观众等待
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//演员等待中 观众收看
System.out.println("观众收看了-->" + this.programName);
//观众收看完了
this.flag = !this.flag;//信号灯反转
//通知唤醒演员表演
this.notifyAll();
}
}
线程池


public class Pool_Test {
public static void main(String[] args) {
//1.创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);//参数为线程池大小
//执行
service.execute(new MyThead());
service.execute(new MyThead());
service.execute(new MyThead());
service.execute(new MyThead());
//2.关闭连接
service.shutdown();
}
}
class MyThead implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

五.线程协作
浙公网安备 33010602011771号