Java 병렬 프로 그래 밍:Lock&ReentrantLock&Condition

14165 단어 자바 다 중 루틴
자바 에 서 는 synchronized 키 워드 를 사용 하여 스 레 드 동기 화 를 실현 하 는 것 외 에 자바 util.concurrent.locks 패키지 의 재 입 자물쇠(ReentrantLock)를 사용 하여 동기 화 를 실현 할 수 있 습 니 다.오늘 은 ReentrantLock 동기 화 를 배 우 러 왔 습 니 다.
다음은 본 고 에 포 함 된 지식 점 이다.
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

좋은 웹페이지 즐겨찾기