Java并发编程:核心方法与框架(txt+pdf+epub+mobi电子书下载)


发布时间:2021-01-22 17:53:38

点击下载

作者:高洪岩

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Java并发编程:核心方法与框架

Java并发编程:核心方法与框架试读:

前言

为什么要写这本书

早在几年前笔者就曾想过整理一份与Java并发包有关的稿件。因为市面上所有的Java书籍都是以1章或2章的篇幅介绍Java并发包技术,这就导致对Java并发包的讲解并不是非常详尽,包含的知识量远远不够,并没有完整覆盖Java并发包技术的知识点。但可惜,苦于当时的时间及精力有限,一直没有如愿。

也许是注定的安排,笔者现所在单位是集技术与教育为一体的软件类企业,学员在学习完JavaSE/JavaEE之后想探索更深入的技术,比如大数据、分布式、高并发类的专题,就会立即遇到与Java并发包中API相关的问题。为了带领学员在技术层面上有更高的追求,所以我将Java并发包的技术点以教案的方式进行整理,在课堂上与同学们一起进行学习、交流,同学们反响非常强烈。至此,若干年前的心愿终于了却,同学们也很期待这样一本书能出版发行,那样他们就有真正的纸质参考资料了。若这份资料也被其他爱好Java并发的朋友们看到,并通过它学到相关知识,那就是我最大的荣幸了。

本书将给读者一个完整的视角,秉承“大道至简”的主导思想,只介绍Java并发包开发中最值得关注的内容,希望能抛砖引玉,以个人的一些想法和见解,为读者拓展出更深入、全面的思路。本书特色

本书尽量减少“啰嗦”式的文字语言,全部用Demo式案例来讲解技术点的实现,使读者看到代码及运行结果后就可以知道此项目要解决的是什么问题。类似于网络中Blog的风格,可让读者用最短的时间学会此知识点,明白此知识点如何应用,以及在使用时要避免什么。这就像“瑞士军刀”,虽短小,却锋利。本书的目的就是帮读者快速学习并解决问题。读者对象

·Java初级、中级程序员

·Java多线程开发者

·Java并发开发者

·系统架构师

·大数据开发者

·其他对多线程技术感兴趣的人员如何阅读本书

在整理本书时,笔者本着实用、易懂的学习原则整理了10个章节来介绍Java并发包相关的技术。

第1章讲解了Semaphore和Exchanger类的使用,学完本章后,能更好地控制线程间的同步性,以及线程间如何更好、更方便地传输数据。

第2章是第1章的延伸,主要讲解了CountDownLatch、CyclicBarrier类的使用及在Java并发包中对并发访问的控制。本章主要包括Semaphore、CountDownLatch和CyclicBarrier的使用,它们在使用上非常灵活,所以对于API的介绍比较详细,为读者学习控制同步打好坚实的基础。

第3章是第2章的升级,由于CountDownLatch和CyclicBarrier类都有相应的弊端,所以在JDK1.7中新增加了Phaser类来解决这些缺点。

第4章中讲解了Executor接口与ThreadPoolExecutor线程池的使用,可以说本章中的知识也是Java并发包中主要的应用技术点,线程池技术也在众多的高并发业务环境中使用。掌握线程池能更有效地提高程序运行效率,更好地统筹线程执行的相关任务。

第5章中讲解Future和Callable的使用,接口Runnable并不支持返回值,但在有些情况下真的需要返回值,所以Future就是用来解决这样的问题的。

第6章介绍Java并发包中的CompletionService的使用,该接口可以增强程序运行效率,因为可以以异步的方式获得任务执行的结果。

第7章主要介绍的是ExecutorService接口,该接口提供了若干方法来方便地执行业务,是比较常见的工具接口对象。

第8章主要介绍计划任务ScheduledExecutorService的使用,学完本章可以掌握如何将计划任务与线程池结合使用。

第9章主要介绍Fork-Join分治编程。分治编程在多核计算机中应用很广,它可以将大的任务拆分成小的任务再执行,最后再把执行的结果聚合到一起,完全利用多核CPU的优势,加快程序运行效率。

第10章主要介绍并发集合框架。Java中的集合在开发项目时占有举足轻重的地位,在Java并发包中也提供了在高并发环境中使用的Java集合工具类,读者需要着重掌握Queue接口的使用。勘误和支持

