Java并发编程:锁

时间:2021-09-11 12:10:35





林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

一、基础知识

       在Java并发编程里头,锁是一个非常重要的概念。就如同现实生活一样,如果房子上了锁。别人就进不去。Java里头如果一段代码取得了一个锁,其它地方再想去这个锁(或者再执行这个相同的代码)就都得等待锁释放。锁其实分成非常多。比如有互斥锁、读写锁、乐观锁、悲观锁、自旋锁、公平锁、非公平锁等。包括信号量其实都可以认为是一个锁。

 1、什么时需要锁呢?

     其实非常多的场景,如共享实例变量、共享连接资源时以及包括并发包中BlockingQueue、ConcurrentHashMap等并发集合中都大量使用了锁。基体上使用同步的地方都可以改成锁来用,但是使用锁的地方不一定能改成同步来用。

2、 锁和同步的对比

1)同步synchronized算是一个关键词,是来来修饰方法的,但是锁lock是一个实例变量,通过调用lock()方法来取得锁

2)、只能同步方法,而不能同步变量和类,锁也是一样

3)、同步无法保证线程取得方法执行的先后顺序。锁可以设置公平锁来确保。
4)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程*访问而不受锁的限制。锁也是一样。
6)、线程睡眠时,它所持的任何锁都不会释放。
7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:

最后,还需要说的一点是。如果使用锁,那么一定的注意编写代码,但不很容易出现死锁!避免方法后文后讲。

 3、简单实例

