2022.8.20 线程通信问题与线程池

7、线程通信问题

生产者消费者模式的问题

应用场景︰生产者和消费者问题

  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库﹐消费者将仓库中产品取走消费﹒

  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止.

  • 如果仓库中放有产品﹐则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止.

 

 

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

  • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后﹐又需要马上通知消费者消费

  • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费.

  • 在生产者消费者问题中,仅有synchronized是不够的

    • synchronized可阻止并发更新同一个共享资源,实现了同步

    • synchronized不能用来实现不同线程之间的消息传递(通信)

Java提供了几个方法解决线程之间的通信问题

方法名作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同﹐会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常lllegalMonitorStateException

线程通信问题解决方式

管程法

并发协作模型“生产者/消费者模式”--->管程法(中间有个缓冲区)

  • 生产者︰负责生产数据的模块(可能是方法﹐对象﹐线程﹐进程);

  • 消费者∶负责处理数据的模块(可能是方法﹐对象﹐线程﹐进程);

  • 缓冲区∶消费者不能直接使用生产者的数据﹐他们之间有个“缓冲区“

生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

  1 package com.xing.demo03;
  2 
  3 /**
  4  * 测试:生产者消费者模型-->利用缓冲区解决:管程法
  5  */
  6 public class TestPC {
  7     public static void main(String[] args) {
  8         //缓冲区
  9         SynContainer synContainer = new SynContainer();
 10 
 11         new Producer(synContainer).start();
 12         new Consumer(synContainer).start();
 13     }
 14 }
 15 
 16 //生产者
 17 class Producer extends Thread {
 18     //缓冲区
 19     SynContainer container;
 20 
 21     public Producer(SynContainer container) {
 22         this.container = container;
 23     }
 24 
 25     //生产
 26     @Override
 27     public void run() {
 28         for (int i = 0; i < 100; i++) {
 29             //放入缓冲区
 30             container.push(new Product(i));
 31             System.out.println("生产了" + i + "件产品");
 32         }
 33     }
 34 }
 35 
 36 //消费者
 37 class Consumer extends Thread {
 38     //缓冲区
 39     SynContainer container;
 40 
 41     public Consumer(SynContainer container) {
 42         this.container = container;
 43     }
 44 
 45     //消费
 46     @Override
 47     public void run() {
 48         for (int i = 0; i < 100; i++) {
 49             //从缓冲区取出产品
 50             Product product = container.pop();
 51             System.out.println("消费了-->" + product.id + "件产品");
 52         }
 53     }
 54 }
 55 
 56 //产品
 57 class Product {
 58     int id;//产品编号
 59     public Product(int id) {
 60         this.id = id;
 61     }
 62 }
 63 
 64 //缓冲区
 65 class SynContainer {
 66     //需要一个容器大小
 67     Product[] products = new Product[10];
 68 
 69     //容器计数器
 70     int count = 0;
 71 
 72     //生产者放入产品
 73     public synchronized void push(Product product) {
 74         //如果容器满了,需要等待消费者消费
 75         /*如果是if的话,假如消费者1消费了最后一个,这是index变成0此时释放锁被消费者2拿到而不是生产者拿到,这时消费者的wait是在if里所以它就直接去消费index-1下标越界,如果是while就会再去判断一下index得值是不是变成0了*/
 76         while (count == products.length) {
 77 
 78             //通知消费者消费,生产者等待
 79             try {
 80                 this.wait();
 81             } catch (InterruptedException e) {
 82                 e.printStackTrace();
 83             }
 84         }
 85         //如果没有满,需要丢入产品
 86         products[count] = product;
 87         count++;
 88 
 89         //通知消费者消费
 90         this.notifyAll();
 91     }
 92 
 93     //消费者消费产品
 94     public synchronized Product pop() {
 95         //判断是否能消费
 96         while (count <= 0) {
 97 
 98             //等待生产者生产
 99             try {
100                 this.wait();
101             } catch (InterruptedException e) {
102                 e.printStackTrace();
103             }
104         }
105 
106         //如果可以消费
107         count--;
108         //取出产品
109         Product product = products[count];
110 
111         //吃完了 通知生产者生产
112         this.notifyAll();
113 
114         //返回消费的产品
115         return product;
116     }
117 }
118 
119  

 

