本文只要是接上篇继续,上篇地址:
【学习记录】线程学习(一)
九、线程通信
- 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:ExecutorService 和 Executors
(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】多线程详解