在看锁的源码时,首先来看个锁的实例,从而对锁有一个简单的理解。由线程A输出1、2、3.接着线程B输出4、5、6.最后线程A再输出7、8、9

  1. package com.func.axc.reentrantlock;  
  2.   
  3. import java.util.concurrent.locks.Condition;  
  4. import java.util.concurrent.locks.Lock;  
  5. import java.util.concurrent.locks.ReentrantLock;  
  6.   
  7. /* 
  8.   功能概要: 
  9.    
  10.   @author linbingwen 
  11.   @since 2016年5月27日 
  12.  /  
  13. public class ReenTrantLockTest {  
  14.   
  15.     static class NumberWrapper {  
  16.         public int value = 1;  
  17.     }  
  18.   
  19.     public static void main(String[] args) {  
  20.         // 初始化可重入锁  
  21.         final Lock lock = new ReentrantLock();  
  22.   
  23.         // 第一个条件当屏幕上输出到3  
  24.         final Condition reachThreeCondition = lock.newCondition();  
  25.         // 第二个条件当屏幕上输出到6  
  26.         final Condition reachSixCondition = lock.newCondition();  
  27.   
  28.         // NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final  
  29.         // 注意这里不要用Integer, Integer 是不可变对象  
  30.         final NumberWrapper num = new NumberWrapper();  
  31.         // 初始化A线程  
  32.         Thread threadA = new Thread(new Runnable() {  
  33.             @Override  
  34.             public void run() {  
  35.                 // 需要先获得锁  
  36.                 lock.lock();  
  37.                 try {  
  38.                     System.out.println(”threadA start write”);  
  39.                     // A线程先输出前3个数  
  40.                     while (num.value <= 3) {  
  41.                         System.out.println(num.value);  
  42.                         num.value++;  
  43.                     }  
  44.                     // 输出到3时要signal,告诉B线程可以开始了  
  45.                     reachThreeCondition.signal();  
  46.                 } finally {  
  47.                     lock.unlock();  
  48.                 }  
  49.                 lock.lock();  
  50.                 try {  
  51.                     // 等待输出6的条件  
  52.                     reachSixCondition.await();  
  53.                     System.out.println(”threadA start write”);  
  54.                     // 输出剩余数字  
  55.                     while (num.value <= 9) {  
  56.                         System.out.println(num.value);  
  57.                         num.value++;  
  58.                     }  
  59.   
  60.                 } catch (InterruptedException e) {  
  61.                     e.printStackTrace();  
  62.                 } finally {  
  63.                     lock.unlock();  
  64.                 }  
  65.             }  
  66.   
  67.         });  
  68.   
  69.         Thread threadB = new Thread(new Runnable() {  
  70.             @Override  
  71.             public void run() {  
  72.                 try {  
  73.                     lock.lock();  
  74.   
  75.                     while (num.value <= 3) {  
  76.                         // 等待3输出完毕的信号  
  77.                         reachThreeCondition.await();  
  78.                     }  
  79.                 } catch (InterruptedException e) {  
  80.                     e.printStackTrace();  
  81.                 } finally {  
  82.                     lock.unlock();  
  83.                 }  
  84.                 try {  
  85.                     lock.lock();  
  86.                     // 已经收到信号,开始输出4,5,6  
  87.                     System.out.println(”threadB start write”);  
  88.                     while (num.value <= 6) {  
  89.                         System.out.println(num.value);  
  90.                         num.value++;  
  91.                     }  
  92.                     // 4,5,6输出完毕,告诉A线程6输出完了  
  93.                     reachSixCondition.signal();  
  94.                 } finally {  
  95.                     lock.unlock();  
  96.                 }  
  97.             }  
  98.   
  99.         });  
  100.   
  101.         // 启动两个线程  
  102.         threadB.start();  
  103.         threadA.start();  
  104.     }  
  105. }  
package com.func.axc.reentrantlock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 功能概要:
*
* @author linbingwen
* @since 2016年5月27日
*/
public class ReenTrantLockTest {

static class NumberWrapper {
    public int value = 1;
}

public static void main(String[] args) {
    // 初始化可重入锁
    final Lock lock = new ReentrantLock();

    // 第一个条件当屏幕上输出到3
    final Condition reachThreeCondition = lock.newCondition();
    // 第二个条件当屏幕上输出到6
    final Condition reachSixCondition = lock.newCondition();

    // NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final
    // 注意这里不要用Integer, Integer 是不可变对象
    final NumberWrapper num = new NumberWrapper();
    // 初始化A线程
    Thread threadA = new Thread(new Runnable() {
        @Override
        public void run() {
            // 需要先获得锁
            lock.lock();
            try {
                System.out.println("threadA start write");
                // A线程先输出前3个数
                while (num.value &lt;= 3) {
                    System.out.println(num.value);
                    num.value++;
                }
                // 输出到3时要signal,告诉B线程可以开始了
                reachThreeCondition.signal();
            } finally {
                lock.unlock();
            }
            lock.lock();
            try {
                // 等待输出6的条件
                reachSixCondition.await();
                System.out.println("threadA start write");
                // 输出剩余数字
                while (num.value &lt;= 9) {
                    System.out.println(num.value);
                    num.value++;
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    });

    Thread threadB = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                lock.lock();

                while (num.value &lt;= 3) {
                    // 等待3输出完毕的信号
                    reachThreeCondition.await();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            try {
                lock.lock();
                // 已经收到信号,开始输出4,5,6
                System.out.println("threadB start write");
                while (num.value &lt;= 6) {
                    System.out.println(num.value);
                    num.value++;
                }
                // 4,5,6输出完毕,告诉A线程6输出完了
                reachSixCondition.signal();
            } finally {
                lock.unlock();
            }
        }

    });

    // 启动两个线程
    threadB.start();
    threadA.start();
}

}

输出结果:

Java并发编程:锁



这个题目用同步的方法也做其实也可以。但是用锁可能更好一点。在上面笔者使用了锁和条件从而完成 了要求。


二、说说源码

最基础的我们先来看看lock方法

