Java 학습노트24 - 스레드의 다중 스레드 안전, 잠금, 동기화 메커니즘

12620 단어
본고의 주요 내용 1. 다중 라인 안전 문제 2. 깨우기 메커니즘
01라인 보안
  • A:스레드 안전 문제가 다중 스레드를 유발하고 같은 데이터 자원을 방문할 때 스레드가 휴면하면 스레드 안전 문제
    /*
     * 3   ,      ,  
     */
    public class ThreadDemo {
     public static void main(String[] args) {
     //  Runnable       
     Tickets t = new Tickets();
     //  3 Thread   ,  Runnable     
     Thread t0 = new Thread(t);
     Thread t1 = new Thread(t);
     Thread t2 = new Thread(t);
     
     t0.start();
     t1.start();
     t2.start();
     
     }
    }
    
    /*
     *        ,      
     */
    public class Tickets implements Runnable{
    
     //       
     private int ticket = 10;
     private Object obj = new Object();
    
     public void run(){
     while(true){
    
       //     ,  0,    ,  --  
         if( ticket > 0){
           try{
              Thread.sleep(10); //              
           }catch(Exception ex){}
           System.out.println(Thread.currentThread().getName()+"     "+ticket--);
         }
     }
     }
    }
    
  • 를 초래할 수 있다.
    출력 결과: 창구 3 매표 10창구 2 매표 9창구 1 매표 8창구 1 매표 7창구 3 매표 7창구 2 매표 6창구 3 매표 5창구 1 매표 5창구 2 매표 4창구 3 매표 3창구 2 매표 2창구 1 매표 3창구 3 매표 3창구 3 매표 1창구 1 매표 0창구 2 매표 1창구 2 매표 1창구 1창구 1창구 1 매표 1창구 1창구 1창구 1에 다음과 같은 문제가 존재합니다. 표에 중복된 표 오류가 발생했습니다.표 0,-1은 여러 개의 라인이 변수에 대해 쓰기 작업을 동시에 수행하여 발생하는 라인 안전 문제입니다. 이 문제를 해결하려면 라인의 동기화를 고려해야 합니다.
  • B:동기 코드 블록은 라인 안전 문제가 라인 휴면을 통해 발생하는 안전 문제를 해결한다. 해결 방안: 동기 코드 블록 공식:synchronized(임의의 대상) {라인이 조작할 공유 데이터}
        /*
         * 3   ,      ,  
         */
        public class ThreadDemo {
         public static void main(String[] args) {
           //  Runnable       
           Tickets t = new Tickets();
           //  3 Thread   ,  Runnable     
           Thread t0 = new Thread(t);
           Thread t1 = new Thread(t);
           Thread t2 = new Thread(t);
           
           t0.start();
           t1.start();
           t2.start();
           
         }
        }
      
        public class Tickets implements Runnable{
         
         //       
         private int ticket = 100;
         private Object obj = new Object();
         
         public void run(){
           while(true){
             //      ,    ,       
             synchronized(obj){
             //     ,  0,    ,  --  
               if( ticket > 0){
                 try{
                    Thread.sleep(10);
                 }catch(Exception ex){}
                 System.out.println(Thread.currentThread().getName()+"      "+ticket--);
               }
             }
           }
         }
        }
    
  • 출력 결과: 창구 1 매표 10 창구 1 매표 9 창구 1 매표 8 창구 1 매표 7 창구 1 매표 6 창구 1 매표 5 창구 1 매표 4 창구 1 매표 3 창구 1 매표 2 창구 1 매표 1 매표 1 매표 1
  • C: 동기화 코드 블록의 실행 원리 동기화 코드 블록: 코드 블록 성명에synchronized synchronized(잠금 대상) {라인 보안 문제가 발생할 수 있는 코드} 동기화 코드 블록의 잠금 대상은 임의의 대상이 될 수 있다.그러나 여러 개의 라인이 있을 때 같은 자물쇠 대상을 사용해야만 라인의 안전을 보장할 수 있다.
  • D: 동기화 방법의 사용 동기화 방법은 스레드 공유 데이터, 동기화를 한 방법으로 추출하는 것이다.방법의 성명에 동기화 키워드를 추가하는 일반적인 방법은 동기화 코드 블록과 마찬가지로 자물쇠가 있고 대상 자물쇠는 본 클래스의 대상의 인용이다. 즉,this동기화 정적 방법의 대상 자물쇠는 본 클래스의 이름이다.class 속성
     public class Tickets implements Runnable{
    
    //       
    private  int ticket = 100;
    
    public void run(){
      while(true){
        payTicket();
      }
    }
    
    public  synchronized void payTicket(){  
        if( ticket > 0){
          try{
             Thread.sleep(10);
          }catch(Exception ex){}
          System.out.println(Thread.currentThread().getName()+"      "+ticket--);
        }
      
    }
     }
    
  • 출력 결과: 창구2는 티켓팅 10창구 2는 티켓팅 9창구 2는 티켓팅 8창구 2는 티켓팅 7창구 2는 티켓팅 6창구 2는 티켓팅 5창구 2는 티켓팅 4창구 2는 티켓팅 3창구 2는 티켓팅 2창구 2는 티켓팅 1입니다.
    02 Lock 커넥터
  • A:JDK1.5 새로운 기능 Lock 인터페이스 Lock 인터페이스는 synchronized 방법과 문장을 사용하는 것보다 대상을 향한 자물쇠를 제공합니다. 이 자물쇠에 더 많은 조작 자물쇠 기능을 제공합니다.Lock 인터페이스의 일반적인 방법인 void lock () void unlock () 은 Lock 인터페이스를 사용하고, 그 중의 lock () 방법과 unlock () 방법은 동기화
  • 를 대체할 수 있습니다.
  • B: Lock 커넥터 사용
    /*
     * 3   ,      ,  
     */
    public class ThreadDemo {
      public static void main(String[] args) {
        //  Runnable       
        Tickets t = new Tickets();
        //  3 Thread   ,  Runnable     
        Thread t0 = new Thread(t);
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        
        t0.start();
        t1.start();
        t2.start();
        
      }
    }
    
    public class Tickets implements Runnable{
      
      //       
      private int ticket = 100;
      //       ,  Lock        
      private Lock lock = new ReentrantLock();
      
      public void run(){
        while(true){
          //  Lock    lock   
            lock.lock();
          //     ,  0,    ,  --  
            if( ticket > 0){
              try{
                 Thread.sleep(10);
                 System.out.println(Thread.currentThread().getName()+"      "+ticket--);
              }catch(Exception ex){
                
              }finally{
                //   ,  Lock    unlock
                lock.unlock();
              }
            }
        }
      }
    }
    
  • 03 라인 잠금 해제
  • A:라인의 사라진 자물쇠 원리는 라인 작업에 여러 개의 동기화(여러 개의 자물쇠)가 생겼을 때 동기화에 다른 동기화가 끼워져 있다면.이때 프로그램이 무한히 기다리는 현상을 일으키기 쉽다. 이런 현상을 우리는 사쇄라고 부른다.
        synchronzied(A ){
          synchronized(B ){
                    
          }
      }
    
  • B:라인의 잠금 코드 구현
     public class DeadLock implements Runnable{
      private int i = 0;
      public void run(){
        while(true){
          if(i%2==0){
            //   A  ,   B  
            synchronized(LockA.locka){
              System.out.println("if...locka");
              synchronized(LockB.lockb){
                System.out.println("if...lockb");
              }
            }
          }else{
            //   B  ,   A  
            synchronized(LockB.lockb){
              System.out.println("else...lockb");
              synchronized(LockA.locka){
                System.out.println("else...locka");
              }
            }
          }
          i++;
        }
      }
     }
    
    public class DeadLockDemo {
      public static void main(String[] args) {
        DeadLock dead = new DeadLock();
        Thread t0 = new Thread(dead);
        Thread t1 = new Thread(dead);
        t0.start();
        t1.start();
      }
    }
    
    
    public class LockA {
      private LockA(){}
      
      public  static final LockA locka = new LockA();
    }
    
    
    public class LockB {
      private LockB(){}
      
      public static final LockB lockb = new LockB();
    }
    
  • 04 스레드 대기 및 깨우기
  • A:스레드 대기와 깨우기 대기 깨우기 메커니즘에 관련된 방법:wait(): 대기, 실행 중인 스레드를 실행 자격과 실행권을 방출하고 스레드 탱크에 저장합니다.notify (): 깨우기, 깨우기 연못에 있는wait ()의 라인을 한 번에 깨우기, 임의로 깨우기.깨우기: 즉, 스레드 탱크의 스레드가 실행 자격을 갖추도록 합니다. notify All (): 모든 스레드를 깨우기: 스레드 탱크의 모든wait () 스레드를 깨울 수 있습니다.상술한 방법은 모두 동기화 중이어야만 유효하다.동시에 이런 방법은 사용할 때 반드시 소속 자물쇠를 표시해야만 이런 방법이 도대체 어느 자물쇠의 라인을 조작했는지 명확하게 할 수 있다.
  • B:사례
    /*
     *       , 2     
     *  name,sex
     *     2   ,         
     *  1  name,age  
     *  2  name,age        
     */
    public class Resource {
    public String name;
    public String sex;
    }
    
    
     /*
     *       ,     Resource       
     *         , 
     *        lisi,nv
     */
    public class Input implements Runnable {
      private Resource r=new Resource();
     
      public void run() {
        int i=0;
        while(true){
          if(i%2==0){
             r.name="  ";
             r.sex=" ";
           }else{
              r.name="lisi";
              r.sex=" ";
            }
          i++;
        }
      }
    }
    
    /*
     *      ,     Resource     ,   
     */
    public class Output implements Runnable {
      private Resource r=new Resource() ;
       
      public void run() {
        while(true){
           System.out.println(r.name+"..."+r.sex); 
          }
        }
    }
    
    
    /*
     *             ,        
     */
    public class ThreadDemo{
      public static void main(String[] args) {
        
        Resource r = new Resource();
        
        Input in = new Input();
        Output out = new Output();
        
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);
        
        tin.start();
        tout.start();
      }
    }
    
  • 위 프로그램이 실행되면 출력 결과에서 Resource 객체에 null 값이 많습니다.null null.....null null.....null null.....null null.....null null.....null null.....null null.....null null.....null .....
    상기 Null 값 해결, 스레드 대기 및 깨우기 사례 null 값 해결
        /*
        *       ,     Resource       
        *         , 
        *        lisi,nv
      */
       public class Input implements Runnable {
         private Resource r;
         public Input(Resource r){
           this.r=r;
         }
        
         public void run() {
           int i=0;
           while(true){
             if(i%2==0){
                r.name="  ";
                r.sex=" ";
              }else{
                 r.name="  "
                 r.sex=" "
               }
             i++;
           }
         }
       }
    
       /*
        *      ,     Resource     ,   
        */ 
       public class Output implements Runnable {
         private Resource r;
         public Output(Resource r){
            this.r=r;
         } 
         public void run() {
           while(true){
              System.out.println(r.name+"..."+r.sex); 
             }
           }
         }
    
       }
       /*
        *             ,        
        */
       public class ThreadDemo{
         public static void main(String[] args) {
           
           Resource r = new Resource();
           
           Input in = new Input(r);
           Output out = new Output(r);
           
           Thread tin = new Thread(in);
           Thread tout = new Thread(out);
           
           tin.start();
           tout.start();
         }
       }
    

    이상 프로그램이 실행되고 출력된 결과 교차 착란대왕이 발견됩니다......남왕후......여왕 후......남대왕......여왕 후......여왕 후......여대왕......여왕 후......남왕후......남왕후......남대왕......여왕 후......남대왕......남자...솔루션:동기화 코드 블록 사용
            /*
              *       ,     Resource       
              *         , 
              *        lisi,nv
            */
             public class Input implements Runnable {
               private Resource r;
               public Input(Resource r){
                 this.r=r;
               }
              
               public void run() {
                 int i=0;
                 while(true){
                  synchronized(r){
                   if(i%2==0){
                       r.name="  ";
                       r.sex=" ";
                  }else{
                       r.name="  "
                       r.sex=" "
                     }
                   i++;
                 }
    
               }
             }
    
             /*
              *      ,     Resource     ,   
              */ 
             public class Output implements Runnable {
               private Resource r;
               public Output(Resource r){
                  this.r=r;
               } 
               public void run() {
                 while(true){
                    synchronized(r){
                     System.out.println(r.name+"..."+r.sex); 
                    }
                   }
                 }
               }
    
             }
             /*
              *             ,        
              */
             public class ThreadDemo{
               public static void main(String[] args) {
                 
                 Resource r = new Resource();
                 
                 Input in = new Input(r);
                 Output out = new Output(r);
                 
                 Thread tin = new Thread(in);
                 Thread tout = new Thread(out);
                 
                 tin.start();
                 tout.start();
               }
             }
    
          ,         ,     
    

    ...남대왕......남대왕......남대왕......남대왕......남대왕......남대왕......남대왕......남자...해결 방안: 라인 대기 및 깨우기 입력 사용: 값을 부여한 후 실행 방법wait()는 출력을 영원히 기다린다. 변수 값은 출력을 출력하기 전에 입력한 notify()를 깨우고, 자신은wait()에서 입력을 영원히 기다린다. 깨어난 후 변수에 다시 값을 부여한다. 값을 부여한 후 출력된 라인 notify(), 자신의wait()를 깨워야 한다.
     /*
      *       , 2     
      *  name,sex
      *     2   ,         
      *  1  name,age  
      *  2  name,age        
      */
     public class Resource {
      public String name;
      public String sex;
      public boolean flag = false;
     }
    
     /*
      *       ,     Resource       
      *         , 
      *        lisi,nv
      */
     public class Input implements Runnable {
      private Resource r ;
      
      public Input(Resource r){
        this.r = r;
      }
      
      public void run() {
        int i = 0 ;
        while(true){
          synchronized(r){
            //   true,  
              if(r.flag){
                try{r.wait();}catch(Exception ex){}
              }
            
            if(i%2==0){
                r.name="  ";
                r.sex=" ";
              }else{
                 r.name="  "
                 r.sex=" "
            }
            //       ,    true
            r.flag = true;
            r.notify();
          }
          i++;
        }
      }
    
     }
     
     /*
      *      ,     Resource     ,   
      */
     public class Output implements Runnable {
      private Resource r ;
      
      public Output(Resource r){
        this.r = r;
      }
      public void run() {
        while(true){
          synchronized(r){  
            //    , false,  
          if(!r.flag){
            try{r.wait();}catch(Exception ex){}
            }
          System.out.println(r.name+".."+r.sex);
          //    false,      
          r.flag = false;
          r.notify();
          }
        }
      }
    
     }
    
     /*
      *             ,        
      */
     public class ThreadDemo{
      public static void main(String[] args) {
        
        Resource r = new Resource();
        
        Input in = new Input(r);
        Output out = new Output(r);
        
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);
        
        tin.start();
        tout.start();
      }
     }
    

    좋은 웹페이지 즐겨찾기