Java 7之多线程第7篇

来源:互联网 发布:咫尺网络下载 编辑:程序博客网 时间:2024/04/28 08:06

线程锁是用来实现同步机制的,前面讲到过使用synchronized关键字来实现同步。

传送门 - 使用Synchronized关键字实现同步  http://blog.csdn.net/mazhimazh/article/details/16921255

使用这个关键字实现的同步块有一些缺点:

(1)锁只有一种类型

(2)线程得到锁或者阻塞

(3)不能实现很好的并发

为了解决如上的各种问题,后来又提出了一种更为复杂的锁 - 线程锁。线程锁可以在几个方面进行提升:

(1)添加不同类型的锁,如读取锁和写入锁(主要实现类为ReentrantReadWriteLock类)

(2)对锁的阻塞没有限制,即可以在一个方法中上锁,在另外一个方法中解锁。

(3)如果线程得不到锁,比如锁由另外一个线程持有,就允许该线程后退或继续执行,或者做其他事情 - 使用类中提供的tryLock()方法

(4)允许线程尝试取锁,并可以在超过等待时间后放弃。

下面来认识一下一个简单的线程锁 - Lock锁。简单的用法如下:

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. public class Counter{  
  2.     private Lock lock = new ReentrantLock();  
  3.     private int count = 0;  
  4.   
  5.     public int inc(){  
  6.         lock.lock();  
  7.         int newCount = ++count;  
  8.         lock.unlock();  
  9.         return newCount;  
  10.     }  
  11. }  

由于++count包括读取、自增、赋值操作,所以为了保证能够原子执行,使用了Lock锁。

Lock锁的功能很强大,不过他的实现复杂了不少。锁的实现源代码主要在Java.util.concurrent.locks包下,先看看锁的框架图,如下所示。



注:如上的粗箭头表示,下面类的实现依赖于上面类的实现。例如,AbstractQueueSynchronizer抽象类的实现依赖于LockSupport和Condition类。


1、Lock接口


Lock接口中定义的方法如下:

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. public interface Lock {  
  2.   
  3.     void lock();        // 获取锁  
  4.     void lockInterruptibly() throws InterruptedException; // 如果当前线程没有被打断,则获取锁  
  5.     boolean tryLock(); // 如果在调用的时候能锁没有被其它线程持有就获取这个锁  
  6.     boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 在给定的时间内获取到锁  
  7.     void unlock();     // 释放锁  
  8.     Condition newCondition(); // 得到Condition实例  
  9. }  


2、AbstractOwnableSynchronizer接口


