生产者消费者问题
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者二号消费者之间相互依赖,互为条件。
在生产者消费者问题中,仅有synchronized是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
Java提供了几个方法解决线程之间的通信问题
方法名 | 作用 |
wait() | 表示线程一直等待,知道其他线程通知,与sleep不同(不释放锁),会释放锁 |
wait(long timeout) | 指定等待毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll | 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度 |
解决方式
1.管程法
生产者将生产好的数据存入缓冲区,消费者从缓冲区拿出数据
package Thread;
public class Thread3 {
public static void main(String[] args) {
BufferArea bufferArea=new BufferArea();
new Producer(bufferArea).start();
new Consumer(bufferArea).start();
}
}
//产品
class Product{
int id;
public Product(int id) {
this.id = id;
}
}
//生产者
class Producer extends Thread{
BufferArea bufferArea;
public Producer(BufferArea bufferArea) {
this.bufferArea = bufferArea;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产第"+i+"个产品");
bufferArea.pushProduct(new Product(i));
}
}
}
//消费者
class Consumer extends Thread{
BufferArea bufferArea;
public Consumer(BufferArea bufferArea) {
this.bufferArea = bufferArea;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("拿走第"+bufferArea.getProduct().id+"产品");
}
}
}
//缓冲池
class BufferArea{
//缓冲池长度为10;
Product []products=new Product[10];
//缓冲池数量
int count=0;
//生产者往缓冲池放入产品
public synchronized void pushProduct(Product product){
if(count==products.length){
//如果缓冲池产品满了,停止生产,等待消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products [count]=product;
count++;
//唤醒消费者消费
this.notify();
}
//消费者消费产品
public synchronized Product getProduct(){
//如果缓冲池没有产品,停止消费,唤醒生产者生产
if (count==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有产品就取走
count--;
Product product=products[count];
//唤醒生产者
this.notify();
return product;
}
}
2.信号灯法
通过标志位体现
package Thread.Thread3;
//信号灯法 生产者消费者问题
public class ThreadTest2 {
public static void main(String[] args) {
Television television=new Television();
new Actor(television).start();
new Audience(television).start();
}
}
//生产者--》演员
class Actor extends Thread{
Television television;
public Actor(Television television) {
this.television = television;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0){
this.television.Perform("<<世上只有妈妈好>>");
}
this.television.Perform("广告时间:送礼就送脑白金");
}
}
}
//消费者--》观众
class Audience extends Thread{
Television television;
public Audience(Television television) {
this.television = television;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.television.Watch();
}
}
}
//产品
class Television {
//flag=true 演员表演,观众等待
//flag=false 观众观看,演员等待
boolean flag=true;
//节目内容为唱歌
private String voice;
//演员表演节目
public synchronized void Perform( String voice){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//演员表演完毕,提醒观看
}else{
System.out.println("节目内容为:"+voice);
this.notifyAll();
//传入参数
this.voice=voice;
//刷新标志位
this.flag=!this.flag;
}
}
//观众观看节目
public synchronized void Watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了"+voice);
this.notifyAll();
this.flag=!this.flag;
}
}
线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理:
corePoolSize | 核心池大小 |
maximumPoolSize | 最大线程数 |
keepAliveTime | 线程没有任务时最多保持多久时间会终止 |
使用线程池
JDK5.0后提供了线程池相关API:ExecutorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
void execute(Runnable command) | 执行命令,没有返回值,一般执行Runnable |
《T》Future 《T》submit(Callable《T》task) | 执行命令,有返回值,一般执行Callable |
void shutdown(); | 关闭连接池 |
package Thread.Thread3;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
//创建
ExecutorService service= Executors.newFixedThreadPool(5);
Test test=new Test();
//执行
service.execute(test);
service.execute(test);
service.execute(test);
//关闭
service.shutdown();
}
}
class Test implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
ps:b站狂神学习