일레븐, 잠금의 깊이의 자바 멀티 스레딩으로

재진입 잠금

1. 동시 공유 데이터로 잠금 (등) 등 동기 (헤비급)와 ReentrantLock와 (경량 등, 등) 여러 구현 도구의 일관성, 자바 플랫폼을 보장합니다. 이 잠금은 우리의 개발을위한 편의를 제공하기 위해 서비스를 작성되었습니다. 재진입 잠금 또한 로크를 획득하도록 외부 함수 후 동일한 스레드를 참조 재귀 자물쇠로 알려진 내측 재귀 함수는 여전히 잠금 코드를 확보해야하지만, 영향을받지 않는다.
2.에서 ReentrantLock와 JAVA 환경과는 재진입 잠금 장치를 동기화됩니다.

3. 코드

//重入锁  轻量级(Lock)与重量级锁(synchronized)---可重入性(递归锁)
public class Test001 implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        set();
    }
    //synchronized代码块执行完毕的时候释放锁
    private synchronized void set() {
        System.out.println("set方法");
        get();
    }
    private synchronized void get() {
        System.out.println("synchronized  可具备可重入性-get方法");
    }
    public static void main(String[] args) {
        Test001 test001 = new Test001();
        Thread thread = new Thread(test001);
        thread.start();
        System.out.println(Thread.currentThread().getName()+"主线程结束");
    }
}

4. 결과

main主线程结束
set方法
synchronized  可具备可重入性-get方法

5. 코드

//演示lock锁是否具备  可重入性(特征:锁可以传递(方法递归传递)),下面的方法为啥会调两次,因为最后一次调用他已经知道第一次已经上锁了(不会在重新获取锁)
public class Test002 implements Runnable {
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        set();
    }

    private void set() {
        try {
            //上锁
            lock.lock();
            System.out.println("set方法");
            get();
        } catch (Exception e) {
            //重入锁的目的就是避免死锁
        } finally {
            lock.unlock();//释放锁
        }
    }

    private void get() {
        try {
            lock.lock();
            System.out.println("lock  可具备可重入性-get方法");
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Test002 test002 = new Test002();
        Thread thread = new Thread(test002);
        thread.start();
    }
}

6. 결과

set方法
lock  可具备可重入性-get方法

둘째, 읽기 - 쓰기 잠금을

1. 비교 잠금 (자바 잠금)에 자바를 잠금 구현, 읽기 - 쓰기 잠금은 더 복잡. , 당신의 프로그램이 공유 리소스에 대한 읽기 및 쓰기 작업의 수를 포함한다고 가정 읽기 및 쓰기 작업은 너무 자주하지 않습니다. 쓰기 작업이없는 경우 , 두 개의 스레드가 여러 스레드가 동시에 공유 리소스를 읽을 수 있도록해야, 아무 문제없이 자원을 읽을 수 있습니다. 이러한 공유 자원을 작성하는 스레드 희망이 있다면, 우리는 더 이상 읽거나 쓸 수있는 다른 스레드에 자원이 없어야 즉 : (번역기의 참고 쓰기가 공존 할 수없는, 쓰기 - - - 읽기, 공존 할 수 읽기 쓰기 우리는) 공존 할 수 없습니다. 이이 문제를 해결하기 위해 읽기 / 쓰기 잠금이 필요합니다. Java5에있는 java.util.concurrent 패키지는 이미 읽기 - 쓰기 잠금이 포함되어 있습니다. 그럼에도 불구하고, 우리는 그 구현 뒤에 원리를 이해한다.

2. 코드

