第五章 Java中的锁

1.队列同步器( AbstractQueuedSynchronizer
     它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排 队工作
     同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法, 随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将 会调用使用者重写的方法。

2.通过AQS实现简单的独占锁 
独占锁Mutex是一个自定义同步组件,它在同一时刻只允许一个线程占有锁。 Mutex中定义了一个静态内部类,该内部类继承了同步器并实现了独占式获取和释放同步状态。在 tryAcquire(int acquires)方法中,如果经过CAS设置成功(同步状态设置为1),则代表获取了同步状 态,而在tryRelease(int releases)方法中只是将同步状态重置为0。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package  com.tlk.chapter5;
 
import  java.util.concurrent.TimeUnit;
import  java.util.concurrent.locks.AbstractQueuedSynchronizer;
import  java.util.concurrent.locks.Condition;
 
/**
  * @author tanlk
  * @date 2017年5月12日下午4:28:47
  */
public  class  Mutex {
     //静态内部类,自定义同步器
     private  static  class  Sync  extends  AbstractQueuedSynchronizer{
         private  static  final  long  serialVersionUID = 2170507079497692699L;
 
         //是否处于占用状态
         protected  boolean  isHeldExclusively() {
             return  getState() ==  1 ;
         }
         
         //当状态为0的时候获取锁
         public  boolean  tryAcquire( int  acquiers) {
             if  (compareAndSetState( 0 1 )) {
                 setExclusiveOwnerThread(Thread.currentThread());
                 return  true ;
             }
             return  false ;
         }
         
         //释放锁,将状态设置为0
         protected  boolean  tryRelease( int  arg) {
             if  (getState() ==  0
                 throw  new  IllegalMonitorStateException();
             setExclusiveOwnerThread( null );
             setState( 0 );
             return  true ;
         }
         
         Condition newCondition(){
             return  new  ConditionObject();
         }
     }
     
     private  final  Sync sync =  new  Sync();
     
     public  void  lock(){
         sync.acquire( 1 );
     }
     
     public  boolean  tryLock(){
         return  sync.tryAcquire( 1 );
     }
     
     public  void  unLock(){
         sync.release( 1 );
     }
     
     public  Condition newCondition(){
         return  sync.newCondition();
     }
     
     public  boolean  isLocked(){
         return  sync.isHeldExclusively();
     }
     
     public  boolean  hasQueuedThreads(){
         return  sync.hasQueuedThreads();
     }
     
     public  void  lockInterruptibly()  throws  InterruptedException{
         sync.acquireInterruptibly( 1 );
     }
     
     public  boolean  tryLock( long  timeout, TimeUnit timeUnit)  throws  InterruptedException{
         return  sync.tryAcquireNanos( 1 , timeUnit.toNanos(timeout));
     }
}
     独占式同步状态获取和释放:在获取同步状态时,同步器维护一 个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列(或停止自 旋)的条件是前驱节点为头节点且成功获取了同步状态。在释放同步状态时,同步器调用 tryRelease(int arg)方法释放同步状态,然后唤醒头节点的后继节点。

3.通过AQS实现简单的共享锁
   和上面的独占锁实现方式类似,只是state的范围为0-2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package  com.tlk.chapter5;
 
import  java.util.concurrent.TimeUnit;
import  java.util.concurrent.locks.AbstractQueuedSynchronizer;
import  java.util.concurrent.locks.Condition;
import  java.util.concurrent.locks.Lock;
 
public  class  TwinsLock  implements  Lock{
     
     private  final  Sync sync =  new  Sync( 2 );
     
     private  static  final  class  Sync  extends  AbstractQueuedSynchronizer{
         
         /**
         
          */
         private  static  final  long  serialVersionUID = 5161111893091476834L;
 
         Sync( int  count){
             if  (count <  0 ) {
                 throw  new  IllegalArgumentException( "count must large than zero." );
             }
             setState(count);
         }
         
         /**
          * 默认state=2,获取的时候,state减需要获取的数量,返回剩余可获取的数量,如果小于0获取失败
          * @param reduceCount
          * @return
          */
         @Override
         protected  int  tryAcquireShared( int  reduceCount) {
             for (;;){
                 int  current = getState();
                 int  newCount = current - reduceCount;
                 if  (newCount <  0  || compareAndSetState(current, newCount)) {
                     return  newCount;
                 }
             }
         }
         
         @Override
         protected  boolean  tryReleaseShared( int  returnCount) {
             for (;;){
                 int  current = getState();
                 int  newCount = current + returnCount;
                 if  (compareAndSetState(current, newCount)) {
                     return  true ;
                 }
             }
         }
     }
     
     
     
 
     @Override
     public  void  lock() {
         sync.acquireShared( 1 );
     }
     
     @Override
     public  void  unlock() {
         sync.releaseShared( 1 );
         
     }
 
     //其他接口方法略
     @Override
     public  void  lockInterruptibly()  throws  InterruptedException {
         
     }
 
     @Override
     public  boolean  tryLock() {
         return  false ;
     }
 
     @Override
     public  boolean  tryLock( long  time, TimeUnit unit)  throws  InterruptedException {
         // TODO Auto-generated method stub
         return  false ;
     }
 
     @Override
     public  Condition newCondition() {
         // TODO Auto-generated method stub
         return  null ;
     }    
     
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package  com.tlk.chapter5;
 
import  java.util.concurrent.locks.Lock;
 
import  org.junit.Test;
 
import  com.tlk.chapter4.SleepUtils;
 
/**
  * 测试TwinsLock,每次至多有两个线程获得锁
  * @author tanlk
  * @date 2017年5月15日下午5:50:35
  */
public  class  TwinsLockTest {
     @Test
     public  void  test(){
         final  Lock lock =  new  TwinsLock();
         class  Worker  extends  Thread{
             @Override
             public  void  run() {
                 lock.lock();
                 try  {
                     SleepUtils.second( 1 );
                     System.out.println(Thread.currentThread().getName());
                     SleepUtils.second( 2 );
                     lock.unlock();
                 catch  (Exception e) {
                     lock.unlock();
                 }
             }
         }
         
         //启动10个线程
         for  ( int  i =  0 ; i <  10 ; i++) {
             Worker w =  new  Worker();
             w.setDaemon( true );
             w.setName( "thread-" +i);
             w.start();
         }
         
         //每隔一秒换行
         for ( int  i= 0 ; i< 10 ; i++){
             SleepUtils.second( 1 );
             System.out.println(i+ "---------------" );
         }
     }
}

4. 重入锁ReentrantLock
     当一个线程调用代码示例Mutex的lock()方法 获取锁之后,如果再次调用lock()方法,则该线程将会被自己所阻塞。所以引入了重入锁,重入锁 成功获取锁的线程再次获取锁,只是增加了同步状态值,这也就要求ReentrantLock在释放同步
状态时减少同步状态值,同步状态值可以为0-n,但大于0时 exclusiveOwnerThread是同一个。
    ReentrantLock有公平性锁(fair)和非公平性锁(unfair), 公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成 线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量

5.读写锁 ReentrantReadWriteLock
1)读写锁,读状态写状态还是都维护在state上面,如果在一个整型变量上维护多种状态,就一定需要“按位切割使用”这个变量,读写锁将变量切 分成了两个部分,高16位表示读,低16位表示写,所以读写锁最大持有量是65535
      jdk具体源码:
       sharedCount:共享读锁数量
        exclusiveCount:互斥写锁数量
1
2
3
4
5
6
7
8
9
   static  final  int  SHARED_SHIFT   =  16 ;
   static  final  int  SHARED_UNIT    = ( 1  << SHARED_SHIFT);
   static  final  int  MAX_COUNT      = ( 1  << SHARED_SHIFT) -  1 ;
   static  final  int  EXCLUSIVE_MASK = ( 1  << SHARED_SHIFT) -  1 ;
 
   /** Returns the number of shared holds represented in count  */
   static  int  sharedCount( int  c)    {  return  c >>> SHARED_SHIFT; }
   /** Returns the number of exclusive holds represented in count  */
   static  int  exclusiveCount( int  c) {  return  c & EXCLUSIVE_MASK; }

    读写锁使用代码简单示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package  com.tlk.chapter5;
 
import  java.util.HashMap;
import  java.util.Map;
import  java.util.concurrent.locks.Lock;
import  java.util.concurrent.locks.ReentrantReadWriteLock;
 
public  class  Cache {
     static  Map<String, Object> map =  new  HashMap<String, Object>();
     static  ReentrantReadWriteLock rwl =  new  ReentrantReadWriteLock();
     static  Lock r = rwl.readLock();
     static  Lock w = rwl.writeLock();
 
     // 获取一个key对应的value
     public  static  final  Object get(String key) {
         r.lock();
         try  {
             return  map.get(key);
         finally  {
             r.unlock();
         }
     }
 
     // 设置key对应的value,并返回旧的value
     public  static  final  Object put(String key, Object value) {
         w.lock();
         try  {
             return  map.put(key, value);
         finally  {
             w.unlock();
         }
     }
 
     // 清空所有的内容
     public  static  final  void  clear() {
         w.lock();
         try  {
             map.clear();
         finally  {
             w.unlock();
         }
     }
}

6.  LockSupport工具
     当需要阻塞或唤醒一个线程的时候,都会使用LockSupport工具类来完成相应工作。 LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而 LockSupport也成为构建同步组件的基础工具。
    主要方法:
     park() 用来阻塞当前线程
    unpark(Thread thread)方法来 唤醒一个被阻塞的线

7.  Condition接口
    任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、 wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现 等待/通知模式。Condition接口也提     供了类似Object的监视器方法,与Lock配合可以实现等待/通知模 式,但是这两者在使用方式及功能特性上还是有差别的
    主要方法:
    await():当前线程进入等待状态直到被通知(signal)或中断
    signal(): 唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁

     接口实现ConditionObject
    ConditionObject是同步器AbstractQueuedSynchronizer的内部类, 主要包括:等待队列、等待和通知

Condition使用代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package  com.tlk.chapter5;
 
import  java.util.concurrent.locks.Condition;
import  java.util.concurrent.locks.Lock;
import  java.util.concurrent.locks.ReentrantLock;
 
/**
  * 有界队列,满添加await,移除signal
  * @author tanlk
  * @date 2017年5月17日下午7:16:53
  * @param <T>
  */
public  class  BoundedQueue<T> {
     
     private  Object[] items;
     //添加的下标,删除的下标和数组当前数量
     private  int  addIndex, removeIndex, count;
     
     private  Lock lock =  new  ReentrantLock();
     private  Condition notEmpty = lock.newCondition();
     private  Condition notFull = lock.newCondition();
     
     public  BoundedQueue( int  size){
         items =  new  Object[size];
     }
     
     //添加一个元素,如果数组满,则添加线程进入等待状态,直到有“空位”
     public  void  add(T t)  throws  InterruptedException{
         lock.lock();
         try {
             while (count == items.length){
                 notFull.await();
            
             items[addIndex] = t;
             if  (++addIndex == items.length) {
                 addIndex =  0 ;
             }
             ++count;
             notEmpty.signal();
         } finally {
             lock.unlock();
         }
     }
     
     //由头部删除一个元素,如果数组为空,则删除线程进入等待状态,直到有新添加元素
     @SuppressWarnings ( "unchecked" )
     public  T remove()  throws  InterruptedException{
         lock.lock();
         try  {
             while (count== 0 ){
                 notEmpty.await();
             }
             Object x = items[removeIndex];
             if  (++removeIndex == items.length) {
                 removeIndex =  0 ;
             }
             --count;
             notFull.signal();
             return  (T) x;
         finally  {
             lock.unlock();
         }
     }
 
     
     public  int  getAddIndex() {
         return  addIndex;
     }
 
     public  int  getRemoveIndex() {
         return  removeIndex;
     }
     
     public  int  getCount(){
         return  count;
     }
 
     
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package  com.tlk.chapter5;
 
import  com.tlk.chapter4.SleepUtils;
 
/**
  * 有界队列测试
  * @author tanlk
  * @date 2017年5月17日下午7:17:52
  */
public  class  BoundedQueueTest {
     static  BoundedQueue<Integer> boundedQueue =  new  BoundedQueue<Integer>( 50 );
     public  static  void  main(String[] args)  throws  InterruptedException {
         
         new  addWorker().start();
         new  removeWorker().start();
         
     }
     
     static  class  addWorker  extends  Thread{
         @Override
         public  void  run() {
             for ( int  i= 0 ; i< 1000 ; i++){
                 try  {
                     System.out.println( "addIndex:" +boundedQueue.getAddIndex());
                     System.out.println( "removeIndex:" +boundedQueue.getRemoveIndex());
                     System.out.println( "count:" +boundedQueue.getCount());
                     boundedQueue.add(i);
                 catch  (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
     
     static  class  removeWorker  extends  Thread{
         @Override
         public  void  run() {
             for ( int  i= 0 ; i< 1000 ; i++){
                 try  {
                     SleepUtils.second( 1 );
                     System.out.println(boundedQueue.remove());
                 catch  (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
             
         }
     }
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值