ReentrantLock 자물쇠의 오용 분석
8603 단어 JavaApp
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockPractice3 {
static ReentrantLock lock = new ReentrantLock();
private static String[] threadArr = {"A","B","C"};
public static void main(String[] args){
ReentrantLockPractice3 pc = new ReentrantLockPractice3();
pc.startDemo();
}
void startDemo(){
for(int i = 0;i<10;i++){
for(String name : threadArr){
TestThread t = new TestThread(name);
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TestThread extends Thread{
//
TestThread(String str){
super(str);
}
public void run(){
try {
lock.lockInterruptibly();
System.out.println( Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
}
결과 출력:
ABCABCABCABCABCABCABCABCABCABC는 이 예에서 저자의 의도는ReentrantLock을 통해 다중 스레드 출력의 질서성을 확보하는 것이다.
세 개의 라인, 라인의 이름은 각각 A, B, C이고 설계 프로그램은 세 개의 라인을'ABC'를 10번 반복해서 인쇄한 후에 종료시킨다.예를 들어 ABCABCABCABCABCABCABCABCABCABCABC는 작가가 Reentrant Lock에 대해 비교적 잘 알고 있을지도 모르지만 이 예는 Reentrant Lock과 아무런 관계가 없다. 즉, Reentrant Lock이 여기에 잠겨 있는 것은 제 역할을 하지 못했다는 것이다.ReentrantLock 자물쇠를 제거하면 출력을 질서정연하게 할 수 있습니다.
public class ReentrantLockPractice3a {
private static String[] threadArr = {"A","B","C"};
public static void main(String[] args){
ReentrantLockPractice3a pc = new ReentrantLockPractice3a();
pc.startDemo();
}
void startDemo(){
for(int i = 0;i<10;i++){
for(String name : threadArr){
TestThread t = new TestThread(name);
t.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TestThread extends Thread{
//
TestThread(String str){
super(str);
}
public void run(){
try {
System.out.print(Thread.currentThread().getName());
} catch(Exception e){
e.printStackTrace();
}
}
}
}
결과 출력:
ABC ABC ABC ABC ABC ABC ABC 결과는 여전히 질서정연한 ABC...서열, 왜 이러지?
실제로 출력을 질서정연하게 하는 것은 이 몇 줄의 코드이다.
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
스레드가 매번 start 이후 100ms 지연되면 스레드가 줄을 서는 데 충분한 시간을 갖게 된다. 즉, 이 지연으로 인해 원래 순서대로 실행되었던 스레드가 질서정연해지고, 지연을 1ms로 수정해도 같은 실행 효과를 얻을 수 있다.따라서 Reentrant Lock 자물쇠는 여기에 제대로 작동하지 않았다.저자가 제시한 이 예는 목적적으로 보면 다중 스레드를 사용하여 신속하게 처리하고 출력의 스레드를 질서정연하게 해야 한다. 실제적인 효과는 스레드가 모두 순서대로 집행되는 것이다. 즉, 단일 스레드를 사용하는 것과 다름없이 오히려 스레드를 만드는 스위치를 증가시켰다.
실제로 자물쇠를 사용하는 것은 반드시 자원의 사용과 관련된다. 만약에 자원의 사용과 관련되지 않는다면 자물쇠를 사용하는 것은 의미가 없다.Reentrant Lock을 설명하기 위해 자원 경쟁의 예를 만들어서 자물쇠가 진정으로 역할을 발휘하도록 할 수 있다. 물론 다중 루트도 반드시 사용된다. 다중 루트의 역할은 처리 효율을 높이는 것이다. 그 난서 집행은 허용된다. 우리가 진정으로 관심을 가져야 할 것은 순서대로 집행해야 하는 변수를 잘 처리하는 것이다.다음 예를 참조하십시오.
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockPractice3b {
public int counter = 0;
static ReentrantLock lock = new ReentrantLock();
private static String[] threadArr = {"A","B","C"};
public static void main(String[] args){
ReentrantLockPractice3b pc = new ReentrantLockPractice3b();
pc.startDemo();
}
void startDemo(){
for(int i = 0;i<10;i++){
for(String name : threadArr){
TestThread t = new TestThread(name, this);
t.start();
}
}
}
class TestThread extends Thread{
ReentrantLockPractice3b rl2;
//
TestThread(String str, ReentrantLockPractice3b rl2){
super(str);
this.rl2 = rl2;
}
public void run(){
try {
lock.lockInterruptibly();
System.out.println("counter:" + rl2.counter++);
// try {
// sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println( Thread.currentThread().getName() );
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
}
이 예에서 우리는 자원 분쟁을 일으킬 변수 (counter) 를 구축했는데, 우리의 목적은 질서정연한 접근을 확보하는 것이다.다음 출력 결과를 보십시오.
counter: 0 A counter: 1 C counter: 2 C counter: 3 A counter: 4 A counter: 5 B counter: 6 B counter: 7 B counter: 8 C counter: 9 A counter: 10 A counter: 1 C counter: 1 C counter: 12A counter: 12A counter: 12A counter: 12A counter: 12A counter: 12A counter: 12A counter: 13B counter: 13B counter: 14B counter: 14B counter: 14B counter: 14B counter: 14 counter: 15C counter: 15C counter: 15C counter: 16 counter: 16 counter: 16 countercounter: 25 A counter: 26 B counter: 27 C counter: 28 A counter: 29 B 볼 수 있습니다.ReentrantLock을 통해counter의 질서를 보장하지만, 라인의 시작은 예상대로 "ABCABC"가 아닙니다.의 방식이 집행되는 것이 아니라 무질서한 것이다. 이것이 바로 가상 기기의 효율적인 스케줄링의 구현이다.만약 라인의 순서대로 집행을 한정한다면, 그것은 단라인과 같지 않습니까?
ReentrantLock을 제거하면 결과가 어떻게 되는지 볼 수 있습니다.
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockPractice3b {
public int counter = 0;
static ReentrantLock lock = new ReentrantLock();
private static String[] threadArr = {"A","B","C"};
public static void main(String[] args){
ReentrantLockPractice3b pc = new ReentrantLockPractice3b();
pc.startDemo();
}
void startDemo(){
for(int i = 0;i<10;i++){
for(String name : threadArr){
TestThread t = new TestThread(name, this);
t.start();
}
}
}
class TestThread extends Thread{
ReentrantLockPractice3b rl2;
//
TestThread(String str, ReentrantLockPractice3b rl2){
super(str);
this.rl2 = rl2;
}
public void run(){
try {
//lock.lockInterruptibly();
System.out.println("counter:" + rl2.counter++);
// try {
// sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println( Thread.currentThread().getName() );
} /*catch (InterruptedException e) {
e.printStackTrace();
} */finally{
//lock.unlock();
}
}
}
}
결과 출력:
counter: 0 counter: 3 A counter: 4 B counter: 2 B counter: 6 C counter: 7 A counter: 1 C counter: 1 C counter: 5 B A counter: 8 counter: 9 C A counter: 11 counter: 1 counter: 10 B counter: 10 B counter: 13 counter: 13 C counter: 14 counter: 14 B counter: 14 B counter: 16 counter: 16 C counter: 16 counter: 1 C counter: 1 C counter: 2 counter: 2 counter: 2 counter: 2 counter: 2 counter: 2 counter: 2 counter: 2 counter: 2 counter27 B B A counter: 26 B C counter: 23 A counter: 28 A counter: 29 C에서 볼 수 있습니다.counter의 값은 혼란스러워서 미리 설정한 목표에 따라 계산하지 않았습니다.ReentrantLock 자물쇠가 작동합니다.
사실, 여기의lock.lockInterruptibly();lock을 사용합니다.lock();자물쇠의 끊김과 관련이 없기 때문에 더 적합하다.
참고:
ReentrantLock 자물쇠를 사용하려면finally 코드 블록에서 반드시 자물쇠를 해제해야 합니다. 그렇지 않으면 동기화로 인한 사라진 자물쇠와 같이 자물쇠가 계속 기다리는 상황이 발생합니다.