//读写锁  jvm内置缓存
public class Test003 {
    private volatile Map<String,String> caChe = new HashMap<>();
    //读写锁
    private ReentrantReadWriteLock  rw1 = new ReentrantReadWriteLock();
    //写入锁
    private WriteLock writeLock = rw1.writeLock();
    //读出锁
    private ReadLock readLock = rw1.readLock();
    //写入元素
    public void put(String key,String value){
        try {
            writeLock.lock();
            System.out.println("正在做写的操作,key:" + key + ",value:" + value + "开始.");
            Thread.sleep(100);
            caChe.put(key,value);
            System.out.println("正在做写的操作,key:" + key + ",value:" + value + "结束.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            writeLock.unlock();
        }
    }
    //读取元素
    public String get(String key){
        try {
            readLock.lock();
            System.out.println("正在做读的操作,key:" + key + ",开始.");
            Thread.sleep(100);
            String value = caChe.get(key);
            System.out.println("正在做读的操作,key:" + key + ",结束.");
            return value;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        }finally {
            readLock.unlock();
        }
    }

    public static void main(String[] args) {
        Test003 test003 = new Test003();
        //写线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0;i<10;i++){
                    test003.put("i",i+"");
                }
            }
        });
        //读线程
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0;i<10;i++){
                    test003.get(i+"");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

3. 결과

正在做写的操作,key:i,value:0开始.
正在做写的操作,key:i,value:0结束.
正在做写的操作,key:i,value:1开始.
正在做写的操作,key:i,value:1结束.
正在做读的操作,key:0,开始.
正在做读的操作,key:0,结束.
正在做写的操作,key:i,value:2开始.
正在做写的操作,key:i,value:2结束.
正在做写的操作,key:i,value:3开始.
正在做写的操作,key:i,value:3结束.
正在做写的操作,key:i,value:4开始.
正在做写的操作,key:i,value:4结束.
正在做读的操作,key:1,开始.
正在做读的操作,key:1,结束.
正在做写的操作,key:i,value:5开始.
正在做写的操作,key:i,value:5结束.
正在做写的操作,key:i,value:6开始.
正在做写的操作,key:i,value:6结束.
正在做写的操作,key:i,value:7开始.
正在做写的操作,key:i,value:7结束.
正在做读的操作,key:2,开始.
正在做读的操作,key:2,结束.
正在做写的操作,key:i,value:8开始.
正在做写的操作,key:i,value:8结束.
正在做写的操作,key:i,value:9开始.
正在做写的操作,key:i,value:9结束.
正在做读的操作,key:3,开始.
正在做读的操作,key:3,结束.
正在做读的操作,key:4,开始.
正在做读的操作,key:4,结束.
正在做读的操作,key:5,开始.
正在做读的操作,key:5,结束.
正在做读的操作,key:6,开始.
正在做读的操作,key:6,结束.
正在做读的操作,key:7,开始.
正在做读的操作,key:7,结束.
正在做读的操作,key:8,开始.
正在做读的操作,key:8,结束.
正在做读的操作,key:9,开始.
正在做读的操作,key:9,结束.

셋째, 비관적 잠금 및 낙관적 잠금

1. 비관적 잠금

항상 최악의 경우를 가정, 다른 모든 스레드의 수정은 데이터를 가져 오는 경우 데이터에 액세스하려는 다른 스레드가 차단 된 중단 된 필요로 할 때, 그것은, (읽기 잠금, 쓰기 잠금, 행 잠금 등)을 잠급니다 생각 . 읽기, 행 잠금 및 쓰기 잠금과 같은 데이터베이스 구현에 의존 수, 자바, 동기화 된 생각은 비관적 잠금입니다, 작업 전에 잠 깁니다.

2. 낙관적 잠금

2.1. 항상 데이터를 얻고 데이터를 수정하는 다른 스레드가 없을 것이라고 믿을 때마다 동시성 문제를 생각하지, 잠겨,하지만 업데이트가 다른 스레드를 결정할 때 더가 그 전에되지 않습니다 데이터, 달성하기 위해 일반적으로 버전 번호 또는 CAS 운영 메커니즘을 수정합니다.

 버젼 : 일반 데이터 플러스 버전 번호는 데이터 테이블의 버전 필드는 데이터의 개수가 변경되는 나타내고 있으며, 데이터가 변경되었을 때, 플러스 버전 값. 데이터를 판독하는 동안 데이터 값을 갱신 할 수있는 스레드 A 버전 값을 판독 할 때 업데이트를 전송할 때, 단지 버전을 읽을 경우에 업데이트 버전의 동일한 현재 데이터베이스의 값 또는 재시 때 업데이트까지 업데이트는 성공적이다.

핵심 SQL 문

업데이트 설정 표 X = X + 1 버전 = 버전 + 1 여기서, ID = #} {ID 및 버전 = # {버전};    

CAS 동작 : 즉, 비교 및 ​​교환, 또는 세 피연산자를 포함한, 비교 세트 여기서 상기 데이터 메모리의 값, 예상 값, 새로운 값. 업데이트 될 필요가되면 동일한 경우, 상기 메모리의 전류 값이 동일하기 전에 재시 일반적인 회전 조작, 즉 연속 시도를 실패하는 경우, 새로운 값으로 업데이트 된 값을 결정한다.

2.2. 실시 예

간단한 예를 들자면하려면, 필드와 경상 수지 (밸런스) $ 100 데이터베이스 테이블에서 계정 정보를 가정하는 것은 버전 필드 1의 현재 값을가집니다.

  1. 이때, 오퍼레이터 A는 (버전 = 1), 및 계정 잔액에서 $ 50 ($ 100 내지 $ 50) 차감 판독.
  2. 오퍼레이터 (A)의 동작 동안, 오퍼레이터 B는 상기 사용자 정보 (버전 = 1)를 판독하고, 잔고로부터 차감 $ 20의 ($ 100 내지 $ 20)이다.
  3. 데이터베이스 업데이트하기 위해 최선을 다하고 계정 공제 균형과 함께 데이터 버전 번호 더한 (버전 = 2)의 운영자 완전한 개정 (균형은 $ 50 =), 데이터 버전은 데이터베이스보다 큰 제출하기 때문에이 시간은 데이터의 현재 버전이 기록 업데이트 된 데이터베이스 레코드는 버전 2로 업데이트됩니다.
  4. 운영자 B는 작업을 완료 할뿐만 아니라 버전 번호 더한 (버전 = 2) 데이터베이스에 데이터를 (균형 $ (80) =) 제출하려고 노력하지만, 데이터베이스 레코드 버전의 발견에 비해 이번에는 운영자 B의 데이터 버전 번호는 2, 신청 데이터베이스는 또한 "업데이트를 수행하기 위해 레코드의 현재 버전보다 커야합니다 버전을 제출,"낙관적 잠금 전략은, 그러므로, 운영자 B 제출이 거부되었습니다 부합하지 않는, 현재 버전이 기록합니다.

따라서, 이전 버전 = 1 개 변형 데이터 결과들에 기초 할 수 연산자의 연산 결과의 커버 오퍼레이터 B를 방지한다.

넷째, 말

항상 믿음을 유지!

게시 된 122 개 원래 기사 · 원 찬양 64 ·은 50000 +를 볼

추천

출처blog.csdn.net/chenmingxu438521/article/details/103872814