JDK并发包-(上).md

一.并发工具概述分为以下两个方面:
a.体系结构
b各个组成部分与作用
传统的多线程并没有提供高级特性,例如:信号量,线程池和执行管理器等,而这些特性恰恰有助于创建更加强大的并发程序,新的Fork/Join框架针对当前的多核系统,也提供了并行编程的可行性;
并发的的体系结构:
(1)并发工具处于java.until.concurrent包;
(2)同步器:为每种特定的同步问题提供了解决方案;
(3)执行器:用来管理线程的执行;
(4)并发集合:提供了集合框架中的集合的并行发行版本;
(5)Fork/Join框架:提供了对并行编程的支持;
(6)atmoic包:提供了不需要锁即可完成并发环境变量使用的原子性操作;
(7)locks包:使用Lock接口为并发编程提供了同步的另外一种替代方案;
二.同步控制的工具
2.1synchronized的功能扩展:重入锁
公平锁:不会产生饥饿现象;只要你排队,最终会得到资源;
synchronized关键字进行锁控制,那么产生的锁就是非公平的
重入锁对其进行公平性设置
   
   
public ReentrantLock(boolean fair)
当参数为fair时,表示锁是公平的,公平锁看起来相当优美,但是要实现公平锁则必然要求系统维护一个有序队列,这就使得实现公平锁的成本相对提高,性能相对非常低下,因此,在默认情况下,锁是非公平的;
ReentrantLock的几个重要方法整理如下:
lock():获得锁,如果锁已经被占用,则等待;
lockInterruptibly():获得锁。但优先响应中断;
trylock():尝试获得锁,如果成功,返回true,失败返回false。该方法不等待,立即返回;
tryLock(long time ,TimeUnit unit):在给定时间内尝试获得锁;
unlock():释放锁;
   
   
/*
重入锁顾名思义是这种锁是可以反复进入的,这里的反复指的是仅仅在一个线程当中;
一个线程连续两次获得同一把锁,这是允许的;
如果一个程序多次获得锁,在释放锁的时候,也必须释放同样的次数;若释放的锁次数多了,会产生异常,反之,释放所得次数少了,那么相当于线程还持有这个锁,因此,其他线程也无法进入临界区;
*/
public class ReentrantLock implements Runnable{//重入锁实现Runnable接口;
public static ReenterLock lock = new ReenterLock();//创建一个重入锁的对象
public static int i = 0;
public void run(){//复写接口中的run()方法;
for(int j=0;j<10000000;j++){
lock.lock();//使用重入锁保护临界资源i,确保多线程对i操作的安全性;
try{
i++;
}
finally{
lock.unlock();//在线程退出时,必须记得释放锁,否则其他线程就没有机会访问临界区;
}
}
}
void unlock() {
// TODO Auto-generated method stub
}
private void lock() {
// TODO Auto-generated method stub
}
public static void main(String[] args) throws InterruptedException{
ReenterLock t3 = new ReenterLock();
ReenterLock t4 = new ReenterLock();
Thread t5= new Thread(t3);
Thread t6= new Thread(t4);
t5.start();
t6.start();
t5.join();
t6.join();
System.out.println(i);
}
public boolean tryLock() {
// TODO Auto-generated method stub
return false;
}
}
2.2重入锁的好搭档:Condition条件
wait()和notify()是和synchronized关键字合作使用的,而Condition是与重入锁相关联的.通过lock接口(重入锁就实现了这一接口)的Condition newCondition()方法可以生成一个与当前重入锁绑定的Condition实例。利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。
   
   
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
 
