Javaのロックについてもお話しします.同時プログラミングの重要な知識点です.


Javaのロックについても
一、javaのロックとは
1,java構文上,javaにおけるロックとは,javaが提供するロックインタフェースおよび関連する実装クラスを指す.
public interface Lockでは、通常、ロックオブジェクトを作成します.
Lock lock = new ReentrantLock(); 

2、アーキテクチャから言えば、ロックは同期メカニズムである.
3機能的には、ロックは、複数のスレッドが共有リソースにアクセスすることを制御するための方法であり、複数のスレッドが共有リソースに同時にアクセスすることを防止する.
二、JVMの角度から見るロック
    ロックといえばsynchronizedキーワードです.ロックはJDK 5から導入された機能で、synchronizedは
キーワードはJDK 5以前から存在し、ロックされていない日にjavaはsynchronizedキーワードでロックを実現しています.
の機能です.ロックはsynchronizedキーワードにも似た機能で、違いはロックが表示され、synchronizedキー
字は暗黙的だ.
1、ロックのメモリの意味
    ロックは1種の同期機構で、ロックは臨界領域の反発の実行を譲ることができて、マルチスレッド環境の下で強い同期を実現して、そしてロックLは解放します
すると、ロックLを解放したAスレッドは、ロックLを取得したBスレッドに通知する.これがロックのメモリの意味です.
2,ロックの解放と取得のメモリの意味
    第1点:ロックの解放のメモリの意味は:スレッドAがロックを解放する時、Javaメモリモデルはローカルメモリの中の共有変数を
の各見出しページがあります.
    2つ目:ロックの取得メモリの意味は、スレッドAがロックを取得すると、Javaメモリモデルがローカルメモリの共有変数を
の値を無効にし、メインメモリに新しい値を読み込みます.
    ここで補足すると、ロックの解放とvolatileキーワードの書き込み操作は同じメモリの意味を持ち、ロックの取得と
volatileキーワードの読み取りには、同じメモリの意味があります.そして実装上、Javaにおけるロックメカニズムの実装は、
volatileキーワード.
読者は考えてください:ロックとvolatileの違いは?
3,ロックのメモリの意味の実現
    ロックには多くの実装がありますが、ここでは最も一般的な再ロック可能ReentrantLockを例に挙げて説明します.再ロック可能
ReentrantLockのメモリの意味の実装は、ロックメソッドを呼び出してロックを取得し、unlockメソッドを呼び出してロックを解放することである.再入力可能
ロックの実装はAQS(AbstractQueueSynchronizer)シンクロナイザフレームワークに依存する.AQS実現の鍵はvolatile関
したがって、ロックのメモリの意味の実装は、volatileキーワードに依存すると一般的に言われています.
三、機能実現と使用の観点からロック
    Javaはマルチスレッドをサポートしています.つまり、複数のスレッドは同じ変数またはオブジェクトに同時にアクセスできます.各オブジェクトがこれを持っているからです.
変数のコピーなので、プログラムの実行中に、あるスレッドで見た変数の値が必ずしも最新ではないため、線が生成されます.
プログラム同期の問題.各スレッドにコピーがあるのは、プログラムの実行速度を速めるためで、多くのマルチコアプロセッサです.
の双曲線コサインを返します.
1、ロックの使い方
ロックの使用方法は比較的簡単で、次のコードを通じて、ロックがどのように使用されているかを熟知することができます.
package com.spider.java;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 
 * @author yangcq
 * @desctiption Java   
 *
 */
public class LockLearning {
	//         ,    
	private static int account_balance = 1000000000;
	public static void main(String[] args) {
		//         lock
		Lock lock = new ReentrantLock();	
		//             
		lock.lock();	
		//     ,      ,                     
		updateAccountBalance(account_balance);	
		//             
		lock.unlock();
	}
	private static int updateAccountBalance(Integer account_balance){
		return account_balance - 1000;
	}	
}

ロックインタフェースを見てみましょう
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

lock():ロックを取得し、メソッドを呼び出すと、現在のスレッドはすぐにロックを取得し、ロックが取得されると、メソッドから戻ります.
lockInterruptibly():ロックの取得(ロックの取得の中断をサポート)
tryLock():ロックを取得します(ブロックされていないロックは、メソッドを呼び出すとすぐに返されます).
tryLock(long time,TimeUnit unit):ロックの取得(タイムアウト取得ロックのサポート)
unlock():ロックを解除する
新Condition():現在のロックにバインドされた待機通知コンポーネントを取得し、現在のスレッドはロックのみを取得します.
コンポーネントのwaitメソッドを呼び出すことができます.呼び出すと、現在のスレッドはロックを解除されます.
ソースコードの注釈はとても詳しくて、みんなは細かく味わうことができます:
    /**
     * Returns a new {@link Condition} instance that is bound to this
     * {@code Lock} instance.
     *
     * 

Before waiting on the condition the lock must be held by the * current thread. * A call to {@link Condition#await()} will atomically release the lock * before waiting and re-acquire the lock before the wait returns. * *

Implementation Considerations * *

The exact operation of the {@link Condition} instance depends on * the {@code Lock} implementation and must be documented by that * implementation. * * @return A new {@link Condition} instance for this {@code Lock} instance * @throws UnsupportedOperationException if this {@code Lock} * implementation does not support conditions */


