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