进程间的通信:多个线程在处理同一个资源,但是处理的动作(线程的动作)却不相同。
进程间的通信可以让资源进行有效的利用
等待唤醒中的方法:
- wait :让线程进入等待状态
- notify :随机唤醒一个线程
- notifyAll :唤醒所有线程
注意:等待唤醒中的方法必须要由同一个对象锁调用,而这个对象锁可以是任意对象,因为等待唤醒中的方法来自Object类,而Object类是所有类的父类。而且必须写在同步方法或者同步代码块里面。
下面是包子铺生产包子和顾客买包子的例子
//包子铺 public class baozipu extends Thread{private baozi bao;public baozipu(baozi bao) {this.bao=bao;}@Overridepublic void run() {int count = 0;while (true) {synchronized (this.bao) {if (bao.isFlag()) {try {bao.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}if (count % 2 == 0) {bao.setPi("薄皮");bao.setXian("猪肉馅");} else {bao.setPi("厚皮");bao.setXian("猪肉馅");}count++;System.out.println("正在生产" + bao.getPi() + bao.getXian() + "包子.....");try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}bao.setFlag(true);bao.notify();System.out.println("包子做好啦");}}} }
//包子 public class baozi {private String pi;private String xian;private boolean flag;public String getPi() {return pi;}public void setPi(String pi) {this.pi = pi;}public String getXian() {return xian;}public void setXian(String xian) {this.xian = xian;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;} }
//顾客 public class guge extends Thread{private baozi bao;public guge(baozi bao) {this.bao = bao;}@Overridepublic void run() {while(true){synchronized(bao){if (bao.isFlag()==false){try {bao.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("吃"+bao.getPi()+bao.getXian()+"包子");bao.setFlag(false);bao.notify();System.out.println("吃完了,开始生产包子...");System.out.println("--------------------------");}}} }
//测试 public class Test {public static void main(String[] args) {baozi bao=new baozi();baozipu bzp=new baozipu(bao);guge gg=new guge(bao);gg.start();bzp.start();} }
如果并发的线程数量很多,并且每个线程都是执行一个很短的时间就结束了,这样频繁的创建线程和销毁线程就会大大降低系统的效率,因为创建线程和销毁线程很消耗时间。
我们可以使用java中的线程池来解决这个问题
线程池就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程的时间,减少了不必要的资源消耗。当程序第一次启动的时候,创建多个线程保存到一个集合中,当我们需要使用的时候,再从集合中remove出来使用,使用remove是因为remove之后线程池中就移除了这个线程,而remove的返回值就是我们remove的线程。
线程池会一直开启,除非手动使用shutdown方法(不建议),某个线程被使用完了之后会自动归还给线程池供其他任务使用。
好处
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
- 线程池的使用步骤
使用线程池的工厂类Executors里面提供的静态方法类newFixedThreadPool生产一个指定线程数量的线程池
创建一个实现Runnable接口的类,重写run方法,用于设置线程任务
调用ExecutorsService中的submit方法,传递线程任务,开启线程
最后程序结束的时候,可以调用ExecutorsService中的shutdown方法销毁线程池,但是不建议销毁,因为我们创建线程池本来就是为了重复利用里面的线程的
//Runnable接口的实现类 public class testimpl implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"执行");} }//测试类 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class Test {public static void main(String[] args) {ExecutorService es= Executors.newFixedThreadPool(2);//创建一个包含两个线程的线程池es.submit(new testimpl());es.submit(new testimpl());es.submit(new testimpl());es.submit(new testimpl());} }