Java 멀티스레드 스레드 동기화

10431 단어 java 다중 루틴
만약 변수를 쓰고 있다면, 그 다음에 다른 라인에서 읽히거나, 지난번에 다른 라인에서 쓴 변수를 읽고 있을 수도 있습니다. 동기화를 사용해야 하고, 읽기 라인은 같은 모니터 자물쇠로 동기화해야 합니다.Brain 동기화 규칙
 
synchronized
  • 모든 대상은 자동으로 단일한 자물쇠를 포함하고 한 대상의 임의의synchronized 방법을 호출할 때 이 대상은 잠깁니다.
  • 특정한 대상에게 모든synchronized 방법은 같은 자물쇠를 공유한다.따라서 어떤 스레드가 대상에 접근하는synchronized 방법을 사용할 때 다른 스레드가 대상에 접근하는synchronized 방법은 모두 막힐 것이다.
  • 한 임무는 여러 번 대상 자물쇠를 얻을 수 있다. 예를 들어 대상을 호출할 때 이 방법은 다른 방법을 호출한다.JVM은 객체가 잠긴 횟수를 추적합니다. 이 또한 잠긴 잠금의 리셋이 가능하며 리셋이 불가능한 잠금은 사라질 수 있습니다.
  • 클래스마다 자물쇠가 있고 클래스마다 클라스 대상이 있다.synchronizedstatic 방법은static 데이터에 대한 병렬 접근을 방지할 수 있다
  • 병발을 사용할 때 필드를private로 설정하여 라인이 필드에 직접 접근하는 것을 방지하는 것이 좋습니다.

  • 잠긴 ReentrantLock을 다시 입력할 수 있습니다.
    java에서.util.concurrent.locks 패키지에서 현식 Lock을 정의합니다. 이 Lock 자물쇠는 현식 생성, 잠금, 방출이 필요합니다.synchronized 키워드보다 현식 Lock 자물쇠는 사용이 번거롭고 사용 시 오류가 발생할 수 있지만 더욱 강력한 기능이 있습니다.locks 패키지에는 두 가지 자물쇠 Reentrant Lock과 Reentrant ReadWrite Lock이 포함되어 있습니다.
  • Reentrant ReadWrite Lock, 블로그를 보고 알아봤어요.루틴이 읽기 자물쇠에 들어가는 전제 조건은 (a) 다른 루틴의 쓰기 자물쇠가 없고 (b) 요청을 쓰지 않거나 쓰기 요청이 있지만 호출 루틴과 자물쇠를 가진 루틴은 동일합니다.라인이 자물쇠에 들어가는 전제 조건입니다. (a) 다른 라인의 자물쇠가 없고 (b) 다른 라인의 자물쇠가 없습니다.
  • ReentrantLock은 두 가지 구조가 있는데 ReentrantLock(boolean fair)fair는true에 생성된 자물쇠가 공평한 자물쇠라고 알려주고fair 파라미터에 비공평한 자물쇠를 만들지 않습니다.비공평한 자물쇠는 자물쇠를 직접 획득하고 대기 대기열을 유지하지 않습니다.공평한 자물쇠는 현재 라인이 대기 행렬의 첫 번째인지 확인해야 한다.
  • public class LockTest {
    
        private int current = 0;
    
        private Lock lock = new ReentrantLock();
    
        
    
        public int next(){
    
            //lock.lock();
    
            boolean flag = lock.tryLock();
    
            if(flag){
    
                try{
    
                    ++current;
    
                    Thread.yield();
    
                    ++current;
    
                    return current;
    
                }finally{
    
                    lock.unlock();
    
                }
    
            }else{
    
                System.out.println("locked");
    
                return -1;
    
            }
    
        }
    
    }

    디스플레이 자물쇠는 자물쇠를 가져오려고 시도할 수 있습니다.synchronized 키워드보다 더 유연한 곳입니다.tryLock (long timeout, Time Unit unit) 은 자물쇠를 가져올 때까지 기다릴 시간도 설정할 수 있습니다.Lock 잠금이 잘못될 가능성이 높기 때문에 특별한 문제를 해결하지 않으면 synchronized 키워드를 사용하면 됩니다.
    volatile
    volatile 키워드를 사용하면 기본 형식 변수의 값을 부여하고 원자 작업으로 되돌려줍니다.
    volatile는 성명된 필드가 가시적일 수 있습니다.가시성이란 필드에 쓰기 작업을 하기만 하면 모든 읽기 작업이 이 수정을 볼 수 있다는 것을 말한다.어떤 필드에 대한 스레드 수정 때문에 메모리에 직접적으로 나타나지 않을 수도 있고, 다른 스레드도 최신 값을 얻지 못할 수도 있습니다.
    volatile 키워드는 신중하게 사용해야 합니다. 라인 동기화가 필요할 때synchronized 키워드를 선택하십시오.
    원자류
    Java SE5는 Atomic Integer, Atomic Long, Atomic Reference 등 특수한 원자성 변수 클래스를 제공합니다.이러한 클래스는 boolean compareAndSet(int expect, int update) 형식으로 원자적 조건을 업데이트합니다.이 원자류들은 값을 가져오고 설정하는 데 사용되는 기본 get, set 작업이 있습니다.
    Atomic 클래스의 디자인은 주로java를 구축하는 데 사용된다.util.concurrent의 클래스는 일반적인 프로그래밍은 자물쇠를 사용하는 것이 안전하지만 성능 개선과 관련될 때 이런 원자류는 크게 쓸모가 있다.
    public class AtomicityTest implements Runnable {
    
        public static void main(String[] args) {
    
            // TODO Auto-generated method stub
    
            new Timer().schedule(new TimerTask() {
    
                
    
                @Override
    
                public void run() {
    
                    // TODO Auto-generated method stub
    
                    System.err.println("aborting");
    
                    System.exit(0);
    
                }
    
            }, 5000);
    
            ExecutorService exec = Executors.newCachedThreadPool();
    
            AtomicityTest at = new AtomicityTest();
    
            exec.execute(at);
    
            
    
            while(true){
    
                int val = at.getValue();
    
                if(val % 2 != 0){
    
                    System.out.println(val);
    
                    System.exit(0);
    
                }
    
            }
    
        }
    
        
    
        private AtomicInteger i = new AtomicInteger(0);
    
        public int getValue(){
    
            return i.get();
    
        }
    
        
    
        private void evenIncrement(){
    
            i.addAndGet(2);
    
            System.out.println(i);
    
        }
    
        
    
        @Override
    
        public void run(){
    
            while(true)
    
                evenIncrement();
    
        }
    
    }

    임계 구역과 기타 대상에서 동기화
    때때로 우리는 일부 코드가 여러 개의 라인에 동시에 접근하지 않기를 바랄 뿐이다. 이런 동기화된 코드 블록을 임계구역이라고 부른다.임계 구역은synchronized 키워드를 사용하여 만들 수 있습니다.
    synchronized(syncObject) {//임계 영역 코드}
    임계 부호 블록에 들어갈 때 syncObject 대상의 자물쇠를 가져와야 합니다.주의해야 할 것은 syncObject는 대상 자체인this일 수도 있고 다른 대상을 사용할 수도 있다는 것이다.synchronized 키워드를 소개할 때 모든 대상은 하나의 자물쇠를 포함하고 임계 구역은 대상 자물쇠를 통해 이루어진다고 말했다.이렇게 하면 여러 개의 라인이 같은 대상에 동시에 들어갈 수 있으며, 이 대상이 방문한 코드 블록이 서로 다른 대상 자물쇠를 사용하기만 하면 된다.
    ThreadLocal
    ThreadLocal은 변수에 대한 공유를 근절함으로써 다중 스레드에서 공유된 자원의 충돌을 방지합니다.ThreadLocal은 같은 변수를 사용하는 각 스레드마다 다른 저장소를 생성합니다.ThreadLocal 객체는 일반적으로 정적 필드로 저장됩니다.다음 코드는 각 스레드에 대해 Integer id를 설정하는 것과 같습니다.
    public class ThreadLocalTest {
    
        public static ThreadLocal<Integer> value = new ThreadLocal<Integer>(){
    
            protected synchronized Integer initialValue() {
    
                return 100;
    
            }
    
        };
    
        
    
        public static void main(String[] args) {
    
            // TODO Auto-generated method stub
    
            new Timer().schedule(new TimerTask() {
    
                
    
                @Override
    
                public void run() {
    
                    // TODO Auto-generated method stub
    
                    System.exit(0);
    
                }
    
            }, 5000);
    
            
    
            ExecutorService exec = Executors.newCachedThreadPool();
    
            for(int i=0; i<10; ++i){
    
                exec.execute(new Accesor(value.get()));
    
            }
    
            exec.shutdown();
    
        }
    
    
    
    }
    
    class Accesor implements Runnable {
    
        private int id;
    
        
    
        public Accesor(Integer id){
    
            this.id = id.intValue();
    
            System.out.println("init value: " + this.id);
    
        }
    
        
    
        public void run(){
    
            while(true) id++;
    
        }
    
    }

    좋은 웹페이지 즐겨찾기