由于笔者的水平有限,加之编写时间仓促,书中难免会出现一些错误或者不准确的地方,恳请读者批评指正。笔者邮箱是279377921@qq.com,期待能够得到你们的真挚反馈,在技术之路上互勉共进。

本书的源代码可以在华章网站(www.hzbook.com)下载。致谢

感谢所在单位领导的支持与厚爱,使我在技术道路上更有信心。

感谢机械工业出版社华章公司的编辑们始终支持我的写作,是你们的鼓励和帮助引导我顺利完成全部书稿。高洪岩第1章Semaphore和Exchanger的使用

本书将介绍并发包中常见的并发类的主要API方法,掌握这些API方法所提供的功能是掌握并发包技术的主要手段,每一个类所提供的功能都是独有的,控制线程的行为也是不同的,这些都要依赖于类中的方法才可以实现。并发工具类中的方法其实并不算少,但它们之间却有着非常相似的功能,所以在学习上可以增加效率,理解起来并不是非常复杂。

作为本书的第1章,我将和大家一起交流一下类Semaphore和Exchanger的使用及其有关API,类Semaphore所提供的功能完全就是synchronized关键字的升级版,但它提供的功能更加的强大与方便,主要的作用就是控制线程并发的数量,而这一点,单纯地使用synchronized是做不到的。

在本章将介绍Semaphore类中的常用API,方法列表如图1-1所示。图1-1 类Semaphore中的API

类Exchanger的主要作用可以使2个线程之间互相方便地进行通信,它的常用API如图1-2所示。图1-2 类Exchanger中的API1.1Semaphore的使用

本章将对Semaphore类中的全部方法进行案例式的实验,这样可以全面地了解此类提供了哪些核心功能。

单词Semaphore的中文含义是信号、信号系统。此类的主要作用就是限制线程并发的数量,如果不限制线程并发的数量,则CPU的资源很快就被耗尽,每个线程执行的任务是相当缓慢,因为CPU要把时间片分配给不同的线程对象,而且上下文切换也要耗时,最终造成系统运行效率大幅降低,所以限制并发线程的数量还是非常有必要的。

在生活中也存在这种场景,比如一个生产键盘的生产商,发布了10个代理销售许可,所以最多只有10个代理商来获得其中的一个许可,这样就限制了代理商的数量,同理也限制了线程并发数的数量,这就是Semaphore类要达到的目的。

Semaphore类发放许可的计算方式是“减法”操作。1.1.1 类Semaphore的同步性

多线程中的同步概念其实就是排着队去执行一个任务,执行任务是一个一个执行,并不能并行执行,这样的优点是有助于程序逻辑的正确性,不会出现非线程安全问题,保证软件系统功能上的运行稳定性。

那么本节就使用一个初步的案例来看看Semaphore类是如何实现限制线程并发数的。

创建实验用的项目SemaphoreTest1,类Service.java代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + " begin timer=" + System.currentTimeMillis()); Thread.sleep(5000); System.out.println(Thread.currentThread().getName() + " end timer=" + System.currentTimeMillis()); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } }}

类Semaphore的构造函数参数permits是许可的意思,代表同一时间内,最多允许多少个线程同时执行acquire()和release()之间的代码。

无参方法acquire()的作用是使用1个许可,是减法操作。

创建3个线程类如图1-3所示。图1-3 线程数量为3

运行类Run.java代码如下:package test;import service.Service;import extthread.ThreadA;import extthread.ThreadB;import extthread.ThreadC;public class Run {public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); ThreadC c = new ThreadC(service); c.setName("C"); a.start(); b.start(); c.start(); }}

程序运行后的效果如图1-4所示。图1-4 同步运行了

说明使用代码:private Semaphore semaphore = new Semaphore(1);来定义最多允许1个线程执行acquire()和release()之间的代码,所以打印的结果就是3个线程是同步的。1.1.2 类Semaphore构造方法permits参数作用

参数permits的作用是设置许可的个数,前面已经使用过代码:private Semaphore semaphore = new Semaphore(1);

来进行程序的设计,使同一时间内最多只有1个线程可以执行acquire()和release()之间的代码,因为只有1个许可。

其实还可以传入>1的许可,代表同一时间内,最多允许有x个线程可以执行acquire()和release()之间的代码。

创建实验用的项目SemaphoreTest2,将SemaphoreTest1项目中的所有源代码复制到SemaphoreTest2中。

