자바 노트 - 스 레 드 동기 화 (7 가지 동기 화 방식)

26939 단어 자바
스 레 드 동기 화 (7 가지 방식)
 --친구 가 이 글 을 전재 하고 싶 으 면 전재 주 소 를 밝 혀 주세요. "http://www.cnblogs.com/XHJT/p/3897440.html" 감사합니다. 
동기 화 를 왜 사용 합 니까?    자바 는 다 중 스 레 드 병행 통 제 를 허용 합 니 다. 여러 스 레 드 가 공유 가능 한 자원 변 수 를 동시에 조작 할 때 (예 를 들 어 데이터 의 추가 삭제 검사),    데이터 가 정확 하지 않 고 서로 충돌 할 수 있 으 므 로 이 스 레 드 가 조작 되 기 전에 다른 스 레 드 에 의 해 호출 되 지 않도록 동기 화 자 물 쇠 를 추가 합 니 다.    이로써 이 변수의 유일 성과 정확성 을 확보 했다.
 
1. 동기 화 방법    synchronized 키 워드 를 수식 하 는 방법 이 있 습 니 다.    자바 의 모든 대상 에 내 장 된 자물쇠 가 있 기 때문에 이 키워드 로 수식 할 때,    내 장 된 자 물 쇠 는 모든 방법 을 보호 할 수 있다.이 방법 을 호출 하기 전에 내 장 된 자 물 쇠 를 가 져 와 야 합 니 다. 그렇지 않 으 면 차단 상태 에 있 습 니 다.
    코드:    public synchronized void save(){}
   주: synchronized 키워드 도 정적 방법 을 수식 할 수 있 습 니 다. 이 정적 방법 을 사용 하면 전체 클래스 가 잠 겨 있 습 니 다.
 
2. 동기 코드 블록    synchronized 키 워드 를 수식 하 는 구문 블록 입 니 다.    이 키워드 에 의 해 수 식 된 문장 블록 은 자동 으로 내 장 된 자 물 쇠 를 추가 하여 동기 화 를 실현 한다
    코드:    synchronized(object){     }
    주: 동기 화 는 비용 이 많이 드 는 작업 이기 때문에 동기 화 된 내용 을 최대한 줄 여야 합 니 다.    일반적으로 전체 방법 을 동기 화 할 필요 가 없 으 며, synchronized 코드 블록 을 사용 하여 키 코드 를 동기 화하 면 됩 니 다.         코드 인 스 턴 스:    
