线程的等待和通知一般用的是 wait()和notity方法
wait()让当前线程进入等待状态,直到被通知为止 wait(long)能够设定时间被通知或时间结束
notify()随机通知一个等待的线程 notifyAll()通知所有的等待线程
package com;
public class WaitDemo {
public void print() throws InterruptedException {
for (int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName() + "" + i);
if (i==50){
//让线程开始等待
this.wait();
System.out.println("线程开始等待");
}
}
}
public void notifyTest(){
//让等待的线程开始执行
this.notifyAll();
}
public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo = new WaitDemo();
new Thread(() ->{
synchronized (waitDemo){
try {
waitDemo.print();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(5000);
synchronized (waitDemo){
waitDemo.notify();
}
}
}
通过notifyAll通知被等待的线程让线程继续执行
wait()和sleep()的区别
wait()来自Object sleep方法来自Thread
wait()是由锁对象调用 sleep()是由线程调用
wait()执行后会自动释放锁 sleep()执行后不会释放锁
wait执行后可以被通知唤醒也可以设定时间等时间结束
sleep执行后只能等待时间结束后自动唤醒不能通过通知唤醒
生产者消费者模式
某个模块负责产生数据,这些数据由另一个模块来负责处理。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
生产者消费者模式的优点
解耦: 利用缓存区让生产者和消费者不直接依赖,降低了耦合性。
支持并发:生产者和消费者是两个独立的并发体,他们中间是通过缓冲区连接,生产者只需要往缓冲区丢数据,消费者只需要从缓冲区拿数据,这样就能解决因为处理速度的不同而发生阻塞.
忙闲不均:
当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。 等生产者的制造速度慢下来,消费者再慢慢处理掉,两者都能
阻塞队列:
提供了阻塞的入列方法和阻塞的出队方法,如果队列满了就让方法一直处于阻塞状态直到有空间 为止,如果队列空了就让出列方法一直阻塞直到队列有数据可用
BlockingQueue接口
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
//初始化一个排队队列,能够容纳10个人
BlockingQueue<Person> queue = new ArrayBlockingQueue<Person>(10);
Station station = new Station(queue);
Texi texi1 = new Texi(queue,"texi1");
Texi texi2 = new Texi(queue,"texi2");
Texi texi3 = new Texi(queue,"texi3");
service.execute(station);
service.execute(texi1);
service.execute(texi2);
service.execute(texi3);
Thread.sleep(3000);
service.shutdownNow();
}
//定义排队的人
static class Person {
private String name;
public Person(String name) {
this.name = name;
System.out.println(name + "被创建");
}
//上车出发
public void setOff() {
System.out.println("Nice,I will set off,I am" + name);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
//定义生产者,假定只有一个生产者
static class Station implements Runnable {
private BlockingQueue<Person> queue;
volatile static int count = 0;
public Station(BlockingQueue<Person> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
//生产人
System.out.println("火车站准备生产人: " + System.currentTimeMillis());
Person person = new Person("SB" + count++ + "号");
queue.put(person);
System.out.println("火车站生产人完毕: " + System.currentTimeMillis());
//休眠300ms
Thread.sleep(300);
}
} catch (InterruptedException ex) {
}
}
}
//定义一个消费者
static class Texi implements Runnable {
private BlockingQueue<Person> queue;
private String name;
public Texi(BlockingQueue<Person> queue, String s) {
this.name = s;
this.queue = queue;
}
public void run() {
try {
while (true) {
//消费乘客
System.out.println("消费者" + name + "准备消费乘客: " + System.currentTimeMillis());
Person person = queue.take();
//Yahoo,出发喽!!
person.setOff();
System.out.println("消费者" + name + "消费乘客完毕: " + "|||||||" + System.currentTimeMillis());
//休眠1000ms,才能接下一个客
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
}
}
}
实现类
ArrayBlockingQueue 类 数据结构为数组
LinkedBlockingQueue类 链表结构
线程池:
为什么要使用线程池:线程池能够回收利用线程资源
1:线程和任务分离,提升线程重用性;
2:控制线程并发数量,降低服务器压力,统一管理所有线程;
3:提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;