信号灯法

并发协作模型“生产者/消费者模式”--->信号灯法(设置一个标志位:true就等待,false就通知生产或消费)

 1 package com.xing.demo03;
 2 
 3 /**
 4  * @author: sjmp1573
 5  * @date: 2020/11/18 21:34
 6  * @description:
 7  */
 8 
 9 public class TestPC2 {
10     public static void main(String[] args) {
11         TV tv = new TV();
12         new Player(tv).start();
13         new Wathcher(tv).start();
14     }
15 }
16 
17 //生产者--演员
18 class Player extends Thread{
19     TV tv;
20     public Player(TV tv){
21         this.tv = tv;
22     }
23 
24     @Override
25     //表演
26     public void run() {
27         for (int i = 0; i < 20; i++) {
28             if(i%2==0){
29                 this.tv.play("快乐大本营");
30             }else{
31                 this.tv.play("天天向上");
32             }
33         }
34     }
35 }
36 
37 //观众
38 class Wathcher extends Thread{
39     TV tv;
40     public Wathcher(TV tv){
41         this.tv = tv;
42     }
43 
44     @Override
45     //看节目
46     public void run() {
47         for (int i = 0; i < 20; i++) {
48             tv.watch();
49         }
50     }
51 }
52 
53 //产品--节目
54 class TV{
55     //    演员表演,观众等待  T
56     //    观众观看,演员等待  F
57     String voice;  // 表演节目
58     boolean flag = true;
59 
60     //    表演
61     public synchronized void play(String voice){
62         if(!flag){
63             try {
64                 this.wait();
65             } catch (InterruptedException e) {
66                 e.printStackTrace();
67             }
68         }
69         System.out.println("演员表演了: "+voice);
70         // 通知观众观看
71         this.notifyAll();// 通知唤醒
72         this.voice = voice;
73         this.flag = !this.flag;
74 
75     }
76 
77     //    观看
78     public synchronized void watch(){
79         if (flag){
80             try {
81                 this.wait();
82             } catch (InterruptedException e) {
83                 e.printStackTrace();
84             }
85         }
86         System.out.println("观看了: "+voice);
87         //        通知演员表演
88         this.notifyAll();
89         this.flag = !this.flag;
90     }
91 }

 

8、线程池

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能景响很大。

  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

  • 好处:

    • 提高响应速度(减少了创建新线程的时间)

    • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

    • 便于线程管理(..….)

      • corePoolSize:核心池的大小

      • maximumPoolSize:最大线程数

      • keepAliveTime:线程没有任务时最多保持多长时间后会终止

使用线程池

  • JDK 5.0起提供了线程池相关API: ExecutorService和Executors

  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

    • void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable

    • <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable

    • void shutdown():关闭连接池

  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

 1 package com.xing.demo03;
 2 
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 
 6 /**
 7  * @author: sjmp1573
 8  * @date: 2020/11/18 21:53
 9  * @description:
10  */
11 
12 public class TestPool {
13     public static void main(String[] args) {
14         //1.创建服务,创建线程池  参数为线程池大小
15         ExecutorService service = Executors.newFixedThreadPool(10);
16          
17         //将线程放入池子执行 
18         service.execute(new MyThread());
19         service.execute(new MyThread());
20         service.execute(new MyThread());
21         service.execute(new MyThread());
22 
23         //2.关闭连接
24         service.shutdown();
25 
26     }
27 }
28 class MyThread implements Runnable{
29 
30     @Override
31     public void run() {
32         System.out.println(Thread.currentThread().getName());
33     }
34 }

 

 

posted @ 2022-08-21 00:07  暴躁C语言  阅读(24)  评论(0编辑  收藏  举报