2,ロックとsynchronizedキーワードの比較
ロックといえば、ロックの始祖であるsynchronizedキーワードを取り上げざるを得ませんが、まずsynchronizedキーワードの使用を見てみましょう.
package com.spider.java;
import java.util.logging.Logger;
/**
 * 
 * @author yangcq
 * @description synchronized        
 *
 */
public class SynchronizedLearning {
	static Logger logger = Logger.getAnonymousLogger();
	public static void main(String[] args) {
		/**
		 * synchronized        2   ,        
		 */
		//    
		synchronized(SynchronizedLearning.class){
			logger.info("     ...");
		}
		synchronizedMethod();
	}
	//     
	public static synchronized void synchronizedMethod(){
		logger.info("      ...");
	}
}

まとめてみると、ロックはsynchronizedキーワードともロックを実現できる機能で、ロックは
synchronizedキーワードの後、ロックはより先進的で、synchronizedがあります.
キーワードが持たない機能には、主に以下の点があります.
ブロックされていない取得ロックをサポートします.
中断された取得ロックをサポートする.
タイムアウト取得ロックをサポートする.
四、AQSシンクロナイザフレームワーク(AbstractQueueSynchronizer)
    ロックの実装はAQSに依存し,ロックインタフェースの実装は基本的に同期を集約することによって行われる.
スレッドアクセス制御を完了するために、器のサブクラスが使用されます.AQSはキュー同期器とも呼ばれ、ロックやその他の同
ステップコンポーネントのベースフレーム.AQSはintメンバー変数を使用して同期状態を表し、内蔵FIFOチームを通じて
列を使用して、リソース取得スレッドのキュー作業を完了します.
    AQSの主な使用方法は継承であり、サブクラスは同期器AQSを継承し、その抽象的な方法を実現する.
に表示されます.
    キュー同期器AQSはロックの実現の鍵であり、ロックの実現においてキュー同期器を集約し、キュー同
ステップはロックの意味を実現します.両者の関係は,ロックはユーザ向けであり,同期器は面であると理解できる.
ロックの実装クラス.
1,同期フレームAQSの使用説明
まず、ロック、パーソナルロックをカスタマイズします.つまり、同じ時点でロックを取得できるのは1つのスレッドだけです.
package com.spider.java;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
 * 
 * @author yangcq
 * @description      -    UserDefineLock,        ,           
 *
 */
public class UserDefineLock implements Lock {

	//        AQS  ,                         
	private final SynchronizerDefine synchronizerDefine = new SynchronizerDefine();
	@Override
	public void lock() {
		synchronizerDefine.acquire(1);
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		synchronizerDefine.acquireInterruptibly(1);
	}

	@Override
	public boolean tryLock() {
		return synchronizerDefine.tryAcquire(1);
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		return synchronizerDefine.tryAcquireNanos(1, unit.toNanos(time));
	}

	@Override
	public void unlock() {
		synchronizerDefine.release(1);
	}

	@Override
	public Condition newCondition() {
		return synchronizerDefine.newCondition();
	}
	
	public boolean hasQueueThreads(){
		return synchronizerDefine.hasQueuedThreads();
	}
	
	public boolean isLocked(){
		return synchronizerDefine.isHeldExclusively();
	}

	//      ,      
	private static class SynchronizerDefine extends AbstractQueuedSynchronizer{
		/**
		 * 
		 */
		private static final long serialVersionUID = -5572420543429760541L;
		//              
		protected boolean isHeldExclusively(){
			return getState() == 1;
		}
		//     0 ,   
		public boolean tryAcquire(int acquires){
			if(compareAndSetState(0,1)){
				//     :Sets the thread that currently owns exclusive access.
				setExclusiveOwnerThread(Thread.currentThread());
				return true;
			}
			return false;
		}
		//      ,      0
		protected boolean tryRelease(int releases){
			if(getState() == 0){
				throw new IllegalMonitorStateException();
			}
			setExclusiveOwnerThread(null);
			setState(0);
			return true;
		}
		//     Condition,  Condition     condition  
		Condition newCondition(){
			return new ConditionObject();
		}
	}
}

