Javaのマルチスレッド - ロック学習

  (Javaの下に最近コンパイル知識によって書き留め、マルチスレッド)

同期と原則を使用します

使用します。

Javaは自分のキーワードを同期され、推定は、我々は、Javaの初期使用のロック機構と接触することでロックを取得しようとする試みを阻止するためのロック、スレッド同期コードが実行されるようにJavaオブジェクトを使用して同期、マルチスレッド、ロックを取得するためのスレッドが解放されませんこの間、他のスレッドがロックを取得しようとし、その後、安全なマルチスレッドコードの実行を実現するために待ちます。

   1つの一般的な同期方法:新しい複数のインスタンスが、彼らはお互いに影響を与えていない場合は、現在のオブジェクトのインスタンスをロックし、同じオブジェクトインスタンスの同期方法は、ロックと競合する必要があります

       パブリッククラス種皮{例えば

    public synchronized void method1(){

    System.out.println("method1");

    }

    public synchronized void method2(){

    System.out.println("method2");

    }

  }

静的同期方法2:オブジェクト、クラスオブジェクトとクラスクラスのロック・オブジェクト・インスタンスとして互いに独立クラス(のjava.lang.Class)を使用してクラスオブジェクト。

       パブリッククラスTESTB {例えば

    public 静的 synchronized void method1(){

    System.out.println("method1");

    }

    public 静的 synchronized void method2(){

    System.out.println("method2");

    }

  }

方法3シンクブロック:ロックなどのオブジェクトの後ろにブラケットを使用して同期

       パブリッククラスTESTC {例えば

    プライベートオブジェクトのロック

    public void method1(){

    synchronized(lock){

      System.out.println( "法1");

    }

    }

    public void method2(){

    synchronized(lock){

      System.out.println( "方法2");

    }

    }

  }

         クラスやオブジェクトのインスタンスをさらに検討がメモリ内のデータのほんの一部である後に使用することを学ぶ、それはそれのロック機能を提供する方法ですか?

 同期の原則:

      (Java構文仕様基準オラクル   https://docs.oracle.com/javase/specs/jls/se12/html/jls-17.html#jls-17.1   と

             JVMの説明  https://docs.oracle.com/javase/specs/jvms/se12/html/jvms-2.html#jvms-2.11.10

                  

              

    大雑把翻訳: 

       (1)。各Javaクラスは、ロックとモニタのロックを解除することができます関連するスレッドをしているモニター、一度スレッドがこのモニターをロックし、他のスレッドは、彼がロックを解除ロックを解除するために待機する必要があり、監視オブジェクトは、同じリエントラントでありますリエントラントスレッドは、1で数える方法はまた、待機モニターの対象となるのを待ちます。

       (2)メソッドはロック取得静的メソッドのクラスの例をモニター、モニターロックの現在のオブジェクト(本)の実行前に、共通の同期を取得し、ブロック同期方法を実行する前に、ロックオブジェクトブラケットを監視取得します。

       具体的なプロセスは、この方法は、コンパイルフラグ(ACC_SYNCHRONIZED)が再生されconstantpool、JVM同期方法について実行される(3)、実行されると、方法を実行するために、前者の方法を実行識別されるモニタに入り、および方法が正常に行われ実行または例外がモニターから出た後、ブロックの同期方法はmonitorenterとmonitorexit命令がブロックのメソッドの前と後の時間をコンパイルに追加されます

  複合体の一部である実装原理モニタオブジェクトヘッダデータ構造JavaとJavaのオブジェクトモデルを、理解する必要性を理解していき、あなたはホットスポットのチュートリアルやソースコードを参照することができます。Https://github.com/openjdk-mirror/jdk7u-hotspot

     ホットスポットは、C ++の実装で、OOP-クラースモデルに基づいて設計されています。OOP(通常のオブジェクトポインタ)共通オブジェクト・ポインタを参照し、クラース・オブジェクト・インスタンス記載された特定のタイプのために。

     

     再来一个源码(oop.hpp)截图: 

      

 _mark是图里的markword metadata里的_klass 是图里指向方法区instanceKlass 的元数据指针,_mark 字段则包含了需要java类的状态信息,对象的锁状态也在其中。

    以32位操作系统为例子:

    

monitor机制:

  

 

ObjectMonitor(objectMonitor.cpp)的源码里的尝试获取锁解析。

简单说明: 尝试获取monitor 方法,THREAD 是请求获取monitor的线程,  _owner 是记录的获取到当前monitor的线程。

       (1)两者不相等时再判断是否线程可以拥有_own的锁(等线程部分详解),不拥有再基于CAS尝试获取,都失败返回false。

       (2)两者相等计数加一,返回true

这只是monitor其中一个方法,其他的enter,exit等方法可以参考源码,monitor的方法和java对象的头部信息完成了synchronized的锁机制。

java synchronized 锁说明:

(1)偏向锁: 锁标志01 和无锁标志一样,java对象头里还记录了线程ID,线程获取锁时判断线程ID为空或者等于自己,则成功。其他线程同时获取则失败,锁变成轻量级锁。

(2)轻量级锁: 锁标志00 ,获取不到锁的线程会自旋(循环获取),再次获取失败,锁膨胀为重量级锁

(3)重量级锁:锁标志10, 获取不到锁的线程block。

到这里synchronized 关键字 锁的使用和原理基本结束,但有一个最最基本的问题没有解释,线程怎么通过CAS就能安全地获取到锁了,CAS可以说是锁的最基本的原子操作,等看完java并发包的锁再一起讲

 

 

        

二 java并发包里的锁

 使用:

 先看一下lock接口的方法:

void lock();                                                    获取锁,获取不到则线程block
void lockInterruptibly() throws InterruptedException; 获取锁,获取不到,如果线程的中断标识为true,则抛出异常,否则线程block,
boolean tryLock(); 尝试获取锁,成功返回true,不成功返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;尝试获取锁,成功返回true,不成功如果中断标识为true,抛出异常,否者返回false
void unlock(); 释放锁
Condition newCondition(); 返回一个Condition 对象
并发包提供的对象有:ReentrantLock-可重入锁,ReentrantReadWriteLock-可重入读写锁,StampedLock-不可以冲入,适用于内部
ReentrantLock reentrantLock = new ReentrantLock();
if(reentrantlock.tryLock()){ // even you tryLock is true when you lock can still block by the lock ,that is multiply -thread programming
try{
reentrantLock.lock(); //only one thread can lock the reentrantLock,the others will be blocked
......
}
finally{
reentrantLock.unlock(); //in case of code crush ,unlock the reentrantlock in the finally statement block
}
}else
{
....
}

  ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); 

   Lock writeLock = reentrantReadWriteLock.writeLock();

  Lock readLock = reentrantReadWriteLock.readLock();

   writeLock是独占锁,一个线程占用了,其他线程都需要排队

   readLock是共享锁,readLock可以多个线程公用,但是跟writeLock 互斥

 

 

