자바 하이 병렬 프로그래밍: 여러 개의 루트 간에 데이터를 공유하는 방식 연구

내용 요약


여러 스레드 간에 데이터를 공유하고 각 스레드의 실행 코드가 같은지 여부에 따라 우리는 서로 다른 처리 방식을 취할 수 있다. 여기서 간단한 매표 예시를 통해 각 스레드가 같은 코드를 집행할 때 여러 스레드가 서로 다른 코드를 집행하는 상황에 대해 처리 방식이 비교적 유연하다는 것을 설명한다. 여기서 주로 두 가지 방식을 소개했고 두 가지 방식의 비교와 귀납을 통해우리는 여러 라인이 서로 다른 코드를 집행하는 상황에서 어떻게 코드의 설계를 진행하는지 총결해 낼 수 있다

1. 각 라인이 실행하는 코드가 같으면


같은 Runnable 대상을 사용할 수 있습니다. 이 Runnable 대상에 공유 데이터가 있습니다. 예를 들어 매표 시스템

1.1 간단한 매표 시스템의 예

class Ticket implements Runnable{  
    private  int tick = 20;  
    Object obj = new Object();  

    public void run(){  
        while(true){  
            synchronized(obj){  
                if(tick>0){  
                    //  try,  run    Runnable   run,   run     
                    //try{Thread.sleep(10);}catch(Exception e){}  
                    System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  
                }  
            }  
        }  
    }  
}  

class  TicketDemo  
{  
    public static void main(String[] args) {  

        //      Ticket  ,       tick    ,         
        Ticket t = new Ticket();  

        Thread t1 = new Thread(t);  
        Thread t2 = new Thread(t);  
        Thread t3 = new Thread(t);  
        Thread t4 = new Thread(t);  
        t1.start();  
        t2.start();  
        t3.start();  
        t4.start();  
    }  
} 
Thread-0....sale : 20
Thread-0....sale : 19
Thread-0....sale : 18
Thread-0....sale : 17
Thread-0....sale : 16
Thread-0....sale : 15
Thread-0....sale : 14
Thread-0....sale : 13
Thread-0....sale : 12
Thread-3....sale : 11
Thread-3....sale : 10
Thread-3....sale : 9
Thread-3....sale : 8
Thread-3....sale : 7
Thread-3....sale : 6
Thread-3....sale : 5
Thread-3....sale : 4
Thread-3....sale : 3
Thread-3....sale : 2
Thread-3....sale : 1

2. 각 스레드가 실행하는 코드가 다르면


이럴 때 서로 다른 Runnable 대상을 사용할 필요가 없다. 다음과 같은 두 가지 방식으로 이러한 Runnable 대상 간의 데이터 공유를 실현한다.

2.1 방식 1


공유 데이터를 다른 대상에 봉인한 다음 이 대상을 하나하나 Runnable 대상에게 전달합니다.모든 라인이 공유 데이터에 대한 조작 방법도 그 대상에게 분배하여 완성하면 이 데이터에 대한 각 조작의 상호 배척과 통신을 실현하기 쉽다.
사상: 하나의 클래스가 데이터와 조작 데이터를 제공하는 동기화 방법, 그리고 두 개의 스레드가 구조 함수를 통해 데이터를 수신하고 조작하는 것을 정의하여 주 함수에서 직접 스레드 대상을 만들면 조작을 완성할 수 있다.

2.2 방법2


이러한 Runnable 대상을 특정한 클래스의 내부 클래스로 하고 공유 데이터를 이 외부 클래스의 구성원 변수로 하며 모든 루트가 공유 데이터에 대한 조작 방식도 외부 클래스에 분배하여 공유 데이터에 대한 각 조작의 상호 배척과 통신을 실현하고 내부 클래스의 각 Runnable 대상으로 외부 클래스를 호출하는 이런 방법들이다.
사상: 한 외부 클래스에 두 개의 내부 클래스가 있는데 이 두 내부 클래스가 데이터를 공유하도록 외부 클래스의 같은 구성원을 모두 조작하도록 한다. 방법과 데이터는 모두 이 구성원에게 있다. 직접 방법을 호출하면 데이터의 조작을 완성할 수 있다.

2.3 방법 3: 위의 두 방식을 조합하여


공유 데이터를 다른 대상에 봉인하고 모든 라인이 공유 데이터에 대한 조작 방법도 그 대상에게 분배하여 완성한다. 대상은 이 외부 클래스의 구성원 변수나 방법 중의 국부 변수이고 각 라인의 Runnable의 대상은 외부 클래스의 구성원 내부 클래스나 국부 외부 클래스이다.

2.4 기술 요약


동기화하여 서로 밀어내는 몇 단락의 코드는 각각 몇 개의 독립된 방법에 두는 것이 가장 좋고 이런 방법은 같은 종류에 두는 것이 비교적 쉽다. 이렇게 하면 그것들 간의 동기화 서로 밀어내거나 통신을 실현하기 쉽다.

2.5 각 라인이 실행하는 코드마다 다른 3가지 방식에 대해 한 면접문제를 통해 설명한다


수요: 4개의 라인을 설계하는데 그 중에서 두 라인은 매번 j에 1씩 증가하고 다른 두 라인은 매번 j에 1씩 감소하며 프로그램을 작성한다.

사용 방법 1 구현