  1. package java.util.concurrent.locks;  
  2. import java.util.concurrent.TimeUnit;  
  3.   
  4. public interface Lock {  
  5.   
  6.     //取得锁,但是要注意lock()忽视interrupt(), 拿不到锁就 一直阻塞  
  7.     void lock();  
  8.   
  9.     //同样也是取得锁,但是lockInterruptibly()会响应打扰 interrupt()并catch到InterruptedException,从而跳出阻塞  
  10.     void lockInterruptibly() throws InterruptedException;  
  11.   
  12.     //尝试取得锁,成功返回true  
  13.     boolean tryLock();  
  14.   
  15.     //在规定的时间等待里,如果取得锁就返回tre  
  16.     boolean tryLock(long time, TimeUnit unit) throws InterruptedException;  
  17.   
  18.     //释放锁  
  19.     void unlock();  
  20.     //条件状态,非常有用,Blockingqueue阻塞队列就是用到它了  
  21.     Condition newCondition();  
  22. }  
package java.util.concurrent.locks; 
import java.util.concurrent.TimeUnit;

public interface Lock {

//取得锁,但是要注意lock()忽视interrupt(), 拿不到锁就 一直阻塞
void lock();

//同样也是取得锁,但是lockInterruptibly()会响应打扰 interrupt()并catch到InterruptedException,从而跳出阻塞
void lockInterruptibly() throws InterruptedException;

//尝试取得锁,成功返回true
boolean tryLock();

//在规定的时间等待里,如果取得锁就返回tre
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

//释放锁
void unlock();
//条件状态,非常有用,Blockingqueue阻塞队列就是用到它了
Condition newCondition();

}

1、接下来看看它最常见的实现类,ReentrantLock可重入锁。