并更改类Service.java代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(2);public void testMethod() { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + " begin timer=" + System.currentTimeMillis()); Thread.sleep(5000); System.out.println(Thread.currentThread().getName() + " end timer=" + System.currentTimeMillis()); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } }}

使用代码new Semaphore(2)来实例化Semaphore类的含义是同一时间内最多允许2个线程执行acquire()和release()之间的代码。

创建3个线程类如图1-5所示。图1-5 3个线程类代码

程序运行后的效果如图1-6所示。图1-6 线程AB共同进入C被排斥

图1-6中打印的结果说明同一时间只有2个线程可以同时执行acquire()和release()之间的代码。

另外需要说明一下,对Semaphore类的构造方法传递的参数permits值如果大于1时,该类并不能保证线程安全性,因为还是有可能会出现多个线程共同访问实例变量,导致出现脏数据的情况。1.1.3 方法acquire(int permits)参数作用及动态添加permits许可数量

有参方法acquire(int permits)的功能是每调用1次此方法,就使用x个许可。

创建Java项目,名称为Semaphore_acquire(int permits)_release(int permits),创建类Service.java代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(10);public void testMethod() {try { semaphore.acquire(2); System.out.println(Thread.currentThread().getName() + " begin timer=" + System.currentTimeMillis());int sleepValue = ((int) (Math.random() * 10000)); System.out.println(Thread.currentThread().getName() + " 停止了" + (sleepValue / 1000) + "秒"); Thread.sleep(sleepValue); System.out.println(Thread.currentThread().getName() + " end timer=" + System.currentTimeMillis()); semaphore.release(2); } catch (InterruptedException e) { e.printStackTrace(); } }}

线程类ThreadA.java代码如下:package extthread;import service.Service;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service = service; } @Overridepublic void run() { service.testMethod(); }}

类Run.java代码如下:package test;import service.Service;import extthread.ThreadA;public class Run {public static void main(String[] args) throws InterruptedException { Service service = new Service(); ThreadA[] a = new ThreadA[10];for (int i = 0; i < 10; i++) { a[i] = new ThreadA(service); a[i].start(); } }}

程序运行效果如图1-7所示。图1-7 运行结果

在代码中一共有10个许可,每次执行semaphore.acquire(2);代码时耗费掉2个,所以10/2=5,说明同一时间只有5个线程允许执行acquire()和release()之间的代码。

如果多次调用Semaphore类的release()或release(int)方法时,还可以动态增加permits的个数。

创建测试用的项目addPermitsCount,类Run.java代码如下:package test;import java.util.concurrent.Semaphore;public class Run {public static void main(String[] args) {try { Semaphore semaphore = new Semaphore(5); semaphore.acquire(); semaphore.acquire(); semaphore.acquire(); semaphore.acquire(); semaphore.acquire(); System.out.println(semaphore.availablePermits()); semaphore.release(); semaphore.release(); semaphore.release(); semaphore.release(); semaphore.release(); semaphore.release(); System.out.println(semaphore.availablePermits()); semaphore.release(4); System.out.println(semaphore.availablePermits()); } catch (InterruptedException e) { e.printStackTrace(); } }}

程序运行后的效果如图1-8所示。图1-8 成功动态添加许可

此实验说明构造参数new Semaphore(5);中的5并不是最终的许可数量,仅仅是初始的状态值。1.1.4 方法acquireUninterruptibly()的使用

方法acquireUninterruptibly()的作用是使等待进入acquire()方法的线程,不允许被中断。

先来看一个能中断的实验。

创建项目Semaphore_acquireUninterruptibly_1,类Service.java代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + " begin timer=" + System.currentTimeMillis());for (int i = 0; i < Integer.MAX_VALUE / 50; i++) { String newString = new String(); Math.random(); } System.out.println(Thread.currentThread().getName() + " end timer=" + System.currentTimeMillis()); semaphore.release(); } catch (InterruptedException e) { System.out.println("线程" + Thread.currentThread().getName() + "进入了catch"); e.printStackTrace(); } }}

线程类代码如图1-9所示。

运行类Run.java代码如下:package test;import service.Service;import extthread.ThreadA;import extthread.ThreadB;public class Run {public static void main(String[] args) throws InterruptedException { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); a.start(); ThreadB b = new ThreadB(service); b.setName("B"); b.start(); Thread.sleep(1000); b.interrupt(); System.out.println("main中断了a"); }}图1-9 线程类代码

