多个线程同时运行时,会产生线程并发可使用同步操作确保数据的安全性,如果需要各线程之间交互,可是使用线程等待和唤醒模式,在这里常用的等待唤醒中经典的模式为“生产者和消费者模式”
生产者和消费者由两类线程组成: 若干个生产者线程 负责提交用户的请求,若干个消费者线程负责处理生成出来的任务。 他们操作一块共享内存区进生成/消费的产品(数据): Mobile (手机编号)
生成者线程类: Provider : 无限制的生成手机
消费者线程类:Customer : 无限制的消费手机
共享存储区: Storage ( push 、pop) 存储手机的对象数组
测试类 行数据通信。```java
public class Mobile {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Mobile(int id) {
this.id = id;
}
}
存储区
```java
package com.j2008.provider_customer;
/**
* ClassName: Storage
* Description:
* date: 2020/11/10 15:32
* 存储区,它是生产者和消费者共享的空间 ( 生产者和消费者将该对象作为公有锁)
* @author wuyafeng
* @version 1.0 softeem.com
*/
public class Storage {
// 定义存储手机的对象数据
Mobile [] mobiles = new Mobile[10];
int index=0; // 个数
static int n=1000;
/**
* 存放手机
* @param mobile
*/
public synchronized void push(Mobile mobile){
//考虑容器上限已满,必须等待
while(index == mobiles.length){
System.out.println("容器已满,需等待");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//通知消费者取消费 ,将其他线程全部唤醒
this.notifyAll();
mobiles[index]=mobile;
index++;
}
/**
* 取出手机 1 2 3 4
* 1 2 3
* index--;
* mobile[index] =null
* @return 取出的手机对象
*/
public synchronized Mobile pop(){
Mobile m = null;
// 判断index是否小于0
while(index<=0){
//等待
System.out.println("容器中没有手机,需要等待");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
m = mobiles[index];
//将容器中的这个位置 设置为空
mobiles[index]=null;
// 通知生产者去生产
this.notifyAll();
return m;
}
public synchronized int getSize(){
return index;
}
}
生产者:
package com.j2008.provider_customer;
/**
* ClassName: Provider
* Description:
* date: 2020/11/10 15:54
*
* @author wuyafeng
* @version 1.0 softeem.com
*/
public class Provider implements Runnable {
//共享存储区
Storage storage =null;
public Provider(Storage storage){
this.storage = storage;
}
@Override
public void run() {
//手机编号
int n=1000;
//一直生产
while(true){
Mobile m = new Mobile(n);
storage.push(m);
System.out.println(Thread.currentThread().getName()+
"生产了一部手机,其编号:"+m.getId()+" 其库存:"+storage.getSize());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
n++;
}
}
}
消费者
package com.j2008.provider_customer;
/**
* ClassName: Customer
* Description:
* date: 2020/11/10 15:58
*
* @author wuyafeng
* @version 1.0 softeem.com
*/
public class Customer implements Runnable {
Storage storage=null;
public Customer(Storage storage){
this.storage = storage;
}
@Override
public void run() {
while(true){
Mobile mobile = storage.pop();
System.out.println(Thread.currentThread().getName()+
"消费了一部手机,编号》》"+mobile.getId()+" 库存:"+storage.getSize());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类
package com.j2008.provider_customer;
/**
* ClassName: TestProviderCustomer
* Description:
* date: 2020/11/10 16:01
*
* @author wuyafeng
* @version 1.0 softeem.com
*/
public class TestProviderCustomer {
public static void main(String[] args) {
//创建公有的存储空间
Storage storage = new Storage();
Provider provider1 = new Provider(storage);
Provider provider2 = new Provider(storage);
Provider provider3 = new Provider(storage);
Thread th1 = new Thread(provider1,"张飞");
Thread th2 = new Thread(provider2,"刘备");
Thread th3 = new Thread(provider3,"关羽");
th1.start();
th2.start();
th3.start();
//消费者上场
Customer customer1 = new Customer(storage);
Customer customer2 = new Customer(storage);
Customer customer3 = new Customer(storage);
Thread th4 = new Thread(customer1,"张飞的老婆");
Thread th5 = new Thread(customer2,"刘备的老婆");
Thread th6 = new Thread(customer3,"关羽的老婆");
th4.start();
th5.start();
th6.start();
}
}
测试结果
关羽生产了一部手机,其编号:1000 其库存:1
张飞的老婆消费了一部手机,编号》》1000 库存:0
张飞生产了一部手机,其编号:1000 其库存:1
刘备生产了一部手机,其编号:1000 其库存:2
刘备的老婆消费了一部手机,编号》》1000 库存:1
关羽的老婆消费了一部手机,编号》》1000 库存:0
容器中没有手机,需要等待
关羽生产了一部手机,其编号:1001 其库存:0
张飞的老婆消费了一部手机,编号》》1001 库存:0
张飞生产了一部手机,其编号:1001 其库存:2
刘备生产了一部手机,其编号:1001 其库存:2
关羽的老婆消费了一部手机,编号》》1001 库存:0
刘备的老婆消费了一部手机,编号》》1001 库存:0
容器中没有手机,需要等待
关羽生产了一部手机,其编号:1002 其库存:0
张飞的老婆消费了一部手机,编号》》1002 库存:0
刘备生产了一部手机,其编号:1002 其库存:2
张飞生产了一部手机,其编号:1002 其库存:2
刘备的老婆消费了一部手机,编号》》1002 库存:1
关羽的老婆消费了一部手机,编号》》1002 库存:0
```## 1、定义
用于创建和管理线程的容器就是线程池 (Thread Pool) ,在线程池中的线程执行完任务后不会立马进入销毁状态,而是重置到线程池中变为“空闲线程” 。 有利于避免频繁创建线程消耗资源,提供线程复用率,有限管理该线程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lhcrieyn-1605486918300)(assets/1604998934958.png)]
2、使用线程池的原因:
在多线程环境下,对于不断创建和销毁效率非常消耗系统资源,对于多线程之间的切换存在线程安全问题, 这是使用统一的管理类管理一些线程是比较好的解决办法
3、线程的运行机制:
- 在线程池模式下,任务是提交给线程池,由线程池根据当前空闲线程进行分配任务,如果没有空闲线程,由管理类创建线程或者进入任务等待队列中。
- 一个线程同时只能执行一个任务,但多个任务可以同时提交给这个线程池。
线程池的常用类 (ExecutedService) return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
参数1: corePoolSize:核心线程数
参数2:maximumPoolSize :最大线程数
参数3: keepAliveTime : 线程活动时长
参数4: 对于参数3的单位
1、可缓存的线程池 newCacheThreadPool(n);如果线程池中没有空闲线程,则创建新线程并放入线程池中,无上限线程数,如果有空闲线程则直接使用该线程
public static void main(String[] args) {
// 创建可缓存线程
ExecutorService service =Executors.newCachedThreadPool();
// 创建10个线程
int n=0;
for(int i = 0 ;i < 10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {
@Override //你们内部类
public void run() {
//任务
System.out.println(Thread.currentThread().getName()+"---"+n);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//由于以上的线程 每次执行完成只需要500毫秒 ,会释放线程对象到线程池中
// 1000毫秒后创建的线程 就会复用上一次的线程对象
}
//关闭线程池
service.shutdown();
}
}
2、可重用的固定线程池: newFixedThreadPool(n) ,线程数量固定,如果没有空闲线程,则存放无界队列中等待
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(3);
// 连续创建10个线程, 由于只有3个线程,所有线程只能等待
// 2秒后再执行3个
for(int i =0;i<10;i++){
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
// 结果: 每2秒执行3个线程
}
4、单线程的线程池 newSingleThreadExecutor : 线程池中只有一个线程数,所有的任务都通过该线程执行,它可以保证所有的任务是FIFO模式, 满足队列结构