  1. public class ReentrantLock implements Lock, java.io.Serializable {  
  2.     private static final long serialVersionUID = 7373984872572414699L;  
  3.     private final Sync sync; //就只有一个Sync变量,ReentrantLock的所有方法基本都是调用Sync的方法   
public class ReentrantLock implements Lock, java.io.Serializable { 
private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync; //就只有一个Sync变量,ReentrantLock的所有方法基本都是调用Sync的方法

2、构造函数

  1. public ReentrantLock() {  
  2.     sync = new NonfairSync(); //默认非公平锁  
  3. }  
  4.   
  5. public ReentrantLock(boolean fair) {  
  6.     sync = (fair)? new FairSync() : new NonfairSync();//公平锁  
  7. }  
    public ReentrantLock() { 
sync = new NonfairSync(); //默认非公平锁
}
public ReentrantLock(boolean fair) {
    sync = (fair)? new FairSync() : new NonfairSync();//公平锁
}</pre>其里的公平锁的意思是哪个线程先来等待,谁就先获得这个锁。而非公平锁则是看操作系统的调度,有不确定性。一般设置成非公平锁的性能会好很多。<br><h2></h2><p></p>3、然后看看lock方法<div class="dp-highlighter bg_java"><div class="bar"><div class="tools"><b>[java]</b> <a href="#" class="ViewSource" title="view plain" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><span data-mod="popu_168"> <a href="#" class="CopyToClipboard" title="copy" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy</a><div style="position: absolute; left: 818px; top: 4641px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_5" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_5" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=5&amp;width=16&amp;height=16" wmode="transparent"></div><div style="position: absolute; left: 818px; top: 4641px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_21" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_21" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=21&amp;width=16&amp;height=16" wmode="transparent"></div></span><span data-mod="popu_169"> <a href="#" class="PrintSource" title="print" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a></span><a href="#" class="About" title="?" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div></div><ol start="1" class="dp-j"><li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;lock()&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sync.lock();&nbsp;&nbsp;</span></li><li class="alt"><span>}&nbsp;&nbsp;</span></li></ol></div><pre class="java" name="code" style="display: none;">    public void lock() {
    sync.lock();
}</pre>还有这个<p></p><p></p><div class="dp-highlighter bg_java"><div class="bar"><div class="tools"><b>[java]</b> <a href="#" class="ViewSource" title="view plain" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><span data-mod="popu_168"> <a href="#" class="CopyToClipboard" title="copy" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy</a><div style="position: absolute; left: 818px; top: 4784px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_6" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_6" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=6&amp;width=16&amp;height=16" wmode="transparent"></div><div style="position: absolute; left: 818px; top: 4784px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_22" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_22" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=22&amp;width=16&amp;height=16" wmode="transparent"></div></span><span data-mod="popu_169"> <a href="#" class="PrintSource" title="print" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a></span><a href="#" class="About" title="?" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div></div><ol start="1" class="dp-j"><li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;lockInterruptibly()&nbsp;</span><span class="keyword">throws</span><span>&nbsp;InterruptedException&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sync.acquireInterruptibly(<span class="number">1</span><span>);&nbsp;&nbsp;</span></span></li><li class="alt"><span>}&nbsp;&nbsp;</span></li></ol></div><pre class="java" name="code" style="display: none;">    public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}</pre><br>发现都 是调用 sync这个变量的方法,它其实是一个ReentrantLock的内部类。真实起作用的其实是它,所以直接看它源码:<p></p><p>首先是非公平锁:</p><p></p><div class="dp-highlighter bg_java"><div class="bar"><div class="tools"><b>[java]</b> <a href="#" class="ViewSource" title="view plain" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><span data-mod="popu_168"> <a href="#" class="CopyToClipboard" title="copy" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy</a><div style="position: absolute; left: 818px; top: 4991px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_7" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_7" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=7&amp;width=16&amp;height=16" wmode="transparent"></div><div style="position: absolute; left: 818px; top: 4991px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_23" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_23" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=23&amp;width=16&amp;height=16" wmode="transparent"></div></span><span data-mod="popu_169"> <a href="#" class="PrintSource" title="print" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a></span><a href="#" class="About" title="?" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div></div><ol start="1" class="dp-j"><li class="alt"><span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;NonfairSync&nbsp;</span><span class="keyword">extends</span><span>&nbsp;Sync&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">long</span><span>&nbsp;serialVersionUID&nbsp;=&nbsp;7316153563782823691L;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">final</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;lock()&nbsp;{&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(compareAndSetState(</span><span class="number">0</span><span>,&nbsp;</span><span class="number">1</span><span>))&nbsp;</span><span class="comment">//0未获取,1已经获取</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setExclusiveOwnerThread(Thread.currentThread());<span class="comment">//设置独占模式,则一个锁只能被一个线程持有,其他线程必须要等待。</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acquire(<span class="number">1</span><span>);</span><span class="comment">//如果没有取得锁,尝试使用信号量的方式</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;tryAcquire(</span><span class="keyword">int</span><span>&nbsp;acquires)&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;nonfairTryAcquire(acquires);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></li></ol></div><pre class="java" name="code" style="display: none;">    final static class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        if (compareAndSetState(0, 1)) //0未获取,1已经获取
            setExclusiveOwnerThread(Thread.currentThread());//设置独占模式,则一个锁只能被一个线程持有,其他线程必须要等待。
        else
            acquire(1);//如果没有取得锁,尝试使用信号量的方式
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
  </pre>它使用到的方法如下:<p></p><p></p><div class="dp-highlighter bg_java"><div class="bar"><div class="tools"><b>[java]</b> <a href="#" class="ViewSource" title="view plain" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><span data-mod="popu_168"> <a href="#" class="CopyToClipboard" title="copy" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy</a><div style="position: absolute; left: 818px; top: 5332px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_8" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_8" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=8&amp;width=16&amp;height=16" wmode="transparent"></div><div style="position: absolute; left: 818px; top: 5332px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_24" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_24" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=24&amp;width=16&amp;height=16" wmode="transparent"></div></span><span data-mod="popu_169"> <a href="#" class="PrintSource" title="print" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a></span><a href="#" class="About" title="?" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div></div><ol start="1" class="dp-j"><li class="alt"><span><span class="comment">//设置状态</span><span>&nbsp;&nbsp;</span></span></li><li class=""><span><span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;compareAndSetState(</span><span class="keyword">int</span><span>&nbsp;expect,&nbsp;</span><span class="keyword">int</span><span>&nbsp;update)&nbsp;{&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;unsafe.compareAndSwapInt(</span><span class="keyword">this</span><span>,&nbsp;stateOffset,&nbsp;expect,&nbsp;update);&nbsp;&nbsp;</span></span></li><li class=""><span>}&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span><span class="comment">//可以看到,&nbsp;compareAndSwapInt不是用Java实现的,&nbsp;而是通过JNI调用操作系统的原生程序.注意它是原子方法(C++写的)</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>ublic&nbsp;<span class="keyword">final</span><span>&nbsp;</span><span class="keyword">native</span><span>&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;compareAndSwapInt(Object&nbsp;o,&nbsp;</span><span class="keyword">long</span><span>&nbsp;offset,</span><span class="keyword">int</span><span>&nbsp;expected,&nbsp;</span><span class="keyword">int</span><span>&nbsp;x);&nbsp;&nbsp;</span></span></li></ol></div><pre class="java" name="code" style="display: none;">    //设置状态
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

//可以看到, compareAndSwapInt不是用Java实现的, 而是通过JNI调用操作系统的原生程序.注意它是原子方法(C++写的)

public final native boolean compareAndSwapInt(Object o, long offset,int expected, int x);

最终取得锁的方法其实在java Unsafe类的compareAndSwap方法。compareAndSwap是个原子方法,原理是cas.就是说如果他是xx,那么就改为xxx. 这个是高效,而且是原子的,不用加锁. 也不用但是其他值改了而产生误操作,应为会先判断当前值,符合期望才去改变. 

4、tryLock()方法

上面是lock方法是的调用,如果是tryLock呢?

  1. public boolean tryLock() {  
  2.     return sync.nonfairTryAcquire(1);  
  3. }  
    public boolean tryLock() { 
return sync.nonfairTryAcquire(1);
}
再看sync的方法

  1. final boolean nonfairTryAcquire(int acquires) {  
  2.     final Thread current = Thread.currentThread();  
  3.     int c = getState();//取得状态  
  4.     if (c == 0) {//0表示未获取锁  
  5.         if (compareAndSetState(0, acquires)) {//CAS设置状态  
  6.             setExclusiveOwnerThread(current);//设置独占线程  
  7.             return true;  
  8.         }  
  9.     }  
  10.     else if (current == getExclusiveOwnerThread()) {//当前线程已有这个锁了  
  11.         int nextc = c + acquires;//设置重入的次数,如果是一个线程在有锁的情况下多次调用tryLock就有可能进入这个方法  
  12.         if (nextc < 0// 重入数溢出了  
  13.             throw new Error(“Maximum lock count exceeded”);  
  14.         setState(nextc);  
  15.         return true;  
  16.     }  
  17.     return false;//如果到这里就是没有取到锁了,  
  18. }  
        final boolean nonfairTryAcquire(int acquires) { 
final Thread current = Thread.currentThread();
int c = getState();//取得状态
if (c == 0) {//0表示未获取锁
if (compareAndSetState(0, acquires)) {//CAS设置状态
setExclusiveOwnerThread(current);//设置独占线程
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//当前线程已有这个锁了
int nextc = c + acquires;//设置重入的次数,如果是一个线程在有锁的情况下多次调用tryLock就有可能进入这个方法
if (nextc < 0) // 重入数溢出了
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;//如果到这里就是没有取到锁了,
}

其中getState()方法是在AbstractQueuedSynchronizer类的就方法,取得就是下面这个变量

  1. private volatile int state;  
private volatile int state;

在互斥锁中它表示着线程是否已经获取了锁,0未获取,1已经获取了,大于1表示重入数。同时AQS提供了getState()、setState()、compareAndSetState()方法来获取和修改该值:

  1. protected final int getState() {  
  2. return state;  
  3. }  
  4. protected final void setState(int newState) {  
  5. state = newState;  
  6. }  
  7. protected final boolean compareAndSetState(int expect, int update) {  
  8. return unsafe.compareAndSwapInt(this, stateOffset, expect, update);  
  9. }  
protected final int getState() { 
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
这些方法需要java.util.concurrent.atomic包的支持,采用CAS操作,保证其原则性和可见性。

5、tryLock(long timeout, TimeUnit unit)方法

  1. public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {  
  2.     return sync.tryAcquireNanos(1, unit.toNanos(timeout));  
  3. }  
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { 
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

带有超时时间等待获取锁的方法。真正调用 的其实是Sync父类AbstractQueuedSynchronizer的方法

  1. public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {  
  2. (Thread.interrupted())//检测到当前线程的中断标志为true  
  3.  throw new InterruptedException();  
  4. urn tryAcquire(arg) ||  
  5.  doAcquireNanos(arg, nanosTimeout);  
  6. }  
    public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { 
if (Thread.interrupted())//检测到当前线程的中断标志为true
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}

这里调用 了两个方法tryAcquire和doAcquireNanos,其实tryAcquire调用的方法就是Lock()调用的方法

  1. protected final boolean tryAcquire(int acquires) {  
  2.     return nonfairTryAcquire(acquires);  
  3. }  
        protected final boolean tryAcquire(int acquires) { 
return nonfairTryAcquire(acquires);
}
这样就不再说明。下面直接来看doAcquireNanos方法,它才是一直在等待循环获取锁的方法。

  1. private boolean doAcquireNanos(int arg, long nanosTimeout)  
  2.     throws InterruptedException {  
  3.     long lastTime = System.nanoTime();  
  4.     final Node node = addWaiter(Node.EXCLUSIVE);//放入等待的节点,会组成 一个链表  
  5.     try {  
  6.         for (;;) { //死循环,时间到了才会跳出  
  7.             final Node p = node.predecessor();  
  8.             if (p == head && tryAcquire(arg)) { //当前节点是头节点。然后尝试获得锁  
  9.                 setHead(node);  
  10.                 p.next = null// 把当前节点去掉  
  11.                 return true;  
  12.             }  
  13.             if (nanosTimeout <= 0) { //超出等待时间  
  14.                 cancelAcquire(node);  
  15.                 return false;  
  16.             }  
  17.             if (nanosTimeout > spinForTimeoutThreshold &&  
  18.                 shouldParkAfterFailedAcquire(p, node))  
  19.                 LockSupport.parkNanos(this, nanosTimeout);//还在等待时间内  
  20.             long now = System.nanoTime();  
  21.             nanosTimeout -= now - lastTime;  
  22.             lastTime = now;  
  23.             if (Thread.interrupted())//检测到中断信号,直接跳出  
  24.                 break;  
  25.         }  
  26.     } catch (RuntimeException ex) {  
  27.         cancelAcquire(node);  
  28.         throw ex;  
  29.     }  
  30.     cancelAcquire(node);//检测 到中断信号时才会执行到这里  
  31.     throw new InterruptedException();  
  32. }  
    private boolean doAcquireNanos(int arg, long nanosTimeout) 
throws InterruptedException {
long lastTime = System.nanoTime();
final Node node = addWaiter(Node.EXCLUSIVE);//放入等待的节点,会组成 一个链表
try {
for (;;) { //死循环,时间到了才会跳出
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { //当前节点是头节点。然后尝试获得锁
setHead(node);
p.next = null; // 把当前节点去掉
return true;
}
if (nanosTimeout <= 0) { //超出等待时间
cancelAcquire(node);
return false;
}
if (nanosTimeout > spinForTimeoutThreshold &&
shouldParkAfterFailedAcquire(p, node))
LockSupport.parkNanos(this, nanosTimeout);//还在等待时间内
long now = System.nanoTime();
nanosTimeout -= now - lastTime;
lastTime = now;
if (Thread.interrupted())//检测到中断信号,直接跳出
break;
}
} catch (RuntimeException ex) {
cancelAcquire(node);
throw ex;
}
cancelAcquire(node);//检测 到中断信号时才会执行到这里
throw new InterruptedException();
}

锁的介绍就到这里了,下文再来接着讲~