[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. public abstract class AbstractOwnableSynchronizer   implements java.io.Serializable {  
  2.     protected AbstractOwnableSynchronizer() { }  
  3.       
  4.     private transient Thread exclusiveOwnerThread;// 独占锁的持有线程  
  5.       
  6.     // 提供锁的setXxx()和getXxx()方法  
  7.     protected final void setExclusiveOwnerThread(Thread t) {  
  8.         exclusiveOwnerThread = t;  
  9.     }  
  10.     protected final Thread getExclusiveOwnerThread() {  
  11.         return exclusiveOwnerThread;  
  12.     }  
  13. }  

3、下面来介绍一下几个需要重点理解的概念


1>. AQS -- 指AbstractQueuedSynchronizer类。

    AQS是java中管理“锁”的抽象类,锁的许多公共方法都是在这个类中实现。AQS是独占锁(例如ReentrantLock)和共享锁(例如Semaphore)的公共父类。

2>. 队列

    队列是AQS中“等待锁”的线程队列。在多线程中,为了保护竞争资源不被多个线程同时操作而起来错误,我们常常需要通过锁来保护这些资源。在独占锁中,竞争资源在一个时间点只能被一个线程锁访问;而其它线程则需要等待。队列就是管理这些“等待锁”的线程的队列。
    队列是一个非阻塞的 FIFO 队列。也就是说往里面插入或移除一个节点的时候,在并发条件下不会阻塞,而是通过自旋锁和 CAS 保证节点插入和移除的原子性。

    来看一下队列的Node节点源代码,如下:    

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static final class Node {  
  2.     static final Node SHARED = new Node();// 共享锁  
  3.     static final Node EXCLUSIVE = null;   // 独占锁  
  4.       
  5.     // 定义waitStatus值  
  6.     static final int CANCELLED =  1;  // 线程已被取消  
  7.     static final int SIGNAL    = -1;  // 当前线程的后继线程需要被unpark(唤醒)  
  8.       
  9.     static final int CONDITION = -2;  // 处在Condition休眠状态的线程在等待Condition唤醒  
  10.     static final int PROPAGATE = -3;  // 其它线程获取到共享锁  
  11.   
  12.     /** 
  13.      * Status field, taking on only the values: 
  14.      *   SIGNAL:     The successor of this node is (or will soon be) 
  15.      *               blocked (via park), so the current node must 
  16.      *               unpark its successor when it releases or 
  17.      *               cancels. To avoid races, acquire methods must 
  18.      *               first indicate they need a signal, 
  19.      *               then retry the atomic acquire, and then, 
  20.      *               on failure, block. 
  21.      *   CANCELLED:  This node is cancelled due to timeout or interrupt. 
  22.      *               Nodes never leave this state. In particular, 
  23.      *               a thread with cancelled node never again blocks. 
  24.      *   CONDITION:  This node is currently on a condition queue. 
  25.      *               It will not be used as a sync queue node 
  26.      *               until transferred, at which time the status 
  27.      *               will be set to 0. (Use of this value here has 
  28.      *               nothing to do with the other uses of the 
  29.      *               field, but simplifies mechanics.) 
  30.      *   PROPAGATE:  A releaseShared should be propagated to other 
  31.      *               nodes. This is set (for head node only) in 
  32.      *               doReleaseShared to ensure propagation 
  33.      *               continues, even if other operations have 
  34.      *               since intervened. 
  35.      *   0:          None of the above 
  36.      * 
  37.      * The values are arranged numerically to simplify use. 
  38.      * Non-negative values mean that a node doesn't need to 
  39.      * signal. So, most code doesn't need to check for particular 
  40.      * values, just for sign. 
  41.      * 
  42.      * The field is initialized to 0 for normal sync nodes, and 
  43.      * CONDITION for condition nodes.  It is modified using CAS 
  44.      * (or when possible, unconditional volatile writes). 
  45.      */  
  46.     volatile int waitStatus;  
  47.   
  48.     volatile Node prev;  
  49.     volatile Node next;  
  50.     volatile Thread thread; // 节点对应的当前线程  
  51.     Node nextWaiter;  
  52.       
  53.     // 共享锁则返回true,独占锁则返回false。  
  54.     final boolean isShared() {  
  55.         return nextWaiter == SHARED;  
  56.     }  
  57.     // 如果有的话,返回前一个节点  
  58.     //predecessor引用的作用是为了支持锁等待超时(timeout)和锁等待回退(cancellation)的功能  
  59.     final Node predecessor() throws NullPointerException {  
  60.         Node p = prev;  
  61.         if (p == null)  
  62.             throw new NullPointerException();  
  63.         else  
  64.             return p;  
  65.     }  
  66.     // --------几个构造函数----------------------------------  
  67.     Node() {  }    
  68.     Node(Thread thread, Node mode) {     // Used by addWaiter  
  69.         this.nextWaiter = mode;  
  70.         this.thread = thread;  
  71.     }  
  72.     Node(Thread thread, int waitStatus) { // Used by Condition  
  73.         this.waitStatus = waitStatus;  
  74.         this.thread = thread;  
  75.     }  
  76. }// end Node  
  77.   
  78. /* CLH是一个非阻塞的 FIFO 队列。也就是说往里面插入或移除一个节点的时候,在并发条件下不会阻塞,而是通过自旋锁和 
  79.  * CAS 保证节点插入和移除的原子性 
  80.  */  
  81. private transient volatile Node head;   
  82. private transient volatile Node tail;  
 举一个操作,向队列末尾插入元素时的操作如下:

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. // 向队列中插入Node节点  
  2.     private Node enq(final Node node) {  
  3.         for (;;) {  
  4.             Node t = tail;  
  5.             if (t == null) { // Must initialize  
  6.                 if (compareAndSetHead(new Node()))  
  7.                     tail = head;  
  8.             } else {  
  9.                 node.prev = t;  
  10.                 if (compareAndSetTail(t, node)) {  
  11.                     t.next = node;  
  12.                     return t;  
  13.                 }  
  14.             }  
  15.         }  
  16.     }  


3>. CAS函数 -- Compare And Swap 
     CAS函数,是比较并交换函数,它是原子操作函数;即,通过CAS操作的数据都是以原子方式进行的。例如,compareAndSetHead(), compareAndSetTail(), compareAndSetNext()等函数。它们共同的特点是,这些函数所执行的动作是以原子的方式进行的。

      如果对他们的原理不怎么理解,可以查看:传送门 - http://blog.csdn.net/mazhimazh/article/details/18908493


转载自http://blog.csdn.net/mazhimazh/

0 0
原创粉丝点击