原理:

打开java并发包的源码,ReentrantLock的源码,ReentrantLock的Lock等方法基本就是Sync 属性的方法.ReentrantLock 有 lock lockInterruptibly tryLock 带时间的trylock unlock等方法。Sync有公平锁和非公平锁,默认非公平锁


以调用ReentrantLock lock方法为例子 内部调用FairSync或者UnFairSync的lock方法,内部调用AbstractQueuedSynchronizer的acquire的方法,父类方法调用抽象方法tryAcquire(抽象方法在子类实现,FairSync实现时考虑了排队)
方法成功后 再acquireQueued,正真排队获取锁。其他方式的lock和trylock等方法可以参考代码。

   

    hasQueuedPredecessors 方法时判断当前线程是不是在排队的对头部。不在头部返回true。

再来看看AbstractQueuedSynchronizer 的队列和获取锁的方法,判断如果是在head后的节点,再次tryacquire,成功则返回中断标志位, 如果需要中断则调用unsafe类中断线程。

  等待队列模型: 

 

释放锁:非共享锁释放队列头。

 

 

CAS

这里用到了Unsafe类提供的基本的CAS操作和线程的中断方法,UnSafe类还提供了线程安全的基本类。这里先看看CAS操作的原理

unsafe的CAS方法一个例子:

 

打开hotspot的Unsafe类(unsafe.cpp)

 

步骤基本意思是oopDesc (oopDesc::atomic_compare_exchange_oop)方法判断孤弱入参对象指定位置的值等于期望的值,交换为给定的新值,并设置barrier,内存屏障是多线程同时运行时取到某个值时 因为缓存锁必须到主存里取最新的值,volatile关键字就是类似方式实现

继续看原子交换方法(opp.inline.hpp):其他是一些判断主要是 Atomic::cmpxchg 和  Atomic::cmpxchg_ptr 方法

atomic的方法跟计算机的操作系统有关系,选一个atomic_linux_x86.inline.hpp文件:

由内嵌的汇编代码的汇编指令cmpxchgl 完成值的比较和交换,原子性得以保证。

 

volatile

volatile关键字的作用是让每个线程的缓存里的数据跟主存里保持一致。
问题引入: 多个线程多个cpu执行时,每个cpu都有自己的缓存(电脑的L1,L2,寄存器都是缓存),修改和读取数据都是先从缓存修改读取,再同步到内存,这样就可能出现其他线程读取不到最新数据的问题
解决方法: 设置成volatile的属性字段,多个缓存会读取时都回读取到最新的数据。
实现原理:
volatile的属性字段读写操作前面和后面加一个 内存屏障,强制读和写到主存


Cpu数据原子操作实现:
基于总线或者缓存锁 #lock,总线锁 锁住所有的cpu,性能较差。 缓存锁锁住缓存并基于缓存一致性,其他cpu写的缓存数据无效,实现数据原子操作。


 

 

 








 

おすすめ

転載: www.cnblogs.com/thinkqin/p/11099384.html