public class ReenterLockCondition implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();// .通过lock接口(重入锁就实现了这一接口)的Condition newCondition()方法可以生成一个与当前重入锁绑定的Condition实例
public void run(){
try{
lock.lock();
condition.await();//使当前线程等待,同时释放当前锁;
System.out.println("Thread is going on");
}catch(InterruptedException e){
e.printStackTrace();
}finally{//利用try...catch结构来处理异常,finally{}来清理资源,释放资源;
lock.unlock();
}
}
public static void main(String args[]) throws InterruptedException{
ReenterLockCondition t11 = new ReenterLockCondition();
Thread t12 = new Thread(t11);//重入锁作为实例传入线程中;
t12.start();
Thread.sleep(2000);//通知线程t1继续执行
lock.lock();
condition.signal();//当其他线程中使用signal()或者signAll()方法时,线程会重新获得锁并继续执行
lock.unlock();
}
}
2.3ReadWriteLock读写锁
ReadWriteLock读写锁是一种读写分离锁,读写分离锁可以有效地帮助减少锁竞争,一提升系统性能。如何来理解锁机制可以提升系统性能呢?比如线程A1,A2,A3进行写操作,B1,B2,B3进行读操作,如果使用重入锁或者内部锁,则理论上说所有读之间,读与写之间,写和写之间都是串行操作。当B1进行读取时,B2,B3则需要等待锁。由于读操作并不对数据的完整性造成破坏,这种等待显然是不合理的,因此,读写锁就有了则回避了这一弊端。
在这种情况下,读写锁允许多个线程同时进行读,使得 B1,B2,B3之间真正并行。但是,考虑到数据的完整性,写写操作和读写锁间依然是需要相互等待和持有锁的。总的来说,读写锁存在约束访问。
a.读-读不互斥:读读之间不阻塞;
b.读写互斥:读阻塞写,写也会阻塞读;
c.写写互斥:写写阻塞;
   
   
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
public class ReadWriteLocckDemo {
private static Lock lock = new ReentrantLock();//创建一个私有的
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();
private int value;
public Object handleRead(Lock lock) throws InterruptedException{
try{
lock.lock();//模拟读操作
Thread.sleep(1000);//读操作的耗时越多,读写锁的优势就越明显;
return value;
}finally{
lock.unlock();
}
}
 
public void handleWrite(Lock lock,int index) throws InterruptedException{
try{
lock.lock(); //模拟写操作;
Thread.sleep(1000);
value = index;
}finally{
lock.unlock();
}
}
public static void main(String[] args){
final ReadWriteLockDemo demo = new ReadWriteLockDemo();
Runnable readRunnable = new Runnable(){
public void run(){
try{
demo.handleRead(readLock);
// demo.handleRead(lock);
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
Runnable writeRunnable = new Runnable(){
public void run(){
try{
demo.handleWrite(writeLock,new Random().nextInt());
// demo.handleWrite(lock,new Random().nextInt());
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
for(int i1=0;i1<18;i1++){
new Thread(readRunnable).start();
}
for(int i2 = 18; i2<20;i2++){
new Thread(writeRunnable).start();
}
}
}
2.4Semaphore同步器
经典的信号量通过计数器控制对共享资源的访问;
Semphore(int count):创建拥有count个许可证的信号量;
acquire()/acquire(int num):获取1/num个许可证
release()/release(int num):释放1/num个许可证
下面是信号量的一个简单应用:
    
    
import java.util.concurrent.Semaphore;
 
public class Person extends Thread{
private Semaphore semaphore;
public Person(Semaphore semaphore, String name){
this.semaphore= semaphore;
setName(name);
}
public void run(){
System.out.println(getName() +"is waiting...");
try{
semaphore.acquire(1);
System.out.println(getName() +"is servicing...");
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(getName() +"is done!");
semaphore.release();
}
}

   
   
import java.util.concurrent.Semaphore;
 
//模拟银行柜台服务的例子,两个柜台,三个需要办理业务的人员;
/*
在使用信号量的时候,首先要声明信号量的数量,其次要获得信号量的许可;最后对信号量进行释放;
*
* */
public class SemaphoreDemo {
public static void main(String[] args){
Semaphore semaphore = new Semaphore(2);/这里的变量声明代表的意思是信号量拥有多少个许可证,允许多少个线程进入此区域;
Person p1 = new Person(semaphore,"A");
p1.start();
Person p2 = new Person(semaphore,"B");
p2.start();
Person p3 = new Person(semaphore,"C");
p3.start();
}
}
2.5倒计时器:CountDownLatch
CountDownLatch是一个非常实用的多线程控制工具,此工具必须发生指定数量的事件后才可以继续运行, 实质上这个工具是用来控制线程等待的,它可以让某一个线程等待直到倒计时结束,再开始执行。
CountDownLatch(int count):count作为此构造函数所接收的参数,即必须发生count数量才可以打开锁存器;
await():等待锁存器,此方法是要求主线程等待所有10个检查任务全部完成,待10个任务完成后,主线程才能继续执行;
countDown();触发事件,此方法就是通知CountDownLatch,一个线程已经完成了任务,倒计时器可以减1了;
以下代码是CountDownLatch()的简单应用:
   
   
import java.util.concurrent.CountDownLatch;
 
public class CdlDemo {
public static void main(String[] args){
CountDownLatch countdownlatch = new CountDownLatch(3);//变量表示需要发生的线程数目;必须发生count数量后才可以打开锁存器;
new Test(countdownlatch,"A").start();
new Test(countdownlatch,"B").start();
new Test(countdownlatch,"C").start();//此时程序是无法正常输出的,因为创建了计数闩,三个运动员线程在启动过程中都调用了计数闩,因此需在程序下方添加进行计数功能的代码;
for(int i = 0;i<3;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
countdownlatch.countDown();
System.out.println(i);
if(i == 2)
System.out.println("start");
}
}
}
 
class Test extends Thread {
private CountDownLatch countDownlatch;
public Test(CountDownLatch countdownlatch,String name){
this.countDownlatch=countdownlatch;
setName(name);
}
public void run(){
try {
countDownlatch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//在线程等待通知的过程中有可能会抛出异常;
for(int i=0;i<3;i++){
System.out.println(getName() +" :"+ i);
}
}
}
2.6同步器CyclicBarrier,Exchabger,Phaser
CyclicBarrier是另外一种多线程并发控制实用工具。和CountDownLatch非常类似,它也可以实现线程间的计数等待,但它的功能比CountDownLatch更加复杂且强大。
CyclicBarrier顾名思义为循环栅栏,何谓栅栏,意思就是:通常在私人的府邸的周围围上一圈栅栏,阻止闲杂人等进入,用在线程中就是为了阻止线程继续执行,使其在栅栏处等待。Cyclic意为循环,说明计数器可以重复使用。此同步器也是要求多个线程执行完任务后,主线程才可以执行任务。
    
    
public CyclicBarrier(int parties,Runnable barrierActiion)
在上面的构造函数中,barrierAction就是当计数器一次计数完成后,系统会执行的动作,parties表示的计数总数,也就是参与的线程总数;
   
   
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/*
内部类和匿名内部类的区别:
内部类:一个类定义在另外一个类的里面,内部类可以随意访问外部类的成员函数的成员变量;
class A{
class B{
}
}
匿名内部类:首先属于一个内部类,其次它没有名字;
new 接口(){这个类没有名字,这个类是用来实现这个接口;
}
*/
 
public class Cd2Demo {
public static void main(String[] args){
CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new Runnable(){//主线程一旦通过循环栅栏,通过Runnable接口来实现;
 
@Override
public void run() {
System.out.println("Game start");
}
});
/*
在上面的代码中,利用了匿名内部类,CyclicBarrier中的参数Runnable是一个接口是不能用new Runnable()的,
但是此方法体中的代码和Runnable接口的实现类是完全一样的;
重点在于:这段代码没有名字即是匿名的,此时这个类又在Cd2Demon类的内部,所以将其称之为匿名内部类,new Runnable其实是声明了一个对象,因为Runnable是一个接口类型;
其下的代码是为了实现这个接口;对接口的抽象方法进行复写;
*/
new player(cyclicBarrier ,"A").start();
new player(cyclicBarrier ,"B").start();
new player(cyclicBarrier ,"C").start();
}
}
class player extends Thread{
private CyclicBarrier cyclicBarrier;
public player(CyclicBarrier cyclicBarrier,String name){
this.cyclicBarrier= cyclicBarrier;
setName(name);
}
public void run(){
System.out.println(getName() + "is waiting for other player");
try {
cyclicBarrier.await();//每一个线程在循环栅栏处等待;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
Exchanger交换器
    
    
import java.util.concurrent.Exchanger;
 
public class ExDemo {
public static void main(String[] args) {
Exchanger<String> ex = new Exchanger<>();
new A(ex).start();
new B(ex).start();
}
}
 
class A extends Thread{
private Exchanger<String> ex;
public A(Exchanger<String> ex){
this.ex= ex;
}
public void run(){
String str = null;
try{
String str1 = ex.exchange("Hello");
System.out.println(str1);
str1 = ex.exchange("A");
System.out.println(str1);
str1 = ex.exchange("B");
System.out.println(str1);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
 
class B extends Thread {
private Exchanger<String> ex;
public B(Exchanger<String> ex){
this.ex= ex;
}
public void run(){
String str = null;
try{
String str3 = ex.exchange("Smile");
System.out.println(str3);
str3 = ex.exchange("C");
System.out.println(str3);
str3 = ex.exchange("D");
System.out.println(str3);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
从代码运行的结果来看:先运行线程B,再运行线程A,由此可知数据是交替发生变化的;
Phaser同步器:
    
    
import java.util.concurrent.Phaser;
 
public class PhDemo {
public static void main(String[] args){
Phaser phaser = new Phaser(1);
System.out.println("statring...");
new Worker(phaser,"A").start();
new Worker(phaser,"B").start();
new Worker(phaser,"C").start();
for(int i=0;i<3;i++){
phaser.arriveAndAwaitAdvance();
System.out.println("order"+ i +"finish!");
}
phaser.arriveAndDeregister();
System.out.println("All done");
}
}
 
class Worker extends Thread{
private Phaser phaser;
public Worker(Phaser phaser2, String string) {
// TODO Auto-generated constructor stub
}
public void worker(Phaser phaser,String name){
this.phaser=phaser;
this.setName(name);
phaser.register();//将当前线程注册到phaser同步器中;
}
public void run(){
for(int i = 0;i<3;i++){
System.out.println("current order is:" + i + ":"+ getName());
if(i == 3){
phaser.arriveAndDeregister();//若三个任务完成后,则销毁线程;
}
else{
phaser.arriveAndAwaitAdvance();//若任务未执行完毕,则需要等待其他的线程;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.7线程阻塞工具类:LockSupport
LockSupport是一个非常方便实用的线程阻塞工具,它可以在线程任意位置让线程阻塞。和Thread.suspend()相比,它弥补了由于resume()在前发生,导致线程无法继续执行的情况。和Object.wait()相比,它不需要先获得某个对象的锁,也不会抛出InterruptedException异常。
关键字synchronized的作用是实现线程间的同步。它的工作是对同步的代码加锁,使得每一次,只能有一个线程进入同步块,从而保证线程间的 安全性。
 关键字synchronized可以有多种用法。
1.指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁;
2.直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁;
         3.直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁; 
join()方法的意思是等待线程结束,join的英文翻译意思是加入,因为一个线程要加入另外一个线程,那么最好的方法就是等着它一起走;
 join()方法有两个:
public final void join() throws InterruptedException表示无限等待,它会一直阻塞当前线程,直到目标线程执行完毕
         public final synchronized void join(long millis) throws InterruptedException 此方法给出了一个最大等待时间,如果超过给定的时间目标线程还在执行,当前线程也会因为“等不及了”,而继续往下执行;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值