Java 7之多线程第7篇 - 线程锁基础

来源:互联网 发布:淘宝手表拍卖真假 编辑:程序博客网 时间:2024/04/29 21:57

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

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

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

(1)锁只有一种类型

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

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

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

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

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

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

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

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

public class Counter{private Lock lock = new ReentrantLock();private int count = 0;public int inc(){lock.lock();int newCount = ++count;lock.unlock();return newCount;}}

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

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



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


1、Lock接口


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

public interface Lock {    void lock();        // 获取锁    void lockInterruptibly() throws InterruptedException; // 如果当前线程没有被打断,则获取锁    boolean tryLock(); // 如果在调用的时候能锁没有被其它线程持有就获取这个锁    boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 在给定的时间内获取到锁    void unlock();     // 释放锁    Condition newCondition(); // 得到Condition实例}


2、AbstractOwnableSynchronizer接口


public abstract class AbstractOwnableSynchronizer   implements java.io.Serializable {    protected AbstractOwnableSynchronizer() { }        private transient Thread exclusiveOwnerThread;// 独占锁的持有线程        // 提供锁的setXxx()和getXxx()方法    protected final void setExclusiveOwnerThread(Thread t) {        exclusiveOwnerThread = t;    }    protected final Thread getExclusiveOwnerThread() {        return exclusiveOwnerThread;    }}

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


1>. AQS -- 指AbstractQueuedSynchronizer类。

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

2>. 队列

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

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

    static final class Node {        static final Node SHARED = new Node();// 共享锁        static final Node EXCLUSIVE = null;   // 独占锁                // 定义waitStatus值        static final int CANCELLED =  1;  // 线程已被取消        static final int SIGNAL    = -1;  // 当前线程的后继线程需要被unpark(唤醒)                static final int CONDITION = -2;  // 处在Condition休眠状态的线程在等待Condition唤醒        static final int PROPAGATE = -3;  // 其它线程获取到共享锁        /**         * Status field, taking on only the values:         *   SIGNAL:     The successor of this node is (or will soon be)         *               blocked (via park), so the current node must         *               unpark its successor when it releases or         *               cancels. To avoid races, acquire methods must         *               first indicate they need a signal,         *               then retry the atomic acquire, and then,         *               on failure, block.         *   CANCELLED:  This node is cancelled due to timeout or interrupt.         *               Nodes never leave this state. In particular,         *               a thread with cancelled node never again blocks.         *   CONDITION:  This node is currently on a condition queue.         *               It will not be used as a sync queue node         *               until transferred, at which time the status         *               will be set to 0. (Use of this value here has         *               nothing to do with the other uses of the         *               field, but simplifies mechanics.)         *   PROPAGATE:  A releaseShared should be propagated to other         *               nodes. This is set (for head node only) in         *               doReleaseShared to ensure propagation         *               continues, even if other operations have         *               since intervened.         *   0:          None of the above         *         * The values are arranged numerically to simplify use.         * Non-negative values mean that a node doesn't need to         * signal. So, most code doesn't need to check for particular         * values, just for sign.         *         * The field is initialized to 0 for normal sync nodes, and         * CONDITION for condition nodes.  It is modified using CAS         * (or when possible, unconditional volatile writes).         */        volatile int waitStatus;        volatile Node prev;        volatile Node next;        volatile Thread thread; // 节点对应的当前线程        Node nextWaiter;                // 共享锁则返回true,独占锁则返回false。        final boolean isShared() {            return nextWaiter == SHARED;        }        // 如果有的话,返回前一个节点        //predecessor引用的作用是为了支持锁等待超时(timeout)和锁等待回退(cancellation)的功能        final Node predecessor() throws NullPointerException {            Node p = prev;            if (p == null)                throw new NullPointerException();            else                return p;        }        // --------几个构造函数----------------------------------        Node() {  }          Node(Thread thread, Node mode) {     // Used by addWaiter            this.nextWaiter = mode;            this.thread = thread;        }        Node(Thread thread, int waitStatus) { // Used by Condition            this.waitStatus = waitStatus;            this.thread = thread;        }    }// end Node        /* CLH是一个非阻塞的 FIFO 队列。也就是说往里面插入或移除一个节点的时候,在并发条件下不会阻塞,而是通过自旋锁和     * CAS 保证节点插入和移除的原子性     */    private transient volatile Node head;     private transient volatile Node tail;
 举一个操作,向队列末尾插入元素时的操作如下:

// 向队列中插入Node节点    private Node enq(final Node node) {        for (;;) {            Node t = tail;            if (t == null) { // Must initialize                if (compareAndSetHead(new Node()))                    tail = head;            } else {                node.prev = t;                if (compareAndSetTail(t, node)) {                    t.next = node;                    return t;                }            }        }    }


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

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








































































0 0
原创粉丝点击