Javaマルチスレッド同時プログラミング(相互反発ロックRentrant Lock)
13150 ワード
Javaの錠は通常二つに分けられます。
キーワードのsynchronizedで取得したロックを同期ロックと呼び、前のページではJavaマルチスレッド同時プログラミングSynchronizedキーワードと紹介されています。
java.util.co ncurrent(JUC)のカバンの中のロックは、インターフェースLockを継承することによって実現されるRentrant Lock(相互反発ロック)を継承し、ReadWriteLockを継承して実現されるRentrant ReadWriteLock(読み書きロック)です。
本編では主にRentrant Lock(相互反発ロック)を紹介します。
Reentrant Lock(相互反発ロック)
RentrantrantLockは、同じ時間に1つのスレッドにのみ占有され、所持後解放されない前に、他のスレッドがロックを獲得するには待つか、放棄するしかない。
Reentrant Lock相互反発ロックは、あるスレッドが複数回このロックを獲得することができます。
アンフェアロック
公平ロックと非公平ロックは、競争ロック時の秩序の有無で区別される。公平ロックは、順序性(FIFOキュー)を確保し、非公平ロックは、順序性を確保することができない(FIFOキューがあっても)。
しかし、公平は代価を払わなければならず、公平ロックは非公平ロックより性能を消耗するので、公平を確保しなければならない条件で、一般的に非公平ロックを使用するとスループット率が向上する。従って、Reentrant Lockのデフォルトの構造関数も「不公平」です。
一般的に使う
DEMO 1:
ロックのスコープ
DEMO 2:
終了待ち時間
DEMO 4:
TIMEOUTを300 msに変更したら、出力結果:(正常運転)
DEMO 5:
Rentrant Lock or synchronized
Reentrant Lock、synchronizedの間はどうやって選択しますか?
Reentrant Lockはパフォーマンス的にはsynchronizedよりも優れています。
RentrantrantLockは特に注意しなければなりません。明示的なリリースロックが必要なので、ロック()後はunlock()を覚えています。そしてfinallyの中にいなければなりません。
synchronizedは自動的にロックを解除して、使いやすいです。
Reentrant Lockは拡張性があり、ロックを中断し、タイミングロックを自由にコントロールできます。
synchronizedがブロックに入って待つと待ちきれなくなります。
キーワードのsynchronizedで取得したロックを同期ロックと呼び、前のページではJavaマルチスレッド同時プログラミングSynchronizedキーワードと紹介されています。
java.util.co ncurrent(JUC)のカバンの中のロックは、インターフェースLockを継承することによって実現されるRentrant Lock(相互反発ロック)を継承し、ReadWriteLockを継承して実現されるRentrant ReadWriteLock(読み書きロック)です。
本編では主にRentrant Lock(相互反発ロック)を紹介します。
Reentrant Lock(相互反発ロック)
RentrantrantLockは、同じ時間に1つのスレッドにのみ占有され、所持後解放されない前に、他のスレッドがロックを獲得するには待つか、放棄するしかない。
Reentrant Lock相互反発ロックは、あるスレッドが複数回このロックを獲得することができます。
アンフェアロック
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
RentrantrantLockの構造関数から見られますが、Rentrant Lockを実装する際には、公平なロックまたは非公平なロックを選択できます。デフォルトでは非公平なロックを構築します。公平ロックと非公平ロックは、競争ロック時の秩序の有無で区別される。公平ロックは、順序性(FIFOキュー)を確保し、非公平ロックは、順序性を確保することができない(FIFOキューがあっても)。
しかし、公平は代価を払わなければならず、公平ロックは非公平ロックより性能を消耗するので、公平を確保しなければならない条件で、一般的に非公平ロックを使用するとスループット率が向上する。従って、Reentrant Lockのデフォルトの構造関数も「不公平」です。
一般的に使う
DEMO 1:
public class Test {
private static class Counter {
private ReentrantLock mReentrantLock = new ReentrantLock();
public void count() {
mReentrantLock.lock();
try {
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + ", i = " + i);
}
} finally {
// finally
mReentrantLock.unlock();
}
}
}
private static class MyThread extends Thread {
private Counter mCounter;
public MyThread(Counter counter) {
mCounter = counter;
}
@Override
public void run() {
super.run();
mCounter.count();
}
}
public static void main(String[] var0) {
Counter counter = new Counter();
// :myThread1 myThread2 counter
MyThread myThread1 = new MyThread(counter);
MyThread myThread2 = new MyThread(counter);
myThread1.start();
myThread2.start();
}
}
DEMO 1出力:
Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1, i = 0
Thread-1, i = 1
Thread-1, i = 2
Thread-1, i = 3
Thread-1, i = 4
Thread-1, i = 5
DEMO 1は、RentrantrantLockのlockとunlockのみを使用して、一般的なロックの特性を提示し、スレッドの秩序ある実行を確保する。このシーンはsynchronizedにも適用されます。ロックのスコープ
DEMO 2:
public class Test {
private static class Counter {
private ReentrantLock mReentrantLock = new ReentrantLock();
public void count() {
for (int i = 0; i < 6; i++) {
mReentrantLock.lock();
// ,
try{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ", i = " + i);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// finally
mReentrantLock.unlock();
}
}
}
public void doOtherThing(){
for (int i = 0; i < 6; i++) {
// ,
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
}
}
}
public static void main(String[] var0) {
final Counter counter = new Counter();
new Thread(new Runnable() {
@Override
public void run() {
counter.count();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
counter.doOtherThing();
}
}).start();
}
}
DEMO 2出力:
Thread-0, i = 0
Thread-1 doOtherThing, i = 0
Thread-0, i = 1
Thread-1 doOtherThing, i = 1
Thread-0, i = 2
Thread-1 doOtherThing, i = 2
Thread-0, i = 3
Thread-1 doOtherThing, i = 3
Thread-0, i = 4
Thread-1 doOtherThing, i = 4
Thread-0, i = 5
Thread-1 doOtherThing, i = 5
DEMO 3:
public class Test {
private static class Counter {
private ReentrantLock mReentrantLock = new ReentrantLock();
public void count() {
for (int i = 0; i < 6; i++) {
mReentrantLock.lock();
// ,
try{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + ", i = " + i);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// finally
mReentrantLock.unlock();
}
}
}
public void doOtherThing(){
mReentrantLock.lock();
try{
for (int i = 0; i < 6; i++) {
// ,
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
}
}finally {
mReentrantLock.unlock();
}
}
}
public static void main(String[] var0) {
final Counter counter = new Counter();
new Thread(new Runnable() {
@Override
public void run() {
counter.count();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
counter.doOtherThing();
}
}).start();
}
}
DEMO 3出力:
Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1 doOtherThing, i = 0
Thread-1 doOtherThing, i = 1
Thread-1 doOtherThing, i = 2
Thread-1 doOtherThing, i = 3
Thread-1 doOtherThing, i = 4
Thread-1 doOtherThing, i = 5
DEMO 2とDEMO 3の出力を組み合わせると、ロックの役割領域はmRentrant Lockにあります。終了待ち時間
DEMO 4:
public class Test {
static final int TIMEOUT = 300;
private static class Counter {
private ReentrantLock mReentrantLock = new ReentrantLock();
public void count() {
try{
//lock()
mReentrantLock.lock();
// ,
for (int i = 0; i < 6; i++) {
long startTime = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - startTime > 100)
break;
}
System.out.println(Thread.currentThread().getName() + ", i = " + i);
}
} finally {
// finally
mReentrantLock.unlock();
}
}
public void doOtherThing(){
try{
//lockInterruptibly() , ,
mReentrantLock.lockInterruptibly();
for (int i = 0; i < 6; i++) {
// ,
long startTime = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - startTime > 100)
break;
}
System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " ");
}finally {
// ,
if(mReentrantLock.isHeldByCurrentThread()){
mReentrantLock.unlock();
}
}
}
}
public static void main(String[] var0) {
final Counter counter = new Counter();
new Thread(new Runnable() {
@Override
public void run() {
counter.count();
}
}).start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
counter.doOtherThing();
}
});
thread2.start();
long start = System.currentTimeMillis();
while (true){
if (System.currentTimeMillis() - start > TIMEOUT) {
// ,
if(thread2.isAlive()){
System.out.println(" , ");
thread2.interrupt();
}
break;
}
}
}
}
DEMO 4出力:
Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
,
Thread-1
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
スレッドthread 2は300 ms後のtimeoutを待ち、中断は成功を待つ。TIMEOUTを300 msに変更したら、出力結果:(正常運転)
Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1 doOtherThing, i = 0
Thread-1 doOtherThing, i = 1
Thread-1 doOtherThing, i = 2
Thread-1 doOtherThing, i = 3
Thread-1 doOtherThing, i = 4
Thread-1 doOtherThing, i = 5
タイムロックDEMO 5:
public class Test {
static final int TIMEOUT = 3000;
private static class Counter {
private ReentrantLock mReentrantLock = new ReentrantLock();
public void count() {
try{
//lock()
mReentrantLock.lock();
// ,
for (int i = 0; i < 6; i++) {
long startTime = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - startTime > 100)
break;
}
System.out.println(Thread.currentThread().getName() + ", i = " + i);
}
} finally {
// finally
mReentrantLock.unlock();
}
}
public void doOtherThing(){
try{
//tryLock(long timeout, TimeUnit unit)
boolean isLock = mReentrantLock.tryLock(300, TimeUnit.MILLISECONDS);
System.out.println(Thread.currentThread().getName() + " isLock:" + isLock);
if(isLock){
for (int i = 0; i < 6; i++) {
// ,
long startTime = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - startTime > 100)
break;
}
System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
}
}else{
System.out.println(Thread.currentThread().getName() + " timeout");
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " ");
}finally {
// ,
if(mReentrantLock.isHeldByCurrentThread()){
mReentrantLock.unlock();
}
}
}
}
public static void main(String[] var0) {
final Counter counter = new Counter();
new Thread(new Runnable() {
@Override
public void run() {
counter.count();
}
}).start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
counter.doOtherThing();
}
});
thread2.start();
}
}
DEMO 5出力:
Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-1 isLock:false
Thread-1 timeout
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
tryLock()はロックを獲得しようと試みていますが、tryLock(long timeout,TimeUnit unit)は与えられたtimeout時間内にロックを獲得してみます。タイムアウトするとロックをかけずに歩くので、判断しなければなりません。Rentrant Lock or synchronized
Reentrant Lock、synchronizedの間はどうやって選択しますか?
Reentrant Lockはパフォーマンス的にはsynchronizedよりも優れています。
RentrantrantLockは特に注意しなければなりません。明示的なリリースロックが必要なので、ロック()後はunlock()を覚えています。そしてfinallyの中にいなければなりません。
synchronizedは自動的にロックを解除して、使いやすいです。
Reentrant Lockは拡張性があり、ロックを中断し、タイミングロックを自由にコントロールできます。
synchronizedがブロックに入って待つと待ちきれなくなります。