JAVA学习笔记 之 线程池

线程池技术概述

  在JDK之前,还不支持线程池技术,那时程序员需要自己编写线程池非常麻烦。但后来sun公司宣布自己开发线程池,这就是今天学习的内容。当我们使用一个线程的时候,用起来还是很方便的。但当线程很多的时候,每个线程很可能执行一个短时间的任务就结束了,这样总是开线程和关线程很浪费系统资源。所以,线程池技术就相当于开了一个鱼塘,把这些线程养起来,执行完一个任务之后并不销毁。

 

实现线程池

      

  方法:使用工厂类Executors中的静态方法创建线程对象,指定线程个。static ExecutorService.newFixedThreadPool(int  个数)返回线程池对象。返回的是ExecutorService接口的实现类。然后结构实现类对象,调用方法submit (Runable r)提交线程任务。

  如图所示,我们建立ThreadPoolRunable类去继承Runable 作为接口的实现类,然后在主函数里面去调用newFixedThread方法建立两个线程并 用ExecutorService去接收,最后再submit调用。调用的时候,还得实例化一个实现类的对象,就是括号里面的内容。

 

        

  在使用Runable接口实现线程池的时候,有两个问题,第一个是里面的 run方法没有办法抛出异常,另一个问题是它没有返回值,只能是开线程去执行run()里面的动作。

  下面学习另一个方法就是Callable接口,它的好处是可以有返回值,写在泛型里面,然后也可以抛出异常。方法是call()。

  上图是用Callable接口实现的 线程池,在写callable方法的时候注意返回值,这里将返回值选为String。在主函数里的调用格式与Runable相似,但是如果想要得到计算的返回值,还需要用到Future去接收,然后用Future的get方法去获得计算的值。

  

简单应用:线程实现异步计算

         利用线程池同时完成两个计算,第一个线程计算1到100的值,第二个线程计算1到200的值。

         

  这里和上面的区别并不是很大,主要需要说的有两点,第一个是接口的实现类里面的call()方法并不能直接接受参数,因为他是重写的抽象方法。但是接口的构造器可以接受参数,所以public GetSumCallable(int a)就可以接受参数a然后方法call()方法里面去进行计算,最后再返回。第二个就是,结束线程池的方法就是shutdown(),另外要说的是<>里面放的是泛型,所以不可以放int 而是intger 即包装类。

 

实际应用:多线程的安全问题

  举例:在电影院买票的时候,假定有100张电影票,有手机app购买,网页购买和线下购买等方式。这就相当于三个线程去操作同一个数据,当线程同时执行的时候,数据必须实现共享,否则就会出现安全问题。实际情况是,数据即使是共享的,也可能会出现问题。

       

   以上就是异常的实例,我们看到最后的结果出现了-1,就是说多售出了一张票。为什么会出现这种情况呢 ?因为当线程Thread-0进入if语句 的时候,你不能保证它完全执行完ticket--之后再让Thread-1进入if语句。所以,当我们让线程睡眠1秒,就很容易出现两个线程同时操作一个相同数据的情况。故,我们需要想办法解决,而解决的办法其实很容易想到,是不是可以把需要操作共享数据的那一块代码变成单行道,就ok了呢?

  JAVA就提供了这一技术,叫做同步代码块,或者同步方法。具体的原理和操作和人们上厕所排队一样,可以很多人一起等着上厕所,但是进去一个人上厕所之后,它会关上厕所的门,只有它上完厕所,才会打开门,然后另一个人再去上厕所。具体代码如下:

 

   

   使用的关键字叫做synchronized,熟悉六级的小伙伴应该背过类似的单词,意思是同步的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

---恢复内容结束---

---恢复内容结束---

posted @ 2019-02-01 21:47  AbidingAi  阅读(139)  评论(1编辑  收藏  举报