데이터와 조작 공유 데이터의 방법을 한 클래스에 봉하여 두 개의runnable 실현 클래스를 정의하고 두 개의runnable가 모두 공유 데이터의 인용을 가지도록 한다.runnable의 구조 함수에서 직접 조작으로 전송하고 실현 클래스의run 방법에서 봉인 클래스를 호출하는 방법
public class MultyThreadShareMethod1 {  

    public static void main(String[] args){  

        //           ,  
        ShareData2 data1 = new ShareData2();  

        // runnable               
        for(int i=0;i<2;i++){  
        new Thread(new MyRunnable1(data1)).start();  
        new Thread(new MyRunnable2(data1)).start();  
        }  
    }  
}  

//                   
class ShareData2{  
    private int j = 10;  
    public synchronized void increment() {  
        j++;  
        System.out.println(Thread.currentThread().getName()+" inc : "+j);  
    }  
    public synchronized void decrement() {  
        j--;  
        System.out.println(Thread.currentThread().getName()+" dec : "+j);  
    }  
}  

//     ,            
class MyRunnable1 implements Runnable {  

    private ShareData2 data;  
    public MyRunnable1(ShareData2 data) {  
        this.data = data;  
    }  
    @Override  
    public void run() {  
        for(int i=0;i<10;i++){  
        data.increment();  
        }  
    }  
}  

//     ,            
class MyRunnable2 implements Runnable {   
    private ShareData2 data;  
    public MyRunnable2(ShareData2 data) {  
        this.data = data;  
    }  
    @Override  
    public void run() {  
        for(int i=0;i<10;i++){  
        data.decrement();  
        }  
    }  
}  

결과 내보내기
Thread-0 inc : 11
Thread-0 inc : 12
Thread-0 inc : 13
Thread-0 inc : 14
Thread-0 inc : 15
Thread-0 inc : 16
Thread-0 inc : 17
Thread-0 inc : 18
Thread-0 inc : 19
Thread-0 inc : 20
Thread-1 dec : 19
Thread-3 dec : 18
Thread-3 dec : 17
Thread-3 dec : 16
Thread-3 dec : 15
Thread-3 dec : 14
Thread-3 dec : 13
Thread-3 dec : 12
Thread-3 dec : 11
Thread-3 dec : 10
Thread-3 dec : 9
Thread-2 inc : 10
Thread-2 inc : 11
Thread-1 dec : 10
Thread-1 dec : 9
Thread-1 dec : 8
Thread-1 dec : 7
Thread-1 dec : 6
Thread-1 dec : 5
Thread-1 dec : 4
Thread-1 dec : 3
Thread-1 dec : 2
Thread-2 inc : 3
Thread-2 inc : 4
Thread-2 inc : 5
Thread-2 inc : 6
Thread-2 inc : 7
Thread-2 inc : 8
Thread-2 inc : 9
Thread-2 inc : 10

사용 방법 2 구현


데이터와 조작으로 데이터를 공유하는 방법을 하나의 클래스에 봉인하다
두 개의runnable는 내부 클래스로 방식 1에 비해 데이터를 runnable에 전달하지 않고 스스로 가져가서 자신의run 방법에서 데이터를 조작하는 방법을 사용합니다
이곳의 공유 변수는 정적 유형의 구성원 변수로 정의할 수도 있고final 유형의 국부 변수로 정의할 수도 있다.
public class MultyThreadShareData {  

    //                
    //private static ShareData data = new ShareData();  
    public static void main(String[] args){  

        //      final         
        final ShareData data = new ShareData();  

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

        //       
        new Thread(new Runnable(){  
            @Override  
            public void run() {  
                for(int i=0;i<100;i++){  
                    data.increment();  
                    }  
            }  
        }).start();  
        //       
        new Thread(new Runnable(){  
            @Override  
            public void run() {  
                for(int i=0;i<100;i++){  
                    data.decrement();  
                }  
            }  
        }).start();  
        }  
    }  
}  
//                   
class ShareData{  
    private int j = 0;  
    public synchronized void increment() {  
        j++;  
        System.out.println(Thread.currentThread().getName()+" inc : "+j);  
    }  
    public synchronized void decrement() {  
        j--;  
        System.out.println(Thread.currentThread().getName()+" dec : "+j);  
    }  
}  

두 가지 방식의 조합 실현

public class MultyThreadShareDataTest {  

private int j;   
public static void main(String args[]){   
    MultyThreadShareDataTest tt=new MultyThreadShareDataTest();   
        Inc inc=tt.new Inc();   
        Dec dec=tt.new Dec();   

    for(int i=0;i<2;i++){   
        Thread t=new Thread(inc);   
       t.start();  

        t=new Thread(dec);   
       t.start();   
       }   
   }   

    private synchronized void inc(){   
       j++;   
       System.out.println(Thread.currentThread().getName()+"-inc:"+j);   
       }   
    private synchronized void dec(){   
       j--;   
       System.out.println(Thread.currentThread().getName()+"-dec:"+j);   
       }   

    class Inc implements Runnable{   
       public void run(){   
           for(int i=0;i<100;i++){   
           inc();   
           }   
       }   
    }  

    class Dec implements Runnable{   
       public void run(){   
           for(int i=0;i<100;i++){   
           dec();   
           }   
       }   
    }   
}  

좋은 웹페이지 즐겨찾기