嗯嗯..对于一个新手来说,线程的协同作用翻译过来就是就是多个线程要处理同一个资源,处理的动作有先后或者制约的束缚。比如A线程生成包子,B线程吃包子,只有A生成包子B才能吃到,再多一点约束比如只有B吃了包子A才会做新的包子,这样线程A和线程B就有了协同的作用。
刚刚的包子生产其实就是生产者与消费者问题,之前在学校学习操作系统中已经有过比较深入的了解(PV信号量),这就要使用到线程的等待唤醒机制(wait-notify)
这是包子问题的简单实现:
public class PVTest { public static void main(String[] args) { Baozi baozi = new Baozi(); Chihuo c = new Chihuo("胡建",baozi); Baozipu b = new Baozipu("狗不理包子铺",baozi); c.start(); b.start(); } } class Baozi{ //资源类 String pier; String xianer; boolean flag = false; } class Chihuo extends Thread{ //通过继承Thread来创建线程 private Baozi baozi; public Chihuo(String name,Baozi baozi){ super(name); this.baozi = baozi; } @Override public void run() { while(true){ synchronized (baozi){ //同步线程,重要的是协同的两个线程锁一定是一样的 if(baozi.flag == false){ try{ baozi.wait(); //要是没有包子了吃货就等着呗 }catch (InterruptedException e){ e.printStackTrace(); } } System.out.println("吃货正在吃"+baozi.pier+baozi.xianer+"包子"); baozi.flag = false; baozi.notify(); //唤醒线程 try { Thread.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } } } }} class Baozipu extends Thread{ private Baozi baozi; public Baozipu(String name,Baozi baozi){ super(name); this.baozi = baozi; } int count = 0; @Override public void run() { while(true){ synchronized (baozi){ if(baozi.flag == true){ //要是有包子就等吃货吃完再做 try{ baozi.wait(); }catch (InterruptedException e){ e.printStackTrace(); } } if(count%2==0){ //这里就是随便弄两种包子 baozi.pier = "冰皮"; baozi.xianer = "素三鲜"; } else{ baozi.pier = "薄皮"; baozi.xianer = "猪肉大葱"; } count++; System.out.println("包子铺做好了"+baozi.pier+baozi.xianer+"饺子"); System.out.println("可以吃了啊"); baozi.flag = true; baozi.notify(); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
通过这种实例就比较容易理解线程协同工作,但是值得注意的是唤醒的线程不一定立刻就可以运行,如果没有获得锁唤醒的线程就会进入就绪状态
接下来我还学习到了线程池的概念
线程池: 其实就是一个容纳多个线程的容器 ,其中的线程可以反复使用 ,省去了频繁创建线程对象的操作 , 无需反复创建线程而消耗过多资源。 合理利用线程池能够带来三个好处: 1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。 2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。 3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内 存,而把服务器累趴下 ( 每个线程需要大约 1MB 内存 ,线程开的越多,消耗的内存也就越大,最后死机 ) 。 那我们该怎样创建线程池呢? Java 里面线程池的顶级接口是 java.util.concurrent.Executor ,但是严格意义上讲 Executor 并不是一个线程 池,而只是一个执行线程的工具。真正的线程池接口是 java.util.concurrent.ExecutorService 。 要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优 的,因此在 java.util.concurrent.Executors 线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官 方建议使用 Executors 工程类来创建线程池对象。 Executors 类中有个创建线程池的方法如下: public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。 ( 创建的是有界线 程池 , 也就是池中的线程个数可以指定最大数量 ) 获取到了一个线程池 ExecutorService 对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下: public Future<?> submit(Runnable task) : 获取线程池中的某一个线程对象,并执行 Future 接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。 使用线程池中线程对象的步骤: 1. 创建线程池对象。 2. 创建 Runnable 接口子类对象。 (task) 3. 提交 Runnable 接口子类对象。 (take task) 4. 关闭线程池 ( 一般不做 ) 。 以上是我从教科书COPY下来,作为初学者我还没能够弄懂其中的种种机制,只是学会了创建线程池并且使用其中线程