package com.xhj.thread;



    /**

     *        

     * 

     * @author XIEHEJUN

     * 

     */

    public class SynchronizedThread {



        class Bank {



            private int account = 100;



            public int getAccount() {

                return account;

            }



            /**

             *        

             * 

             * @param money

             */

            public synchronized void save(int money) {

                account += money;

            }



            /**

             *         

             * 

             * @param money

             */

            public void save1(int money) {

                synchronized (this) {

                    account += money;

                }

            }

        }



        class NewThread implements Runnable {

            private Bank bank;



            public NewThread(Bank bank) {

                this.bank = bank;

            }



            @Override

            public void run() {

                for (int i = 0; i < 10; i++) {

                    // bank.save1(10);

                    bank.save(10);

                    System.out.println(i + "     :" + bank.getAccount());

                }

            }



        }



        /**

         *     ,     

         */

        public void useThread() {

            Bank bank = new Bank();

            NewThread new_thread = new NewThread(bank);

            System.out.println("  1");

            Thread thread1 = new Thread(new_thread);

            thread1.start();

            System.out.println("  2");

            Thread thread2 = new Thread(new_thread);

            thread2.start();

        }



        public static void main(String[] args) {

            SynchronizedThread st = new SynchronizedThread();

            st.useThread();

        }



    }

     3. 특수 도 메 인 변수 (volatile) 를 사용 하여 스 레 드 동기 화
    a. volatile 키 워드 는 도 메 인 변수의 방문 에 잠 금 면제 체 제 를 제공 합 니 다.    b. volatile 수식 도 메 인 을 사용 하면 가상 컴퓨터 에 이 도 메 인 이 다른 스 레 드 에 의 해 업 데 이 트 될 수 있 음 을 알려 주 는 것 과 같 습 니 다.    c. 따라서 이 도 메 인 을 사용 할 때마다 레지스터 의 값 을 사용 하 는 것 이 아니 라 다시 계산 해 야 합 니 다.    d. volatile 은 원자 조작 을 제공 하지 않 고 final 형식의 변 수 를 수식 할 수 없습니다.        예 를 들 면:        위의 예 에서 account 앞 에 volatile 수식 을 추가 하면 스 레 드 동기 화 를 실현 할 수 있 습 니 다.        코드 인 스 턴 스:    
      //

        class Bank {

            //         volatile

            private volatile int account = 100;



            public int getAccount() {

                return account;

            }

            //      synchronized 

            public void save(int money) {

                account += money;

            }

        }

    주: 다 중 스 레 드 의 비 동기 화 문 제 는 주로 도 메 인 에 대한 읽 기와 쓰기 에 나타 납 니 다. 도 메 인 자체 가 이 문 제 를 피 할 수 있 으 면 이 도 메 인 을 조작 하 는 방법 을 수정 할 필요 가 없습니다.    final 도 메 인 을 사용 하면 잠 금 보호 도 메 인과 volatil e 도 메 인 이 동기 화 되 지 않 는 문 제 를 피 할 수 있 습 니 다.    4. 재 접속 잠 금 으로 스 레 드 동기 화
    자바 SE 5.0 에 자바 util. concurrent 패 키 지 를 추가 하여 동기 화 를 지원 합 니 다.    ReentrantLock 류 는 다시 들 어 갈 수 있 고 서로 배척 할 수 있 으 며 Lock 인터페이스의 자 물 쇠 를 실현 합 니 다.    그것 은 synchronized 방법 과 빠 른 것 을 사용 하 는 것 과 같은 기본 적 인 행위 와 의 미 를 가지 고 그 능력 을 확장 시 켰 다.
    ReenreantLock 류 의 일반적인 방법 은 다음 과 같 습 니 다.
        ReentrantLock (): ReentrantLock 인 스 턴 스 를 만 듭 니 다.        lock (): 자물쇠 획득        잠 금 해제 (): 잠 금 해제    주: ReentrantLock () 은 공정 한 자 물 쇠 를 만 들 수 있 는 구조 방법 도 있 지만 프로그램 운행 효율 을 대폭 낮 출 수 있 기 때문에 사용 하 는 것 을 추천 하지 않 습 니 다.            예 를 들 면:        위의 예 를 바탕 으로 고 친 코드 는 다음 과 같다.            코드 인 스 턴 스:    
//

        class Bank {

            

            private int account = 100;

            //       

            private Lock lock = new ReentrantLock();

            public int getAccount() {

                return account;

            }

            //      synchronized 

            public void save(int money) {

                lock.lock();

                try{

                    account += money;

                }finally{

                    lock.unlock();

                }

                

            }

        }

              주: Lock 대상 과 synchronized 키워드 에 대한 선택:        a. 둘 다 사용 하지 않 고 자바 util. concurrent 패키지 가 제공 하 는 메커니즘 을 사용 하 는 것 이 좋 습 니 다.            사용자 가 자물쇠 와 관련 된 모든 코드 를 처리 하 는 데 도움 을 줄 수 있 습 니 다.        b. synchronized 키워드 가 사용자 의 수 요 를 만족 시 킬 수 있다 면 synchronized 를 사용 하 십시오. 코드 를 간소화 할 수 있 기 때 문 입 니 다.        c. 더 높 은 기능 이 필요 하 다 면 ReentrantLock 류 를 사용 합 니 다. 이때 자 물 쇠 를 제때에 풀 어야 합 니 다. 그렇지 않 으 면 자물쇠 가 생 길 수 있 습 니 다. 보통 finally 코드 에서 자 물 쇠 를 풀 어야 합 니 다.        5. 부분 변 수 를 사용 하여 스 레 드 동기 화 실현    ThreadLocal 관리 변 수 를 사용 하면 이 변 수 를 사용 하 는 모든 스 레 드 는 이 변수의 사본 을 얻 습 니 다.    던 전 간 에 서로 독립 되 어 있 습 니 다. 그러면 모든 스 레 드 는 자신의 변수 던 전 을 마음대로 수정 할 수 있 고 다른 스 레 드 에 영향 을 주지 않 습 니 다.
 
    ThreadLocal 클래스 의 일반적인 방법
 
    ThreadLocal (): 스 레 드 로 컬 변 수 를 만 듭 니 다.    get (): 이 스 레 드 부분 변수의 현재 스 레 드 던 전의 값 을 되 돌려 줍 니 다.    initialValue (): 이 스 레 드 부분 변수의 현재 스 레 드 의 "초기 값" 을 되 돌려 줍 니 다.    set (T value): 이 스 레 드 부분 변수의 현재 스 레 드 던 전의 값 을 value 로 설정 합 니 다.
 
    예 를 들 면:        위의 예 를 바탕 으로 수 정 된 코드 는 다음 과 같다.            코드 인 스 턴 스:        
