[学习视频B站狂神java多线程:https://www.bilibili.com/video/BV1V4411p7EF?]
线程通信
让不同线程之间进行交流,而不是独自执行自己的任务,会传递一些信息
Java提供的用于解决线程之间通信问题的方法
- wait():表示线程一直等待,知道其他线程通知,与sleep不同,wait会释放锁
- wait():指定等待的毫秒数
- notify():唤醒一个处于等待状态的线程
- notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度
- 所有的方法均是Object类的方法,都只能在同步方法或同步代码块中使用,否则会抛出异常
应用场景:生产者和消费者问题
- 对于生产者,没有生产产品之前,要通知消费者等待,生产了产品之后,需要马上通知消费者消费
- 对于消费者,消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
管程法
- 生产者:负责生产数据
- 消费者:负责处理数据
- 缓冲区:消费者不能直接使用生产者的数据,通过缓冲区进行
- 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
//生产者消费者:缓冲区(管程法)
public class TestPC1 {
public static void main(String[] args) {
box box=new box();
Productor p=new Productor(box);
Customer c=new Customer(box);
p.start();
c.start();
}
}
//生产者
class Productor extends Thread {
box box;
public Productor(box box) {
this.box = box;
}
@Override
public void run() {
synchronized (box) {
for (int i = 1; i <= 100; i++) {
try {
box.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产第" + i + "只鸡");
}
}
}
}
//消费者
class Customer extends Thread {
box box;
public Customer(box box){
this.box=box;
}
@Override
public void run() {
synchronized (box){
for(int i=1;i<=100;i++){
try {
System.out.println("消费者消费第"+box.pop().id+"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//缓冲区
class box {
//容器大小
Chicken[] c = new Chicken[10];
//容器已有产品个数
int count = 0;
//生产者放入产品
public void push(Chicken chicken) throws InterruptedException {
//判断容器是否满
if (count == c.length) {
//如果满,生产者等待
this.wait();
}
//如果不满,则放入产品
c[count++] = chicken;
this.notifyAll();
}
//消费者消费产品
public Chicken pop() throws InterruptedException {
//判断容器是否为空
if (count == 0) {
//如果空,等待并通知生产者生产
this.wait();
}
//如果不空,则消费
this.notifyAll();
return c[--count];
}
}
//产品对象
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
信号灯法
设定一个用于判断生产者生产或者消费者消费的信号灯,信号灯的不同状态,进行相应的线程工作
//生产者消费者:信号灯法
//信号灯法可以看作没有缓冲或者缓冲区容量为1,此时将缓冲区容量的判断条件放在一个boolean中,因此不需要缓冲区
//如在两个线程中,线程A工作,B就停止,线程B工作,A就停止,两者不能同时
public class TestPC2 {
public static void main(String[] args) {
Product product = new Product();
Story story = new Story(product);
Buyer buyer = new Buyer(product);
story.start();
buyer.start();
}
}
//生产者
class Story extends Thread {
Product product;
public Story(Product product) {
this.product = product;
}
@Override
public void run() {
for (int i = 0; i < 24; i++) {
if (i % 2 == 0) {
try {
product.push("面包");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
product.push("巧克力");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//消费者
class Buyer extends Thread {
Product product;
public Buyer(Product product) {
this.product = product;
}
@Override
public void run() {
for (int i = 0; i < 24; i++) {
try {
product.pop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//产品对象
class Product {
String thing;
boolean flag = true;//判断标志位,true代表生产者生产,消费者等待,false代表生产者等待,消费者消费
public void push(String str) throws InterruptedException {
synchronized (this) {
if (!flag) {
this.wait();
}
//通知消费者消费
System.out.println("生产者生产了"+str);
this.thing = str;
this.flag = !this.flag;
this.notifyAll();
}
}
public void pop() throws InterruptedException {
synchronized (this) {
if (flag) {
this.wait();
}
//通知生产者生产
System.out.println("消费者消费了"+thing);
this.flag = !this.flag;
this.notifyAll();
}
}
}
线程池
- 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
- 思路:提前创建好多个线程,放入线程池,使用时直接获取,使用完放回,避免频繁创建销毁
- 方法:1、corePoolSize:核心池大小
2、maximunPoolSize:最大线程数
3、keepAliveTime:线程没有任务时多长时间会终止
线程池的实现
- ExecutorService:线程池接口,常见子类ThreadPoolExecutor
1、void execute(Runnable command):执行命令,没有return,用来执行Runnable
2、 Future submit(Callable task):执行任务,有return,用来执行Callable
3、void shutdown():关闭连接池 - Executors:工具类、线程池的工厂类,用于创建并返回不同类型线程池
public class TestThreadPool {
public static void main(String[] args) {
//创建服务,创建线程池
ExecutorService service= Executors.newFixedThreadPool(10);
service.execute(new MyThread());
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());
}
}