java多线程(11):线程同步线程协作
创始人
2024-05-03 18:00:15
0

1 线程通信

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

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

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

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

2 线程通信分析

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

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

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

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

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

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

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

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

3 解决方式1

并发协作模型 “ 生产者 / 消费者模式 ” --->管程法

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

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

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

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

下面以生产鸡为例:

定义鸡的实体

package xiong.demo6;public class Chicken {int id;public Chicken(int id){this.id = id;}
}

生产和消费都在缓冲区进行

package xiong.demo6;//缓冲区
public class SynContainer {//需要一个容器大小Chicken[] chickens = new Chicken[10];//容器计数器int count = 0;//生产者放入产品public synchronized void push(Chicken chicken){//如果容器满了,就需要等待消费者消费while (count == chickens.length){//生产等待try{this.wait();}catch (InterruptedException e){e.printStackTrace();}}//如果没有满。我们就需要丢入产品chickens[count++] = chicken;this.notify();}//消费者消费产品public synchronized Chicken pop(){//判断能否消费while(count==0){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果可以消费count--;Chicken chicken = chickens[count];//吃完了,通知消费者生产this.notifyAll();return chicken;}
}

定义生产者:

package xiong.demo6;public class Productor extends Thread {SynContainer container;public Productor (SynContainer container){this.container = container;}//生产@Overridepublic void run() {int i =0;while(true){i++;System.out.println("生产了"+i+"鸡");container.push(new Chicken(i));}}
}

定义消费者:

package xiong.demo6;public class Consumer extends Thread{SynContainer container;public Consumer(SynContainer container) {this.container = container;}//消费@Overridepublic void run() {int i =0;while(true){i++;System.out.println("消费了-->"+container.pop().id+"只鸡");try {Thread.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}}}
}

调用线程:

package xiong.demo6;public class TestPC {public static void main(String[] args) {SynContainer container  = new SynContainer();new Productor(container).start();new Consumer(container).start();}
}

运行结果如下:

 一边生产一边消费

4 解决方式2

并发协作模型 “ 生产者 / 消费者模式 ” --->信号灯法

5 使用线程池

线程池

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

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

好处: 

  • 提高响应速度(减少了创建新线程的时间) 
  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 
  • 便于线程管理(....) 

        corePoolSize:核心池的大小

        maximumPoolSize:最大线程数 

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

使用线程池

  • JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor 

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

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

        void shutdown():关闭连接池 

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

package xiong.demo7;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TestPool  {public static void main(String[] args) {//1.创建服务,创建线程池//newFixedThreadPool 参数为:线程池大小ExecutorService service = Executors.newFixedThreadPool(10);service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());//2.关闭连接service.shutdown();}
}

package xiong.demo7;public class MyThread implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}

运行结果如下:

 

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...