1.线程通信
1.线程通信概念
- 多个线程处理同一资源,但处理动作不同
2.为什么要进行线程通信
- 因为CPU会随机切换,而我们要多个线程共同协作操作同一资源
3.如何通过线程通信使多个线程实现有效协作
- 等待唤醒机制
2.等待唤醒机制
1.概念
- 多个线程的协作机制,当一个线程完成指定操作就等待(wait),当其他线程完成其操作后将其唤醒(notify),必要时可以唤醒所有线程(notifyAll)
2.等待唤醒方法
- public final void wait():通过锁对象使线程进入永久等待,等待唤醒
- public final void wait(long timeout)通过锁对象使对象进入计时等待
- public final void notify()唤醒该锁对象的一个等待线程
- public final void notifyAll()唤醒该锁对象的所有等待线程
3.注意点
- 即使唤醒了等待的线程,它也不一定会马上执行,因为它在同步块内,还需要获取锁才能继续执行
4.细节
- 等待(wait)和唤醒(notify)必须要由同一锁对象调用,因为用A锁对象等待的线程,只能通过A锁对象才能唤醒
- 等待(wait)和唤醒(notify)是属于Object类的,因为锁对象可以是任意对象
- 等待(wait)和唤醒(notify)必须在同步代码块或者同步方法内使用,因为必须通过锁对象才能调用两个方法
5.生产者与消费者案例
import java.util.Random;
public class demo8 {
public static void main(String[] args) {
BaoZi bz = new BaoZi();
BaoZiPu bzp = new BaoZiPu("包子铺",bz);
ChiHuo ch = new ChiHuo("吃货", bz);
bzp.start();
ch.start();
}
}
class BaoZi{//包子类
private String pu;
private String xian;
private boolean flag;
public String getPu() {
return pu;
}
public String getXian() {
return xian;
}
public boolean isFlag() {
return flag;
}
public void setPu(String pu) {
this.pu = pu;
}
public void setXian(String xian) {
this.xian = xian;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
class BaoZiPu extends Thread{//包子铺类
private BaoZi bz;
private int count;
Random r = new Random();
public BaoZiPu(String bzp, BaoZi bz) {
super(bzp);
this.bz = bz;
}
@Override
public void run() {//生产包子
while (true){
synchronized (bz){
if(bz.isFlag() == false){
if(r.nextInt(2)==0){
bz.setPu("面皮");
bz.setXian("韭菜");
}else {
bz.setPu("饺子皮");
bz.setXian("猪肉");
}
System.out.println(this.getName()+"做出了"+bz.getPu()+bz.getXian()+"包子");
bz.setFlag(true);
bz.notify();
}else {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(++count==100){
break;
}
}
}
}
class ChiHuo extends Thread {//吃货类
private BaoZi bz;
private int count;
public ChiHuo(String ch, BaoZi bz) {
super(ch);
this.bz = bz;
}
@Override
public void run() {//负责吃包子
while (true) {
synchronized (bz) {
if (bz.isFlag() == true) {
System.out.println(this.getName() + "吃了" + bz.getPu() + bz.getXian() + "包子");
bz.setFlag(false);
bz.notify();
} else {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(++count==100){
break;
}
}
}
}
3.线程池
1.由来
- 由于启动一个新线程成本较高,因其与操作系统交互,而使用线程池可较好的提高性能
2.概念
- 一个容纳多个线程的容器,其中的线程可以反复使用
3.优点
- 降低资源消耗,可重复使用线程
- 提高响应速度,因为线程已经创建
- 提高线程的可管理性,可以调整线程个数
4.线程池的使用
- public static ExecutorService newFixedThreadPool(intnThreads)返回指定容量的线程池对象
- public Future<?> submit(Runnable task或者Callabletast)获取线程池的某个线程对象并执行提交任务
- 当submit方法参数为Callable接口实现类对象,重写的call方法有返回值,可通过Future未来对象的get方法来获取
- ExecutorService 的void shutdown():关闭线程池
5.代码实例
import java.util.concurrent.*;
public class demo8 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
A a = new A();//
ExecutorService ex = Executors.newFixedThreadPool(1);//创建线程池对象
Future<Boolean> submit = ex.submit(a);//执行线程并提交执行结果给Future类对象
Boolean b = submit.get();//Future类对象获取返回值并接收
ex.shutdownNow();//关闭线程池
}
}
class A implements Callable<Boolean>{
@Override
public Boolean call() throws Exception {
return true;//重写call方法
}
}