//  Bank ,       

        public class Bank{

            //  ThreadLocal       account

            private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){

                @Override

                protected Integer initialValue(){

                    return 100;

                }

            };

            public void save(int money){

                account.set(account.get()+money);

            }

            public int getAccount(){

                return account.get();

            }

        }

    주: ThreadLocal 과 동기 화 메커니즘        a. ThreadLocal 과 동기 화 체 제 는 다 중 스 레 드 에서 같은 변수의 접근 충돌 문 제 를 해결 하기 위해 서 입 니 다.        b. 전 자 는 '공간 으로 시간 을 바꾼다' 는 방법 을 사용 하고 후 자 는 '시간 으로 공간 을 바꾼다' 는 방식 을 사용한다.
 
 
 
6. 차단 대기 열 을 사용 하여 스 레 드 동기 화
 
    앞의 5 가지 동기 화 방식 은 모두 밑바닥 에서 실 현 된 라인 동기 화 이지 만 우 리 는 실제 개발 에서 밑바닥 구 조 를 최대한 멀리 해 야 한다.     javaSE 5.0 버 전에 추 가 된 java. util. concurrent 패 키 지 를 사용 하면 개발 을 간소화 하 는 데 도움 이 될 것 입 니 다.     이 소절 은 주로 링크 드 BlockingQueue < E > 를 사용 하여 스 레 드 의 동기 화 를 실현 합 니 다.     링크 드 BlockingQueue < E > 는 연 결 된 노드 를 기반 으로 하 는 범위 가 임의의 blocking queue 입 니 다.     대열 은 먼저 나 가 는 순서 (FIFO) 입 니 다. 대열 에 대해 서 는 나중에 자세히 설명 하 겠 습 니 다 ~        링크 드 BlockingQueue 클래스 상용 방법     LinkedBlockingQueue (): Integer. MAX VALUE 용량 의 LinkedBlockingQueue 만 들 기     put (E e): 줄 끝 에 요 소 를 추가 합 니 다. 대기 열 이 가득 차 면 차단 합 니 다.     size (): 대기 열 에 있 는 요소 개 수 를 되 돌려 줍 니 다.     take (): 헤더 요 소 를 제거 하고 되 돌려 줍 니 다. 대기 열 이 비어 있 으 면 차단 합 니 다.        코드 인 스 턴 스:         업 체 의 상품 생산 과 매매 상품 의 동기 화 를 실현 하 다.
 1 package com.xhj.thread;

 2 

 3 import java.util.Random;

 4 import java.util.concurrent.LinkedBlockingQueue;

 5 

 6 /**

 7  *             LinkedBlockingQueue   

 8  * 

 9  * @author XIEHEJUN

10  * 

11  */

