【学习记录】线程学习(二):解决生产者消费者问题方法

本文只要是接上篇继续,上篇地址:
【学习记录】线程学习(一)

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

以上均是Object类方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常lllegalMonitorStateExcepton

经典问题:生产者、消费者问题解决办法

(1)使用管程法解决:


// ===> 使用管程法解决
public class testPC {
	
	public static void main(String[] args) {
		synContainer container = new synContainer();
		new Productor(container).start();	//生产者线程开启
		new Consumer(container).start();	//消费者线程开启
	}
}

class Productor extends Thread{
	synContainer container;
	public Productor(synContainer container) {
		this.container = container;
	}
	
	//生产:
	@Override
	public void run() {
		for (int i = 1; i < 100; i++) {
			System.out.println("生产者 ===> 生产了"+i+"只鸡");
			container.push(new Chicken(i));
		}
	}
}

class Consumer extends Thread{
	synContainer container;
	public Consumer(synContainer container) {
		this.container = container;
	}
	
	//消费
	@Override
	public void run() {
		for (int i = 1; i < 100; i++) {
			System.out.println("消费者 ===> 消费了"+container.pop().id+"只鸡");
		}
	}
}


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

class synContainer{
	
	Chicken[] chickens = new Chicken[10];
	int count = 0;
	
	//生产者放入产品
	public synchronized void push(Chicken chicken) {
		//如果容器满了,需要等待消费者
		if(count == chickens.length) {
			//通知消费者消费,生产等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		chickens[count] = chicken;
		count++;
		//通知消费者消费
		this.notifyAll();
	}
	
	//消费者消费产品
	public synchronized Chicken pop(){
		if(count==0) {
			//等待生产者生产,消费者等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//如果可以消费
		count--;
		Chicken chicken = chickens[count];
		
		//通知生产者生产
		this.notifyAll();
		return chicken;
	}	
}

(2)使用信号灯法解决:


// ===> 使用信号灯法(标志位flag)解决
public class testPC {
	public static void main(String[] args) {
		Cake cake = new Cake();
		new Producer(cake).start();
		new Consumer(cake).start();
	}
}

class Producer extends Thread{	//面包师
	
	Cake cake;
	public Producer(Cake cake) {
		this.cake = cake;
	}
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			this.cake.make("蛋糕");
		}
	};
}

class Consumer extends Thread{	//消费者
	Cake cake;
	public Consumer(Cake cake) {
		this.cake = cake;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			cake.eat();
		}
	};
}

class Cake{
	//面包师制作,消费者等待  True
	//消费者在吃蛋糕,面包师等待  False
	String cake;	//蛋糕
	boolean flag = true;	//标志位,这是解决的关键
	
	//制作蛋糕
	public synchronized void make(String cake) {
		//面包制作完了,面包师等待
		if (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("面包师===>制作了"+cake);
		//通知消费者可以吃了
		this.notifyAll();
		this.cake = cake;
		this.flag = !this.flag;
		
	}
	
	//吃蛋糕
	public synchronized void eat() {
		//如果蛋糕还没制作完成,消费者就等待蛋糕制作
		if(flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("消费者 ===> 吃完了"+cake);
		//消费者已经吃完了蛋糕,通知面包师制作蛋糕
		this.notifyAll();
		this.flag = !this.flag;		
	}
}

十、线程池
1、背景:

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

2、思路:

提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
(可以避免频繁创建销毁,实现重复利用)

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

corePoolSize:核心池大小

maximumPoolSize:最大线程数

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

4、使用:

线程池相关API:ExecutorServiceExecutors

(1)ExecutorService:

真正的线程池接口(常见子类ThreadPoolExecutor)

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

//执行任务,有返回值,一般用来执行Callable
<T> Future<T> submit(Callable<T> task)
 
//关闭连接池
void shutdown()	

//创建线程池例子
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class testPool {
	
	public static void main(String[] args) {
		
		//创建服务,创建线程池
		//newFixedThreadPool:参数为线程池大小
		ExecutorService service = Executors.newFixedThreadPool(10);
		
		//执行
		service.execute(new MyThread());
		service.execute(new MyThread());
		service.execute(new MyThread());
		
		//关闭连接
		service.shutdown();
	}

}
class MyThread implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName());	
	}
}

(2)Executors:

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

tip: 线程开启不一定立即执行,由CPU调度执行


