线程协作
生产者消费者模式
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.
- 对于生产者,没有生产产品之前,要通知消费者等待.而生产了产品之后,又需要马上通知消费者消费
- 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费,
- 在生产者消费者问题中,仅有synchronized是不够的
- synchronized 可阻止并发更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之间的消息传递(通信)
Java提供了几个方法解决线程之间的通信问题
| 方法名 | 作用 |
|---|---|
| wait() | 表示线程一直等待,直到其他线程通知,与sleep不同会释放锁 |
| wait(long timeout) | 指定等待的毫秒数 |
| notify() | 唤醒一个处于等待状态的线程 |
| notifyAII() | 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度 |
管程法
生产者消费者模型利用缓冲区解决
public class TestPC {
public static void main(String[] args) {
SynContainer s = new SynContainer();
new Producer(s).start();
new Consumer(s).start();
}
}
class Producer extends Thread {
SynContainer container;
public Producer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.addChicken(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+container.removeChicken().id+"只鸡");
}
}
}
class Chicken {
int id;
Chicken(int id) {
this.id = id;
}
}
class SynContainer {
// 制定容器大小
Chicken[] chickens = new Chicken[10];
// 计数器
int count = 0;
// 生产者放入产品
public synchronized void addChicken(Chicken chicken) {
while (count == chickens.length) {
// 通知消费者消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
chickens[count++] = chicken;
// 通知消费
this.notifyAll();
}
public synchronized Chicken removeChicken() {
while (count == 0) {
// 等待生产
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Chicken chicken = chickens[--count];
// 通知生产
this.notifyAll();
return chicken;
}
}
在wait()的使用场景中,必须用while循环包裹条件判断,而非if。这是多线程编程的经典规范,目的是:
应对虚假唤醒,确保线程不会在条件不满足时错误执行;
处理多线程竞争下,唤醒后条件已失效的情况,保证逻辑正确性。
信号灯法
// 信号灯法,标志位解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Actor(tv).start();
new Audience(tv).start();
}
}
// 生产者 -> 演员
class Actor extends Thread {
TV tv;
Actor(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
this.tv.play("java...");
} else {
this.tv.play("golang... ");
}
}
}
}
// 消费者 -> 观众
class Audience extends Thread {
TV tv;
Audience(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watch();
}
}
}
// 产品 -> 节目
class TV {
// 拍电影 观众等, 看电影 演员等
String voice;
boolean flag = true; // true -> 没有电影
// 表演
public synchronized void play(String voice) {
while (!flag) { // 注意:这里是!flag,因为flag=false表示已有节目,需等待消费
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("playing " + voice);
// 通知观看
this.voice = voice;
this.flag = false;
this.notifyAll(); // 锁在方法或代码块执行执行完后才释放,所以通知也可以放在前面
}
// 观看
public synchronized void watch() {
while (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 通知表演
System.out.println("watching " + this.voice);
this.flag = true;
this.notifyAll();
}
}
线程池
- 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
- 好处:
- 提高响应速度 (减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)便于线程管理(..)
- corePoolSize:核心池的大小maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
// 创建线程池,制定参数大小
ExecutorService pool = Executors.newFixedThreadPool(10);
// 执行
pool.execute(new myThread());
pool.execute(new myThread());
pool.execute(new myThread());
pool.execute(new myThread());
pool.execute(new myThread());
// 关闭
pool.shutdown();
}
}
class myThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

浙公网安备 33010602011771号