12 public class BlockingSynchronizedThread {

13     /**

14      *                    

15      */

16     private LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();

17     /**

18      *         

19      */

20     private static final int size = 10;

21     /**

22      *          , 0 ,         ; 1 ,         

23      */

24     private int flag = 0;

25 

26     private class LinkBlockThread implements Runnable {

27         @Override

28         public void run() {

29             int new_flag = flag++;

30             System.out.println("     " + new_flag);

31             if (new_flag == 0) {

32                 for (int i = 0; i < size; i++) {

33                     int b = new Random().nextInt(255);

34                     System.out.println("    :" + b + " ");

35                     try {

36                         queue.put(b);

37                     } catch (InterruptedException e) {

38                         // TODO Auto-generated catch block

39                         e.printStackTrace();

40                     }

41                     System.out.println("       :" + queue.size() + " ");

42                     try {

43                         Thread.sleep(100);

44                     } catch (InterruptedException e) {

45                         // TODO Auto-generated catch block

46                         e.printStackTrace();

47                     }

48                 }

49             } else {

50                 for (int i = 0; i < size / 2; i++) {

51                     try {

52                         int n = queue.take();

53                         System.out.println("      " + n + "   ");

54                     } catch (InterruptedException e) {

55                         // TODO Auto-generated catch block

56                         e.printStackTrace();

57                     }

58                     System.out.println("       :" + queue.size() + " ");

59                     try {

60                         Thread.sleep(100);

61                     } catch (Exception e) {

62                         // TODO: handle exception

63                     }

64                 }

65             }

66         }

67     }

68 

69     public static void main(String[] args) {

70         BlockingSynchronizedThread bst = new BlockingSynchronizedThread();

71         LinkBlockThread lbt = bst.new LinkBlockThread();

72         Thread thread1 = new Thread(lbt);

73         Thread thread2 = new Thread(lbt);

74         thread1.start();

75         thread2.start();

76 

77     }

78 

79 }

 
주: BlockingQueue < E > 는 대기 열 을 막 는 데 자주 사용 되 는 방법 을 정의 합 니 다. 특히 세 가지 요 소 를 추가 하 는 방법 은 대기 열 이 가득 찼 을 때 주의해 야 합 니 다.
add () 방법 은 이상 을 던 집 니 다.
offer () 방법 false 되 돌리 기
put () 방법 이 막 힙 니 다.
 
 
7. 원자 변 수 를 사용 하여 스 레 드 동기 화 실현
 
스 레 드 동기 화 를 사용 해 야 하 는 근본 적 인 원인 은 일반 변수 에 대한 조작 이 원자 가 아니 기 때문이다.
원자 조작 이란 무엇 일 까요? 원자 조작 이란 변수 값 읽 기, 변수 값 수정, 변수 값 저장 을 하나의 전체 로 보 는 것 을 말 합 니 다. - 이 몇 가지 행 위 는 동시에 완성 되 거나 완료 되 지 않 습 니 다. 자바 의 util. concurrent. atomic 패키지 에 원자 형식 변 수 를 만 드 는 도구 류 를 제공 합 니 다. 이 종 류 를 사용 하면 스 레 드 동기 화 를 간소화 할 수 있 습 니 다. 그 중에서 AtomicIntege 는r 표 는 int 값 을 원자 방식 으로 업데이트 할 수 있 습 니 다. 프로그램 에서 (예 를 들 어 원자 방식 으로 증가 하 는 계수기) 사용 할 수 있 지만 Integer 를 교체 할 수 없습니다. Number 를 확장 할 수 있 습 니 다. 기회 디지털 류 를 처리 하 는 도구 와 유 틸 리 티 도 구 를 통일 적 으로 접근 할 수 있 습 니 다. AtomicInteger 류 의 일반적인 방법: AtomicInteger (int initialValue): 주어진 초기 값 을 가 진 새로운 AtomicInteger addGet (int dalta) 을 만 듭 니 다. 주어진 값 을 현재 값 과 추가 get (): 현재 값 코드 인 스 턴 스 가 져 오기: Bank 클래스 만 바 꾸 고 나머지 코드 는 위의 첫 번 째 예 와 같 습 니 다.
 1 class Bank {

 2         private AtomicInteger account = new AtomicInteger(100);

 3 

 4         public AtomicInteger getAccount() {

 5             return account;

 6         }

 7 

 8         public void save(int money) {

 9             account.addAndGet(money);

10         }

11

보충 - 원자 조작 은 주로 인용 변수 와 대부분의 원시 변수 (long 과 double 제외) 에 대한 읽 기와 쓰기, volatile 수식 을 사용 하 는 모든 변수 (long 과 double 포함) 에 대한 읽 기와 쓰기 동작 이 있 습 니 다.

좋은 웹페이지 즐겨찾기