程序运行的效果如图1-10所示。图1-10 线程B被中断

线程B成功被中断。

那么不能被中断是什么效果呢?

创建项目Semaphore_acquireUninterruptibly_2,将Semaphore_acquireUninterruptibly_1项目中的所有源代码复制到Semaphore_acquireUninterruptibly_2中,更改Service.java文件代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() { semaphore.acquireUninterruptibly(); System.out.println(Thread.currentThread().getName() + " begin timer=" + System.currentTimeMillis());for (int i = 0; i < Integer.MAX_VALUE / 50; i++) { String newString = new String(); Math.random(); } System.out.println(Thread.currentThread().getName() + " end timer=" + System.currentTimeMillis()); semaphore.release(); }}

程序运行后的效果如图1-11所示。图1-11 线程B没有被中断

acquireUninterruptibly()方法还有重载的写法acquire-Uninterruptibly(int permits),此方法的作用是在等待许可的情况下不允许中断,如果成功获得锁,则取得指定的permits许可个数。1.1.5 方法availablePermits()和drainPermits()

availablePermits()返回此Semaphore对象中当前可用的许可数,此方法通常用于调试,因为许可的数量有可能实时在改变,并不是固定的数量。

drainPermits()可获取并返回立即可用的所有许可个数,并且将可用许可置0。

