Java 병렬 프로 그래 밍:Lock&ReentrantLock&Condition
14165 단어 자바 다 중 루틴
다음은 본 고 에 포 함 된 지식 점 이다.
1.Lock 인터페이스 소개
2.ReentrantLock 사용
3.ReentrantLock 과 synchronized 의 동기 화 차이
4.ReadWriteLock 소개
5.Condition 사용
1.Lock 인터페이스 소개
ReentrantLock 은 자체 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();
}
이 방법 에서 lock(),lockInterruptibly(),tryLock(),tryLock(long time,TimeUnit unit)은 모두 자 물 쇠 를 가 져 오 는 데 사 용 됩 니 다.차 이 는 다음 과 같 습 니 다.lock():자 물 쇠 를 가 져 옵 니 다.자물쇠 가 사용 가능 하 다 면 자 물 쇠 를 가 져 옵 니 다.그렇지 않 으 면 기다 리 십시오.
tryLock():잠 금 을 사용 할 수 있 으 면 잠 금 을 가 져 오고 값 true 를 즉시 되 돌려 줍 니 다.그렇지 않 으 면 되 돌려 줍 니 다. false,기다 리 지 않 습 니 다.
tryLock(long time,TimeUnit unit):주어진 대기 시간 내 에 잠 금 을 사용 할 수 있 으 면 잠 금 을 가 져 오고 값 true 를 즉시 되 돌려 줍 니 다.그렇지 않 으 면 false 로 돌아 갑 니 다.
lockInterruptibly():자 물 쇠 를 가 져 오고 중단 할 때 까지 기 다 립 니 다.잠 금 을 사용 할 수 있 고 현재 스 레 드 가 중단 되 지 않 으 면 잠 금 을 가 져 옵 니 다.그렇지 않 으 면 기다 리 십시오.다른 스 레 드 에 의 해 중단 되면 Interrupt Exception 이상 을 던 집 니 다.
unlock()은 자 물 쇠 를 풀 고 new Condition()뒤에 다시 이야기 합 니 다.
Lock 로 자 물 쇠 를 가 져 오 려 면 수 동 으로 자 물 쇠 를 풀 어야 하 며 이상 이 발생 했 을 때 자동 으로 자 물 쇠 를 풀 지 않 기 때문에 lock()/unlock()은 일반적으로 try/finally 구문 블록 과 결합 하여 완성 합 니 다.보통 사용 형식 은 다음 과 같 습 니 다.
Lock lock = ...;
lock.lock();//
try{
//
}catch(Exception e){
}finally{
lock.unlock();//
}
2.ReentrantLock 의 사용
ReentrantLock 은 Serializable 인 터 페 이 스 를 실현 하 는 것 외 에 유일 하 게 Lock 인 터 페 이 스 를 실현 합 니 다.다음은 사용법 을 살 펴 보 겠 습 니 다.
public class ReentrantLockTest {
private Lock lock = new ReentrantLock(); //lock ,
private List arrList = new ArrayList();
public static void main(String[] args) {
final ReentrantLockTest test = new ReentrantLockTest();
new Thread(){
public void run(){
test.testLock(Thread. currentThread());
}
}.start();
new Thread(){
public void run(){
test.testLock(Thread. currentThread());
}
}.start();
}
/**
* lock
* @param thread
*/
public void testLock(Thread thread){
//Lock lock = new ReentrantLock();// ,
// testLock lock , , lock.lock() , 。 : ,
lock.lock();//lock() , 。 , 。
try {
System. out.println(thread.getName()+" " );
for(int i=0; i<5; i++){
arrList.add(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
System. out.println(thread.getName()+" " );
lock.unlock();//unlock lock , try,catch,finally
}
}
/**
* tryLock
* tryLock(long time, TimeUnit unit)
* @param thread
*/
public void testTryLock(Thread thread){
if(lock .tryLock()){//tryLock() , , , true, ( ), false, 。 。
//tryLock(long time, TimeUnit unit) tryLock() , , , false。 , true。
try {
System. out.println(thread.getName()+" " );
for(int i=0; i<5; i++){
arrList.add(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
System. out.println(thread.getName()+" " );
lock.unlock();
}
} else{
System. out.println(thread.getName()+" " );
}
}
}
실행 결과:Thread-0
Thread-0
Thread-1
Thread-1
동기 화가 이 루어 진 것 을 볼 수 있 습 니 다.lockInterruptibly():자 물 쇠 를 가 져 오고 중단 가능 한 인 스 턴 스 를 기다 리 십시오.
public class TestLockInterruptibly {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
TestLockInterruptibly test = new TestLockInterruptibly();
MyThread thread1 = new MyThread(test);
MyThread thread2 = new MyThread(test);
thread1.start();
thread2.start();
try {
Thread. sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
}
public void testLockInterruptibly(Thread thread) throws InterruptedException{
lock.lockInterruptibly(); // , , , InterruptedException
try {
System. out.println(thread.getName()+" " );
long startTime = System.currentTimeMillis();
for( ; ;) {
if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE )
break;
//。。。
}
}
finally {
System. out.println(Thread.currentThread().getName()+ " finally");
lock.unlock();
System. out.println(thread.getName()+" " );
}
}
}
class MyThread extends Thread {
private TestLockInterruptibly test = null ;
public MyThread(TestLockInterruptibly test) {
this.test = test;
}
@Override
public void run() {
try {
test.testLockInterruptibly(Thread.currentThread());
} catch (InterruptedException e) {
System. out.println(Thread.currentThread().getName()+ " ");
}
}
}
실행 결과:Thread-0
Thread-1
thread 2 가 끊 긴 게 보 여요.
3.ReentrantLock 과 synchronized 의 동기 화 차이
같은 점:
1.동기 화 기능 구현
2.모두 스 레 드 재 입 성 을 가진다.
다른 점:
1.코드 작성 에 있어 ReentrantLock 은 API 차원 의 상호 배척 자물쇠 이 고 synchronized 는 원생 문법 차원 의 상호 배척 자물쇠 이다.
2.기능 상 ReentrantLock 은 synchronized 보다 더 고 급 스 러 운 기능 을 향상 시 켰 습 니 다.
대기 중단 가능,
공평 한 자 물 쇠 를 실현 할 수 있 습 니 다.
자 물 쇠 는 여러 조건 을 연결 할 수 있 습 니 다.
대기 중단 가능 이란 자 물 쇠 를 가 진 스 레 드 가 오랫동안 자 물 쇠 를 풀 지 않 을 때 기다 리 고 있 는 스 레 드 는 기다 림 을 포기 할 수 있 습 니 다.인 터 럽 트 가능 한 기능 은 실행 시간 이 매우 긴 동기 블록 에 도움 이 됩 니 다.Lock 의 lock Interruptibly()방법 은 중단 가능 한 실현 을 기다 리 는 것 이다.
공정 자 물 쇠 는 여러 스 레 드 가 같은 자 물 쇠 를 기다 리 고 있 을 때 자 물 쇠 를 신청 하 는 시간 순서에 따라 순서대로 자 물 쇠 를 가 져 와 야 한 다 는 것 을 말한다.공평 한 자물쇠 가 아니 라 이 점 을 보장 할 수 없다.자물쇠 가 풀 릴 때 모든 스 레 드 가 자 물 쇠 를 얻 을 수 있다.synchronized 는 불공 정 잠 금 입 니 다.ReentrantLock 은 기본적으로 불공 정 잠 금 이지 만 불 값 이 있 는 구조 방법 으로 공정 잠 금 을 요구 할 수 있 습 니 다.
ReentrantLock 구조 기 원본 코드:
//
public ReentrantLock() {
sync = new NonfairSync();
}
//
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
자 물 쇠 는 여러 조건 을 연결 할 수 있다 는 것 은 하나의 ReentrantLock 대상 이 여러 condition 대상 을 연결 할 수 있 는 것 을 말 하 며,synchronized 에 서 는 잠 금 대상 의 wait()와 notify()또는 notify All()이 하나의 은밀 한 조건 을 실현 할 수 있 으 며,하나 이상 의 조건 과 연결 하려 면 자 물 쇠 를 추가 해 야 하 며,ReentrantLock 은 이렇게 할 필요 가 없다.new Condition()방법 을 여러 번 호출 하면 됩 니 다.
3.성능 에 있어 Jdk 6 는 잠 금 에 대한 최적화 가 많이 들 어 갔 고 synchronized 와 ReentrantLock 의 성능 은 대체적으로 똑 같 습 니 다.성능 요 소 는 더 이상 ReentrantLock 을 선택 하 는 이유 가 아니다.가상 컴퓨터 는 앞으로 성능 개선 에 있어 서도 원생 의 synchronized 에 더욱 치 우 칠 것 이 므 로 synchrozied 를 사용 하여 동기 화 하 는 것 을 우선 고려 해 야 한다.
4.ReadWriteLock 소개
ReadWriteLock 도 하나의 인터페이스 입 니 다.두 가지 방법 만 정 의 했 습 니 다.
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}
ReadWriteLock 은 읽 기 전용 작업 과 쓰기 용 잠 금 을 유지 합 니 다.writer 가 없 으 면 읽 기 자 물 쇠 는 여러 reader 스 레 드 에서 동시에 유지 할 수 있 습 니 다.기록 자 물 쇠 는 독점 적 이다.ReentrantReadWriteLock 클래스 는 ReadWriteLock 인 터 페 이 스 를 실현 합 니 다.여러 스 레 드 가 동시에 읽 기 자 물 쇠 를 가 져 오 는 상황 을 살 펴 보 겠 습 니 다.
public class ReadWriteLockTest {
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
final ReadWriteLockTest test = new ReadWriteLockTest();
new Thread(){
public void run(){
test.get2(Thread. currentThread());
}
}.start();
new Thread(){
public void run(){
test.get2(Thread. currentThread());
}
}.start();
}
/**
* readLock :
* @param thread
*/
public void get2(Thread thread) {
readWriteLock.readLock().lock();
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System. out.println(thread.getName()+" " );
}
System. out.println(thread.getName()+" " );
} finally {
readWriteLock.readLock().unlock();
}
}
}
실행 결과:Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-1
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
결과 에 따 르 면 두 스 레 드 가 동시에 실행 되 고 있 는 것 은 읽 기 자물쇠(ReadLock)가 여러 스 레 드 에 의 해 동시에 얻 을 수 있다 는 것 을 의미한다.이것 은 실제 응용 장면 에 도 부합된다.읽 기 작업 의 효율 을 높이다.그러나 주의해 야 할 것 은 한 스 레 드 가 읽 기 자 물 쇠 를 점용 했다 면 다른 스 레 드 가 자 물 쇠 를 신청 하려 면 자 물 쇠 를 신청 하 는 스 레 드 는 읽 기 자 물 쇠 를 풀 기 를 기다 리 고 있 습 니 다.
만약 에 한 스 레 드 가 자 물 쇠 를 점용 했다 면 다른 스 레 드 가 자 물 쇠 를 쓰 거나 자 물 쇠 를 읽 으 려 면 신청 한 스 레 드 는 자 물 쇠 를 풀 기 를 기다 릴 것 입 니 다.
4.Condition 사용
synchronized 와 wait()와 nitofy()/notify All()방법 을 결합 하면 대기/알림 모델 을 실현 할 수 있 습 니 다.ReentrantLock 역시 가능 하지만 Condition 을 빌려 야 합 니 다.Condition 은 더욱 유연성 이 있 고 구체 적 으로 다음 과 같 습 니 다.
1.하나의 Lock 에서 Condition 인 스 턴 스 를 여러 개 만 들 고 다 중 알림 을 실현 할 수 있 습 니 다.
2.notify()방법 으로 알림 을 할 때 알림 을 받 은 스 레 드 는 자바 가상 컴퓨터 가 무 작위 로 선택 하지만 ReentrantLock 과 Condition 을 결합 하면 선택 적 인 알림 을 실현 할 수 있 습 니 다.이것 은 매우 중요 합 니 다.
Condition 을 이용 하여 대기/알림 모델 을 실현 하 는 가장 간단 한 용법 을 살 펴 보 세 요.아래 코드 는 await()와 signal()전에 lock()에서 자 물 쇠 를 가 져 와 야 합 니 다.사용 이 완료 되면 finally 에서 자 물 쇠 를 풀 어야 합 니 다.이것 은 wait()/notify()/notify All()을 사용 하기 전에 대상 자 물 쇠 를 먼저 가 져 와 야 합 니 다.
import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionTest {
private int queueSize = 10;
private PriorityQueue queue = new PriorityQueue(queueSize);
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
ConditionTest test = new ConditionTest();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread {
@Override
public void run() {
consume();
}
private void consume() {
while (true) {
lock.lock();
try {
while (queue.size() == 0) {
try {
System.out.println(" , ");
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll(); //
notFull.signal();
System.out.println(" , " + queue.size() + " ");
} finally {
lock.unlock();
}
}
}
}
class Producer extends Thread {
@Override
public void run() {
produce();
}
private void produce() {
while (true) {
lock.lock();
try {
while (queue.size() == queueSize) {
try {
System.out.println(" , ");
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(1); //
notEmpty.signal();
System.out.println(" , :"
+ (queueSize - queue.size()));
} finally {
lock.unlock();
}
}
}
}
}
Condition 의 await()방법 은 자 물 쇠 를 방출 하 는 것 입 니 다.원인 도 간단 합 니 다.만약 await()방법 이 자 물 쇠 를 방출 하지 않 는 다 면 signal()방법 은 Condition 의 signal()방법 으로 어떻게 호출 할 수 있 습 니까?
하나의 Condition 을 사용 하면 여러 스 레 드 가 이 Condition 에 의 해 await()에 전 달 된 후 Condition 의 signal All()방법 으로 모든 스 레 드 를 깨 웁 니 다.일부 스 레 드 를 따로 깨 우려 면 어떻게 해 야 합 니까?new 는 여러 개의 Condition 을 내 면 됩 니 다.이렇게 하면 프로그램 운행 의 효율 을 향상 시 키 는 데 도 도움 이 됩 니 다.Condition 을 여러 개 사용 하 는 장면 은 흔히 볼 수 있 는데 Array Blocking Queue 에 있 습 니 다.
레 퍼 런 스
《자바 가상 머 신 깊이 들 어가 기》
http://www.cnblogs.com/dolphin0520/p/3923167.html