Java基础--并发实用工具(1)
2016-04-17 01:37
567 查看
1.简介
(声明:简介内容摘自<Java: The Complete Reference, Ninth Edition>中文版)从一开始,Java就对多线程提供了内置支持。例如,可以通过实现Runnable接口或者扩展Thread类来创建新的线程;可以通过使用synchronized关键字来获得同步支持;并且Object类库定义了wait()和notify()方法支持线程间通信。总之,这种对多线程的内置支持是Java重要的革新之一,并且仍然是Java的主要优势之一。
但是,因为Java对多线程的原始支持在概念上仍然是简单的,并不是对所有应用来说都是理想选择,特别是对于大量使用多线程的应用。例如,原始的多线程支持并没有提供一些高级特性,比如信号量、线程池以及执行管理器,而这些特性有助于创建功能强大的并发程序。
为了满足处理并发程序的需要,JDK5增加了并发实用工具,通常也被称为并发API。最初的并发实用工具提供了开发并发应用程序的许多特性,这些特性是程序员期待已久的。例如:提供了同步器(比如信号量)、线程池、执行管理器、锁、一些并发集合以及使用线程获取计算结果的流线化方式。
原始的并发API相当大,JDK7和JDK8的新增特性更是显著增加了这一API的大小。正如您所期望的,围绕并发实用工具的许多问题都很复杂。尽管如此,对于所有程序员而言,大致掌握并发API的工作原理是很重要的。即使在没有严重依赖并行处理的程序中,同步器、可调用线程以及执行器这类特性,依然可以广泛应用于各种情况。
2.同步器之Semaphore
在java.util.concurrent包中,提供了5个同步对象(器)。这里将一个个地介绍,通过它们可以比较容易地处理一些以前比较难处理的情况。同步器本身并不和任何资源相关联,也不应该和任何资源相关联,同步器应该和需要同步的线程相关联。对于需要同步的线程来说,资源只不过是操作数而已,对这些线程来说,重要的是知道能同时对资源能进行访问的线程的数量,即资源信号量所持有的许可证数量。对同步器使用都是让需要同步的线程使用同一个同步器,然后根据同步器本身的特性在不同情景下进行线程同步。Semaphore类的中文翻译为信号量。其实现了经典的信号量。信号量通过其持有的许可证的数量的多少来决定是否让线程访问该资源,信号量维护的许可证的数量为0时,禁止线程访问,大于0时,允许线程访问,并且许可证数量减一。
使用Semaphore的方式为:创建一个信号量同步器Semaphore(int num),可以指定该信号量持有的初始许可证的数量(其实指不指定都没有关系,毕竟可以在程序运行过程中以释放许可证的方式增加许可证的数量,在下面的实例代码中有体现);在对资源进行访问之前先获取许可证,可以获取一个acquire(),也可以获取多个acquier(int num),获取几个,信号量手中的许可证就要减少几个;对资源进行访问之后释放许可证,可以释放一个release(),也可以释放多个release(int
num),如果在线程终结之前没有释放的许可证就算没了(下面的有关代码有体现)。
Semaphore的简单使用方式如下:
import java.util.concurrent.Semaphore; public class SemaphoreTest { //创建控制线程访问的一个信号量 static Semaphore semaphore = new Semaphore(1); public static void main(String[] args) { //线程1用来给公共资源中的count加一 new Thread(()->{ try { semaphore.acquire(); } catch (Exception e) { e.printStackTrace(); } for(int i = 0;i<5;i++){ CommonResource.count++; System.out.println("Set: "+CommonResource.count); //通常的哈,这里睡上一会儿,给了另外一个线程运行的机会 try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } } semaphore.release(); }).start(); //线程2用来给公共资源中的count减一 new Thread(()->{ try { semaphore.acquire(); } catch (Exception e) { e.printStackTrace(); } for(int i = 0;i<5;i++){ CommonResource.count--; System.out.println("Got: "+CommonResource.count); try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } } semaphore.release(); }).start(); // 运行结果 // Set: 1 // Set: 2 // Set: 3 // Set: 4 // Set: 5 // Got: 4 // Got: 3 // Got: 2 // Got: 1 // Got: 0 //如果没有加信号量,几乎不可能出现这种情况,而加了信号量后,就肯定是这种情况咯 } } //公共资源,即两个线程(不一定是两个可以是多个)要操作的资源 class CommonResource{ static int count = 0; }以信号量的方式实现生产者-消费者案例的同步:
import java.util.concurrent.Semaphore; public class ProducerCustomerWithSemaphore { /* * 需要注意的是: * 1.信号量初始许可证的个数仅仅是初始许可证个数 * 2.实际过程中可用的许可证的个数可以一直通过释放来增加,就如本例 * 3.如果一个线程拿到了许可证,但是在终结之前没有释放该许可证,那许可证的总个数就少了一个,就如本例 */ public static void main(String[] args) { Queue3 queue3 = new Queue3(); //生产者 new Thread(()->{ while(queue3.n<6){ queue3.set(); } }).start(); //消费者 new Thread(()->{ while(queue3.n<6){ queue3.get(); } }).start(); } // 运行结果: // Set: 1 // Got: 1 // Set: 2 // Got: 2 // Set: 3 // Got: 3 // Set: 4 // Got: 4 // Set: 5 // Got: 5 // Set: 6 // Got: 6 } class Queue3{ int n; Semaphore semaphoreForCon; Semaphore semaphoreForPro; public Queue3(){ semaphoreForCon = new Semaphore(0); semaphoreForPro = new Semaphore(1); } public void get(){ //消费者 try { semaphoreForCon.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Got: "+n); semaphoreForPro.release(); } public void set(){ //生产者 try { semaphoreForPro.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } n++; System.out.println("Set: "+n); semaphoreForCon.release(); } }
相关文章推荐
- spring源码剖析(五)利用AOP实现自定义Spring注解
- 什么是jvm?
- Java开发web的几种开发模式
- eclipse字符集编码设置
- java xml转义方法以及中文字符的处理
- Java应用的优秀管理工具Maven的下载安装及配置
- 使用java排序的几种方式
- 类的设计技巧
- 20145322《Java程序设计》第2次实验报告
- 20145240《Java程序设计》第七周学习总结
- [Spring]简单Junit和Spring整合配置
- 二分查找算法
- 学习Spring(四) -- Spring的继承与依赖
- 学习Spring(五) -- Spring的配置文件引用
- java内省
- 如何写一个切换JDK版本的bat脚本
- Spring readOnly的简单用法
- 20145219 《Java程序设计》第07周学习总结
- [Spring]Annotation-based container configuration_AnotationBasis_03 --JSR 330 Standard Annotations
- 使用Nexus搭建Eclipse p2仓库镜像