创建测试用的项目availablePermitsTest,类MyService.java代码如下:package myservice;import java.util.concurrent.Semaphore;public class MyService {private Semaphore semaphore = new Semaphore(10);public void testMethod() {try { semaphore.acquire(); System.out.println(semaphore.availablePermits()); System.out.println(semaphore.availablePermits()); System.out.println(semaphore.availablePermits()); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }}

类Run.java代码如下:package test.run;import myservice.MyService;public class Run {public static void main(String[] args) { MyService service = new MyService(); service.testMethod(); }}

程序运行结果如图1-12所示。图1-12 有效的许可数量

创建测试用的项目drainPermitsTest,将项目availablePermitsTest中的所有源代码复制到drainPermitsTest项目中,更改类MyService.java代码如下:package myservice;import java.util.concurrent.Semaphore;public class MyService {private Semaphore semaphore = new Semaphore(10);public void testMethod() {try { semaphore.acquire(); System.out.println(semaphore.availablePermits()); System.out.println(semaphore.drainPermits() + " " + semaphore.availablePermits()); System.out.println(semaphore.drainPermits() + " " + semaphore.availablePermits()); System.out.println(semaphore.drainPermits() + " " + semaphore.availablePermits()); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }}

程序运行后的效果如图1-13所示。图1-13 许可被置0

执行方法drainPermits()返回可用的许可个数,并将可用许可数清零。1.1.6 方法getQueueLength()和hasQueuedThreads()

方法getQueueLength()的作用是取得等待许可的线程个数。

方法hasQueuedThreads()的作用是判断有没有线程在等待这个许可。

这两个方法通常都是在判断当前有没有等待许可的线程信息时使用。

创建测试用的项目twoMethodTest,类MyService.java代码如下:package myservice;import java.util.concurrent.Semaphore;public class MyService {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try { semaphore.acquire(); Thread.sleep(1000); System.out.println("还有大约" + semaphore.getQueueLength() + "个线程在等待"); System.out.println("是否有线程正在等待信号量呢?" + semaphore. hasQueuedThreads()); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }}

类MyThread.java代码如下:package extthread;import myservice.MyService;public class MyThread extends Thread {private MyService myService;public MyThread(MyService myService) {super();this.myService = myService; } @Overridepublic void run() { myService.testMethod(); }}

类Run.java代码如下:package test.run;import myservice.MyService;import extthread.MyThread;public class Run {public static void main(String[] args) { MyService service = new MyService(); MyThread firstThread = new MyThread(service); firstThread.start(); MyThread[] threadArray = new MyThread[4];for (int i = 0; i < 4; i++) { threadArray[i] = new MyThread(service); threadArray[i].start(); } }}

程序运行后的效果如图1-14所示。图1-14 运行结果

线程的个数呈递减的状态。1.1.7 公平与非公平信号量的测试

有些时候,获得许可的顺序与线程启动的顺序有关,这时信号量就要分为公平与非公平的。

所谓的公平信号量是获得锁的顺序与线程启动的顺序有关,但不代表100%地获得信号量,仅仅是在概率上能得到保证。而非公平信号量就是无关的了。

创建测试用的项目semaphoreFairTest,类MyService.java代码如下:package myservice;import java.util.concurrent.Semaphore;public class MyService {private boolean isFair = false;private Semaphore semaphore = new Semaphore(1, isFair);public void testMethod() {try { semaphore.acquire(); System.out .println("ThreadName=" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }}

类MyThread.java代码如下:package extthread;import myservice.MyService;public class MyThread extends Thread {private MyService myService;public MyThread(MyService myService) {super();this.myService = myService; } @Overridepublic void run() { System.out.println("ThreadName=" + this.getName() + "启动了!"); myService.testMethod(); }}

类Run.java代码如下:package test.run;import myservice.MyService;import extthread.MyThread;public class Run {public static void main(String[] args) { MyService service = new MyService(); MyThread firstThread = new MyThread(service); firstThread.start(); MyThread[] threadArray = new MyThread[4];for (int i = 0; i < 4; i++) { threadArray[i] = new MyThread(service); threadArray[i].start(); } }}

程序运行后的效果如图1-15所示。

非公平信号量运行的效果是线程启动的顺序与调用semaphore.acquire()的顺序无关,也就是线程先启动了并不代表先获得许可。

更改MyService.java类代码如下:private boolean isFair = true;

程序运行结果如图1-16所示。图1-15 乱序打印图1-16 有序打印

公平信号量运行的效果是线程启动的顺序与调用semaphore.acquire()的顺序有关,也就是先启动的线程优先获得许可。1.1.8 方法tryAcquire()的使用

无参方法tryAcquire()的作用是尝试地获得1个许可,如果获取不到则返回false,此方法通常与if语句结合使用,其具有无阻塞的特点。无阻塞的特点可以使线程不至于在同步处一直持续等待的状态,如果if语句判断不成立则线程会继续走else语句,程序会继续向下运行。

创建Java项目Semaphore_tryAcquire_1,类代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {if (semaphore.tryAcquire()) { System.out.println("ThreadName=" + Thread.currentThread().getName() + "首选进入!");for (int i = 0; i < Integer.MAX_VALUE; i++) { String newString = new String(); Math.random(); } semaphore.release(); } else { System.out.println("ThreadName=" + Thread.currentThread().getName() + "未成功进入!"); } }}

两个线程类如图1-17所示。图1-17 线程类代码

运行类Run.java代码如下:package test.run;import service.Service;import extthread.ThreadA;import extthread.ThreadB;public class Run {public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); a.start(); ThreadB b = new ThreadB(service); b.setName("B"); b.start(); }}

程序运行后的效果如图1-18所示。图1-18 线程B未获得许可1.1.9 方法tryAcquire(int permits)的使用

有参方法tryAcquire(int permits)的作用是尝试地获得x个许可,如果获取不到则返回false。下面的项目就验证这个结论。

创建Java项目Semaphore_tryAcquire_2,将项目Semaphore_tryAcquire_1中的源代码复制到Semaphore_tryAcquire_2中,更改类代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(3);public void testMethod() {if (semaphore.tryAcquire(3)) { System.out.println("ThreadName=" + Thread.currentThread().getName() + "首选进入!");for (int i = 0; i < Integer.MAX_VALUE; i++) { String newString = new String(); Math.random(); } // 方法release对应的permits值也要更改 semaphore.release(3); } else { System.out.println("ThreadName=" + Thread.currentThread().getName() + "未成功进入!"); } }}

程序运行后的效果如图1-19所示。图1-19 线程B未获得许可1.1.10 方法tryAcquire(long timeout,TimeUnit unit)的使用

有参方法tryAcquire(int long timeout,TimeUnit unit)的作用是在指定的时间内尝试地获得1个许可,如果获取不到则返回false。

创建Java项目Semaphore_tryAcquire_3,将项目Semaphore_tryAcquire_2中的源代码复制到Semaphore_tryAcquire_3中,更改类代码如下:package service;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class Service {private Semaphore semaphore = new Semaphore(1);public void testMethod() {try {if (semaphore.tryAcquire(3, TimeUnit.SECONDS)) { System.out.println("ThreadName=" + Thread.currentThread().getName() + "首选进入!");for (int i = 0; i < Integer.MAX_VALUE; i++) { String newString = new String(); Math.random(); } semaphore.release(); } else { System.out.println("ThreadName=" + Thread.currentThread().getName() + "未成功进入!"); } // 方法release对应的permits值也要更改 } catch (InterruptedException e) { e.printStackTrace(); } }}

程序运行后的效果如图1-20所示。

更改Service.java类代码如下:for (int i = 0; i < Integer.MAX_VALUE; i++) { // String newString = new String(); // Math.random(); }

程序运行结果如图1-21所示。图1-20 线程B未获得许可图1-21 双双获得许可1.1.11 方法tryAcquire(int permits,long timeout,TimeUnit unit)的使用

有参方法tryAcquire(int permits,long timeout,TimeUnit unit)的作用是在指定的时间内尝试地获得x个许可,如果获取不到则返回false。

创建Java项目Semaphore_tryAcquire_4,将项目Semaphore_tryAcquire_3中的源代码复制到Semaphore_tryAcquire_4中,更改类Service.java代码如下:package service;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class Service {private Semaphore semaphore = new Semaphore(3);public void testMethod() { // 1改成3try {if (semaphore.tryAcquire(3, 3, TimeUnit.SECONDS)) { System.out.println("ThreadName=" + Thread.currentThread().getName() + "首选进入!");for (int i = 0; i < Integer.MAX_VALUE; i++) { String newString = new String(); Math.random(); } // 方法release对应的permits值也要更改 semaphore.release(3); } else { System.out.println("ThreadName=" + Thread.currentThread().getName() + "未成功进入!"); } } catch (InterruptedException e) { e.printStackTrace(); } }}

程序运行后的效果如图1-22所示。

更改Service.java类代码如下:for (int i = 0; i < Integer.MAX_VALUE; i++) {}

程序运行结果如图1-23所示。图1-22 线程B未获得许可图1-23 都获得许可1.1.12 多进路-多处理-多出路实验

本实现的目标是允许多个线程同时处理任务,更具体来讲,也就是每个线程都在处理自己的任务。

创建实验用的项目Semaphore_MoreToOne_1,类Service.java代码如下:package service;import java.util.concurrent.Semaphore;public class Service {private Semaphore semaphore = new Semaphore(3);public void sayHello() {try { semaphore.acquire(); System.out.println("ThreadName=" + Thread.currentThread().getName() + "准备"); System.out.println("begin hello " + System.currentTimeMillis());for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "打印" + (i + 1)); } System.out.println(" end hello " + System.currentTimeMillis()); semaphore.release(); System.out.println("ThreadName=" + Thread.currentThread().getName() + "结束"); } catch (InterruptedException e) { e.printStackTrace(); } }}

线程类MyThread.java代码如下:package extthread;import service.Service;public class MyThread extends Thread {private Service service;public MyThread(Service service) {super();this.service = service; } @Overridepublic void run() { service.sayHello(); }}

运行类Run.java代码如下:package test.run;import service.Service;import extthread.MyThread;public class Run {public static void main(String[] args) { Service service = new Service(); MyThread[] threadArray = new MyThread[12];for (int i = 0; i < threadArray.length; i++) { threadArray[i] = new MyThread(service); threadArray[i].start(); } }}

程序运行结果如图1-24所示。图1-24 打印循环中的内容为乱序

运行的效果是多个线程同时进入,而多个线程又几乎同时执行完毕。1.1.13 多进路-单处理-多出路实验

本实现的目标是允许多个线程同时处理任务,但执行任务的顺序却是同步的,也就是阻塞的,所以也称单处理。

创建实验用的项目Semaphore_MoreToOne_2,将Semaphore_MoreToOne_1项目中的所有源代码复制到项目Semaphore_MoreToOne_2中,更改类Service.java代码如下:package service;import java.util.concurrent.Semaphore;import java.util.concurrent.locks.ReentrantLock;public class Service {private Semaphore semaphore = new Semaphore(3);private ReentrantLock lock = new ReentrantLock();public void sayHello() {try { semaphore.acquire(); System.out.println("ThreadName=" + Thread.currentThread().getName() + "准备"); lock.lock(); System.out.println("begin hello " + System.currentTimeMillis());for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "打印" + (i + 1)); } System.out.println(" end hello " + System.currentTimeMillis()); lock.unlock(); semaphore.release(); System.out.println("ThreadName=" + Thread.currentThread().getName() + "结束"); } catch (InterruptedException e) { e.printStackTrace(); } }}

在代码中加入了ReentrantLock对象,保证了同步性。

程序运行结果如图1-25所示。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载