次にテストクラスを作成し、10スレッドを起動し、リソースを競合し、1.5秒ごとに1行の情報を印刷します.
package com.spider.java;
import java.util.concurrent.locks.Lock;
import java.util.logging.Logger;
/**
 * 
 * @author yangcq
 * @description      AbstractQueuedSynchronizer
 * @description public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
 *
 */
public class AbstarctQueueSynchronizerLearning {
	//   logger  
	static Logger logger = Logger.getAnonymousLogger();
	//    
	public static void main(String[] args){
		test();
	}
	public static void test(){
		final Lock lock = new UserDefineLock();
		class ProductThread extends Thread{
			public void run(){
				while(true){
					lock.lock();
					try{
						sleep(1500);
						logger.info(Thread.currentThread().getName());
						sleep(1500);
					}
					catch(Exception e){
						lock.unlock();
					}
					finally{
						lock.unlock();
					}
				}
			}
		}
		//   5   
		for(int i=0;i<10;i++){
			ProductThread productThread = new ProductThread();
			productThread.setDaemon(true);
			productThread.start();
		}
		//   1.5     -----------
		for(int i=0;i<10;i++){
			try {
				ProductThread.sleep(1500);
				logger.info("-----------");
			} 
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

コンソール出力情報(競合関係のため、コンソール出力はランダムで、実行結果が異なる):
2016-7-31 1:44:04 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:04 com.spider.java.AbstarctQueueSynchronizerLearning$1ProductThread run
  : Thread-1
2016-7-31 1:44:06 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:07 com.spider.java.AbstarctQueueSynchronizerLearning$1ProductThread run
  : Thread-1
2016-7-31 1:44:07 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:09 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:10 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:10 com.spider.java.AbstarctQueueSynchronizerLearning$1ProductThread run
  : Thread-1
2016-7-31 1:44:12 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:13 com.spider.java.AbstarctQueueSynchronizerLearning$1ProductThread run
  : Thread-3
2016-7-31 1:44:13 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:15 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:16 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:44:16 com.spider.java.AbstarctQueueSynchronizerLearning$1ProductThread run
  : Thread-3
2016-7-31 1:44:18 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------

五、再ロック可能java.util.concurrent.locks.ReentrantLock
1、再ロックとは
    再読み込み可能ロックとは、再読み込みをサポートするロック、すなわち、同じスレッドで同じロックを繰り返し取得することをサポートするロックです.可
再入ロックと私たちが定義した独占ロックには、どのような違いがありますか.明らかな違いは、もし私たちが
スレッドAがロックを取得した後、lock.lock()メソッドを再度呼び出すと、スレッドAがブロックされ、スレッドA自身が自分自身を
ブロックされています.このように修正すると、上記のプログラムの実行結果は次のようになります.
2016-7-31 1:58:50 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:58:51 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:58:53 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:58:54 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:58:56 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:58:57 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:58:59 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:59:00 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:59:02 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------
2016-7-31 1:59:03 com.spider.java.AbstarctQueueSynchronizerLearning test
  : -----------

2,再入可能ロックはどのように実現されるか
    再ロック可能java.util.concurrent.locks.ReentrantLockには、現在のスレッドを判断する方法があります.
ロックを取得するスレッドであるかどうか、すなわち、ロックを取得したスレッドAであるlock.lock()メソッドを再度呼び出す場合、
再読み込み可能ロックは、同期ステータス値を増加させ、trueを返してロックの取得に成功したことを示します.ソースコードは次のとおりです.
        /**
         * Performs non-fair tryLock.  tryAcquire is
         * implemented in subclasses, but both need nonfair
         * try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

このように処理した後、ロックを解放するとき、それに応じて処理ロジックを変更します.
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

3、再ロック可能な公平と非公平の競争実現
    再ロックは、公平な方法でロックを取得するか、非公平な方法でロックを取得するかをサポートします.設定は簡単ですが、以下の
コード:ロックされたオブジェクトを作成するときに設定します.
Lock lock = new ReentrantLock(true);    //          
Lock lock = new ReentrantLock(false);   //           

ソースコードは次のとおりです.
    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = (fair)? new FairSync() : new NonfairSync();
    }

この2つのロックを取得する方法にはどのような違いがありますか?違いは,公平にロックを取得すると,現在のスレッドが
ロックの取得を要求する最初のスレッドではありません.ロックの取得を最初に要求したスレッドであれば、ロックの取得に成功します.そうでなければ
ロックの取得に失敗しました.
    /**
     * Sync object for fair locks           
     */
    final static class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //        ,         ,          
                //             。
                if (isFirst(current) && compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
    /**
     * Sync object for non-fair locks           
     */
    final static class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
        //           nonfairTryAcquire
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

六、読み書きロックjava.util.concurrent.locks.ReentrantReadWriteLock;
1、読み書きロックとは
    読み書きロックは、複数のスレッドが同じ時点でリソースに同時にアクセスできるロックです.リードライトロックメンテナンス
一対のロック、1つのリードロックと1つのライトロック、実は原理は読み書きが分離しているので、読み書きを主とするシーンの下で、
ほとんどの時間は読み取りであり、複数のリードスレッドが同時にアクセスし、常に同期状態を維持できることを知っています.だから
この場合、ロックをかけないことで、プログラムの実行速度を効果的に向上させることができます.
2,読み書きロックの実現原理
    読み書きロックの原理は、実は2つのロック、1つの読み書きロック、1つの書き込みロックを維持しています.読み取り操作時に読み取りを取得
ロック、書き込み操作時に書き込みロックを取得します.書き込みロックが取得されると、後の読み書き要求がブロックされ、書き込みロックが解放されると、
その後の読み書き操作は続行されます.
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable
    /** Inner class providing readlock    (   )*/
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock   (   )*/
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics      AQS   */
    private final Sync sync;

    リードロックとライトロックは、いずれもReentrantReadWriteLockの内部クラスであり、この2つのロックは、いずれもロックインタフェースを実現している
実装クラス.
public static class ReadLock implements Lock, java.io.Serializable   //   
public static class WriteLock implements Lock, java.io.Serializable  //   

七、Conditionインタフェース
1,Conditionの役割
    ConditionインタフェースはObjectのようなモニタ方法を提供し、ロックと組み合わせて待機/通知モードを実現することができる.我々
Javaオブジェクトのいずれかには、次の方法があります.
wait();             //待ち受ける
wait(long timeout); //タイムアウト待ち
notify();           //呼び覚ます
notifyAll();        //すべてを呼び覚ます
これらの方法はあまり使われていませんが、Javaは私たちに提供してくれました.これらの方法はどんな役割を果たしますか?実はこれらの方法はすべて
Objectオブジェクトのモニタで、synchronizedキーワードと組み合わせて使用し、待機/通知モードを実現できます.
Conditionインタフェースでは、次のような方法も提供されています.
public interface Condition {
    void await() throws InterruptedException;
    void awaitUninterruptibly();
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal();
    void signalAll();
}

Conditionインタフェースはロックと連携して待機/通知モードを実現できる.ここを見て、皆さんは少し疑問に思っているかもしれませんが、
Objectモニタ    + synchronizedキーワード
Conditionモニタ+Lock
これが待機/通知モードのアップグレード版ではないでしょうか.
2,Conditionの使用
package com.spider.java;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
/**
 * 
 * @author yangcq
 * @description Condition   + Lock     /    
 * @description     ConditionLearningTest
 *
 */
public class ConditionLearning {
	static Logger logger = Logger.getAnonymousLogger(); //   
	Lock lock = new ReentrantLock();
	Condition condition_get = lock.newCondition();
	
	//     Map
	Map map = new HashMap();
	
	//    --      ,   ...
	public void conditionWait() throws InterruptedException{
		map.put("yangcq", "");
		lock.lock();
		try{
			while("".equals(map.get("yangcq"))){
				logger.info("key 'yangcq'   ,    ...");
				condition_get.await();
			}
			logger.info("key 'yangcq'    :" + map.get("yangcq"));
		}
		finally{
			lock.unlock();
		}
	}
	//    --     ,   ,  
	public void conditionSignal() throws InterruptedException{
		map.put("yangcq", "1988");
		lock.lock();
		try{
			if("".equals(map.get("yangcq"))){
				logger.info("key 'yangcq'   ,            ...");
			}
			else{
				logger.info("key 'yangcq'   ,         ");
				//       (     )
				condition_get.signal();
			}
		}
		finally{
			lock.unlock();
		}
	}
}

テストクラス:ConditionLearningTest.java
package com.spider.java;
/**
 * 
 * @author yangcq
 * @description Condition   + Lock     /      
 *
 */
public class ConditionLearningTest {
	//   ConditionLearning  
	static ConditionLearning conditionLearning = new ConditionLearning();
	
	public static void main(String[] args) {
		getValue(); //      ,       ...
		putValue(); //        ,   ,       ,         
	}
	
	@SuppressWarnings("static-access")
	public static void getValue(){
		//      
		class ConsumerThread extends Thread{
			public void run(){
				try {
					conditionLearning.conditionWait();
				} 
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		ConsumerThread ConsumerThread = new ConsumerThread();
		ConsumerThread.start();
		try {
			ConsumerThread.sleep(2000);
		} 
		catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void putValue(){
		//      
		class ProductThread extends Thread{
			public void run(){
				try {
					conditionLearning.conditionSignal();
				} 
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		ProductThread ProductThread = new ProductThread();
		ProductThread.start();
	}
}

さて、ここまで書いて、鍵についての説明はしばらく一段落しましたが、筆者のレベルは限られており、不適切な点は避けられませんので、ご了承ください.