本文笔记主要来源:【狂神说Java】多线程详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 生产者-消费者问题是指在多线程环境下,生产者线程和消费者线程共享同一个缓冲区,生产者线程向缓冲区中添加数据,而消费者线程从缓冲区中取出数据。为了避免生产者线程向已满的缓冲区中添加数据,或者消费者线程从空的缓冲区中取出数据,需要使用信号量来进行同步和互斥。 记录型信号量是一种特殊的信号量,它可以记录当前可用的资源数量。在生产者-消费者问题中,可以使用两个记录型信号量来解决该问题。一个信号量记录缓冲区中可用的空间数量,另一个信号量记录缓冲区中已有的数据数量。 当生产者线程向缓冲区中添加数据时,需要先判断空间是否足够,如果空间不足,则需要等待空间释放。此时,生产者线程会对空间信号量进行P操作,表示需要占用一个空间。当消费者线程从缓冲区中取出数据时,需要先判断是否有数据可用,如果没有数据可用,则需要等待数据的产生。此时,消费者线程会对数据信号量进行P操作,表示需要占用一个数据。 当生产者线程向缓冲区中添加数据成功后,需要对空间信号量进行V操作,表示释放一个空间。当消费者线程从缓冲区中取出数据成功后,需要对数据信号量进行V操作,表示释放一个数据。 通过使用记录型信号量,可以有效地解决生产者-消费者问题,保证生产者线程和消费者线程之间的同步和互斥。 ### 回答2: 在生产者-消费者问题中,生产者需要向缓冲区中存入数据,而消费者则需要从缓冲区中取出数据。为了保证生产者和消费者之间的同步和顺序,需要使用信号量。记录型信号量是一种特殊的信号量,在解决生产者-消费者问题时非常有用。 记录型信号量是一种可以记录其值的信号量。在生产者-消费者问题中,可以使用两个记录型信号量表示缓冲区中可以存储的数据的数量和已有数据的数量。当生产者向缓冲区中存入数据时,需要先对空闲空间计数信号量减1。如果空闲空间数为0,则生产者需要等待,直到有其他进程从缓冲区中取出数据并释放空间。在消费者从缓冲区中取出数据时,需要对可取数据数计数信号量减1。如果可取数据数为0,则消费者需要等待,直到有其他进程向缓冲区中存入数据并释放空位。 使用这种方法可以避免生产者和消费者同时访问缓冲区,从而保证同步和顺序。此外,记录型信号量还可以记录任务的优先级和重复执行次数等信息,可用于更复杂的问题。 ### 回答3: 生产者-消费者问题是一种经典的同步问题,一般指在多线程或多进程环境下,生产者从缓冲区中生产数据到缓冲区,消费者从缓冲区中取出数据进行消费,但是生产者和消费者线程在运行时可能会因为某些原因导致竞争关系和不正确的执行顺序,因此需要使用一些同步机制来解决这种竞争关系。记录型信号量是一种有效的同步机制,能够解决生产者-消费者问题。 记录型信号量是一种同步原语,是由一个整数值和一个等待队列组成的。当一个线程需要使用共享资源时,它会降低信号量的值,如果信号量的值为负数,就表示有线程在等待该共享资源,此时会将该线程放入等待队列中,直到有其他线程释放信号量,使得信号量的值为正时,才会唤醒等待队列中的线程继续执行。 在生产者-消费者问题中,可以定义两个记录型信号量分别表示缓冲区中可用的数据量和空的数据量。生产者线程需要向缓冲区中生产数据时,首先需要申请一个空的数据块,然后将数据写入该数据块中,最后将可用数据量信号量的值加1。如果可用数据量的信号量值为负,就表示当前没有空的数据块可用,这时生产者线程会被阻塞,等待消费者线程释放一个数据块。消费者线程从缓冲区中取出数据时,首先需要申请一个可用的数据块,然后将数据读出该数据块并进行消费,最后将空的数据量信号量的值加1。如果空的数据量的信号量值为负,就表示当前没有可用的数据块,这时消费者线程会被阻塞,等待生产者线程释放一个数据块。 使用记录型信号量解决生产者-消费者问题,可以避免在多线程或多进程环境下的竞争冲突和执行顺序错误,使得生产者和消费者线程之间得到正确同步。同时,记录型信号量也可以避免因阻塞和唤醒操作而导致系统资源浪费的问题。因此,记录型信号量是一种非常有效的同步机制,可以广泛应用于多线程和多进程编程中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值