java 다중 스레드 상세 요약

11783 단어 java다중 스레드
1. Thread.start() 및 Thread.run()의 차이점
Thread 클래스의 start () 방법을 호출하여 스레드를 시작합니다. 이 스레드는 준비된 상태이며 실행되지 않습니다.그리고 이 Thread 클래스 호출 방법 run () 을 통해 실행 작업을 완성합니다. 이 방법 run () 은 루틴체라고 합니다. 루틴체는 실행할 루틴의 내용을 포함하고 있습니다. 루틴 방법은 실행이 끝나고 이 루틴은 종료되며 CPU는 다른 루틴을 실행합니다.만약에 Run 방법을 직접 사용한다면 이것은 단지 하나의 방법을 호출할 뿐이다. 프로그램에는 여전히'주 라인'이라는 라인만 있고 새로운 라인을 개척하지 않았다. 그 프로그램의 실행 경로는 하나밖에 없기 때문에 라인을 쓰는 목적을 달성하지 못했다.
테스트 코드는 다음과 같습니다

public class MyThread implements Runnable {
public void run() {
System.err.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
Thread t1 = new Thread(thread, "Thread-1");
Thread t2 = new Thread(thread, "Thread-2");
t1.run();
t2.run();
}
}
. 출력 결과는
>> 현재 프로세스는:main
>> 현재 프로세스는:main
start 메서드로 변경:

package thread;
public class MyThread implements Runnable {
public void run() {
System.err.println(">> :"+Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
Thread t1 = new Thread(thread, "Thread-1");
Thread t2 = new Thread(thread, "Thread-2");
t1.start();
t2.start();
}
}
결과:
>> 현재 프로세스는: Thread-1
> 현재 프로세스: Thread-2 2, ThreadLocal 클래스 상세 정보
ThreadLocal은 문맥을 넓히기 쉬우므로 당연히'로컬 라인'이라고 생각한다.사실, ThreadLocal은 Thread가 아니라 Thread의 국부 변수입니다. 이것을 ThreadLocalVariable라고 부르는 것이 더 이해하기 쉽습니다.ThreadLocal을 사용하여 변수를 유지할 때, ThreadLocal은 이 변수를 사용하는 모든 라인에 독립된 변수 복사본을 제공하기 때문에, 모든 라인은 다른 라인에 대응하는 복사본에 영향을 주지 않고 독립적으로 자신의 복사본을 변경할 수 있습니다.
다음은 스레드 부분 변수(ThreadLocal variables)의 핵심 포인트입니다.
하나의 스레드 부분 변수(ThreadLocal variables)는 모든 스레드에 단독 변수를 편리하게 제공합니다.여러 개의 스레드가 이 변수를 조작할 때 서로 영향을 주지 않을 수 있다. 왜냐하면 모든 스레드가 조작하는 것은 사실상 양을 바꾸는 복사본이기 때문이다.ThreadLocal 실례는 정적 개인 (private static) 필드로 한 클래스에 나타나는데, 이 클래스는 라인을 연결하는 데 사용됩니다.여러 개의 스레드가 ThreadLocal 실례에 접근할 때, 각 스레드는ThreadLocal에서 제공하는 독립된 변수 복사본을 유지합니다.
다음은 테스트 코드입니다. 테스트에 사용됩니다. 하나의 대상 위에 있는 세 개의 라인에 작용하여 같은ThreadLoacl 대상(integer 유형)을 조작하고 더러운 읽기 등이 발생하는지 확인합니다.

public class Test implements Runnable {
private static ThreadLocal<Integer> num = new ThreadLocal<Integer>();
public void run() {
num.set(0);
for (int i = 0; i < 3; i++) {
num.set(num.get() + 1);
System.out.println(Thread.currentThread().getName() + ":num="
+ num.get());
}
}
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test, "Thread-1");
Thread t2 = new Thread(test, "Thread-2");
Thread t3 = new Thread(test, "Thread-3");
t1.start();
t2.start();
t3.start();
}
}
실행 결과는 다음과 같습니다.
Thread-3:num=1Thread-2:num=1Thread-1:num=1Thread-2:num=2Thread-3:num=2Thread-2:num=3Thread-1:num=2Thread-1:num=3Thread-3:num=3
위에서 볼 수 있듯이 더러운 읽기 등은 전혀 일어나지 않았기 때문에 ThreadLocal 라인은 안전하다.
자주 사용하는 사용: DAO 클래스가 단일 클래스일 때 데이터베이스 링크(connection)는 모든 라인에 독립적으로 유지보수되고 서로 영향을 주지 않습니다.
세션의 생성과 사용을 제어할 수 있습니다. 아래와 같이ThreadLocalsession=new ThreadLocal().
ThreadLoacal 및 동기화 메커니즘 비교:
1. 동기화 메커니즘에서 대상의 자물쇠 메커니즘을 통해 같은 시간에 하나의 루트 접근 변수만 확보한다.이때 이 변수는 여러 개의 루틴이 공유된다. 동기화 메커니즘을 사용하면 프로그램이 언제 변수를 읽고 써야 하는지, 언제 특정한 대상을 잠가야 하는지, 언제 대상의 자물쇠를 풀어야 하는지 등 번잡한 문제를 신중하게 분석해야 하기 때문에 프로그램 설계와 작성의 난이도가 상대적으로 크다.
2. ThreadLocal은 다른 각도에서 다중 스레드의 병렬 접근을 해결한다.ThreadLocal은 각 스레드에 독립적인 변수 복사본을 제공하여 여러 스레드가 데이터에 대한 접근 충돌을 격리합니다.모든 스레드가 자신의 변수 복사본을 가지고 있기 때문에 이 변수를 동기화할 필요가 없다.ThreadLocal은 다중 스레드 코드를 작성할 때 안전하지 않은 변수를 ThreadLocal에 봉인할 수 있는 안전한 공유 대상을 제공합니다.
3. 요약하면 다중 스레드 자원 공유의 문제에 대해 동기화 메커니즘은'시간으로 공간을 바꾼다'는 방식을 사용하고 ThreadLocal은'공간으로 시간을 바꾼다'는 방식을 사용한다.전자는 단지 하나의 변수만 제공하여 서로 다른 라인을 줄을 서서 방문하게 하고, 후자는 모든 라인에 하나의 변수를 제공하기 때문에 동시에 방문할 수 있어 서로 영향을 주지 않는다.3. InvalidMonitorStateException 예외
wait ()/notify ()/notify All () 의 모든 방법을 호출할 때, 현재 라인이 이 대상의 자물쇠를 얻지 못하면, Illegal MonitorState Exception의 이상을 던집니다. (즉, 프로그램이 대상의 동기화 블록이나 동기화 방법을 실행하지 않았을 때,wait ()/notify ()/notify All () 를 호출하려고 시도할 때)이 이상은 Runtime Excpetion의 하위 클래스이기 때문에 반드시 포착해야 하는 것은 아니다.RuntimeException으로서 이러한 이상은wait(), notify(), notifyAll()의 방법에 서명하지 않습니다.
다음 코드에서 선을 긋는 부분에 이상이 발생합니다. 대상에 대한 동기화 작업이 없기 때문입니다.

public class Common implements Runnable {
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
this.wait(1000);
method1();
} else {
method2();
notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
}
}
코드로 바꾸고 선을 긋는 부분:

public class Common implements Runnable {
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
synchronized(this){
this.wait(1000);
}
method1();
} else {
method2();
synchronized (this) {
notifyAll();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
}
}
넷, sleep ()와wait ()와suspend ()의 차이
차이점1:
sleep는Thread류의 방법으로 자신의 흐름을 제어하는 데 사용됩니다. 예를 들어 시간을 알리는 라인이 있습니다. 1초에 한 시간씩 인쇄합니다. 그러면 저는 print 방법 앞에 sleep를 추가해서 1초에 한 번씩 실행해야 합니다.알람시계처럼.sleep () 는 현재 스레드의 실행 정지 시간을 표시하고 실행 기회를 다른 스레드에 양보하지만 모니터링 상태는 그대로 유지되며 때가 되면 자동으로 복구됩니다.sleep를 호출하면 대상 자물쇠가 풀리지 않습니다.
wait는 Object 클래스의 방법으로 스레드 간의 통신에 사용됩니다. 이 방법은 현재 이 대상의 자물쇠를 가지고 있는 스레드를 기다리게 하고 다른 스레드가 notify 방법을 호출할 때 깨어날 때까지 기다릴 수 있습니다. 그러나 시간도 지정하고 자동으로 깨어날 수 있습니다.이 방법은 주로 서로 다른 라인 사이의 스케줄링에 쓰인다.대상은wait() 방법을 호출하여 이 스레드가 대상 자물쇠를 포기하고 이 대상을 기다리는 대기 잠금 탱크에 들어가게 합니다. 이 대상에 대한 notify 방법(또는 notify All)을 보낸 후에야 이 스레드가 대상 잠금 탱크에 들어가서 대상 자물쇠가 실행 상태로 들어갈 준비를 합니다.
차이2:wait 방법을 호출하면 현재 라인의 자물쇠를 방출할 수 있다. 사실 라인 간의 통신은 대상에 의해 관리되고 모든 조작 대상의 라인은 이 대상이 자신의wait 방법을 통해 관리한다.마치 이 대상이 텔레비전인 것 같다. 세 사람이 세 개의 라인이다. 그러면 텔레비전의 리모컨은 바로 이 자물쇠이다. 만약에 지금 A가 리모컨을 들고 텔레비전이wait방법을 사용한다면 A는 자신의 리모컨을 건네주고 jVM 가상 기기가 스케줄링한다. 리모컨은 누구에게 맡겨야 하는가.
sleep 방법을 호출하면 자물쇠를 방출하지 않습니다. 왜냐하면 sleep () 는 하나의 라인으로 자신을 관리하는 방법이기 때문에 라인 통신과 관련이 없습니다.아니면 위의 예에서 만약에 A가 리모컨을 잡는 동안 그는 자신의 sleep로 10분마다 리모컨을 조정할 수 있지만 그의 리모컨이 쉬는 10분 동안 리모컨이 그의 손에 있어 다른 사람들은 리모컨을 얻을 수 없다.
suspend () 방법은 자물쇠가 사라지기 쉽다.suspend () 를 호출할 때 목표 라인이 멈추지만, 그 전에 얻은 자물쇠를 가지고 있습니다.이 때, 다른 모든 루트는 잠긴 자원에 접근할 수 없습니다.'끊긴'루트가 다시 실행되지 않는 한.모든 라인에 대해 말하자면, 만약 그들이 목표 라인을 회복하고 싶을 때, 동시에 잠긴 자원을 사용하려고 시도한다면, 자물쇠가 사라질 것이다
다음 상황에서 자물쇠를 가진 라인은 자물쇠를 방출합니다.
1. 동기화 코드 블록을 실행합니다.
2. 동기화 코드 블록을 실행하는 과정에서 이상이 발생하여 스레드가 종료됩니다.
3. 동기화 코드 블록을 실행하는 과정에서 자물쇠가 속하는 대상의wait() 방법을 실행하면 이 라인은 자물쇠를 방출하고 대상의 대기탱크를 진행합니다.
다음 상황에서 스레드는 실행을 중지하지만 스레드는 잠금을 해제하지 않습니다.
1. 동기화 코드 블록을 실행하는 동안 Thread를 실행합니다.sleep () 방법, 현재 라인은 CPU를 포기하고 수면을 시작하며 수면 중 자물쇠를 풀지 않습니다.
2. 동기화 코드 블록을 실행하는 동안 Thread를 실행합니다.yield () 메서드, 현재 스레드가 CPU를 포기하지만 잠금을 해제하지 않습니다.
3. 동기화 코드 블록을 실행하는 과정에서 다른 라인은 현재 대상의suspend () 방법을 실행합니다. 현재 라인은 정지되지만 자물쇠를 풀지 않습니다.
5. 정적 방법에서 동기화 사용
JAVA는 객체 잠금과 클래스 잠금 등 두 가지 유형의 잠금만 인식합니다.
동기화 정적 방법은 이 클래스의 "Class"대상을 가져옵니다. 따라서 한 라인이 동기화 정적 방법에 들어갈 때, 라인 모니터는 클래스 자체의 자물쇠를 가져옵니다. 전체 클래스에 자물쇠를 넣고, 다른 라인은 이 클래스의 정적 동기화 방법에 들어갈 수 없습니다. 여러 라인이 서로 다른 실례 동기화 방법에 동시에 접근할 수 있기 때문입니다. 테스트 코드는 다음과 같습니다.

public class Common implements Runnable {
public synchronized static void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized static void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
method1();
} else {
method2();
// Thread.currentThread().notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
// , ,
Common c1 = new Common();
Common c2 = new Common();
Thread t1 = new Thread(c1, "Thread-1");
Thread t2 = new Thread(c2, "Thread-2");
t1.start();
t2.start();
}
}
실행 결과는 다음과 같습니다. Running Thread-2Running Thread-1Method 2 calledMethod 2 doneMethod 1 calledMethod 1 done
6. 한 대상에 두 개의 라인이 같은 시간에 각각 두 개의 다른 동기화 실례 방법을 호출할 수 있습니까?
아니오, 하나의 대상이 실례 방법을 동기화했기 때문에, 라인은 대상의 대상 자물쇠를 얻었습니다.따라서 이 방법을 실행하고 대상의 자물쇠를 풀어야만 다른 동기화 방법을 실행할 수 있다.테스트 코드는 다음과 같습니다.

public class Common implements Runnable {
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
method1();
} else {
method2();
// Thread.currentThread().notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
// , ,
//Common c1 = new Common();
//Common c2 = new Common();
//c1.start();
//c2.start();
}
}
실행 결과는 다음과 같습니다. Running Thread-1 Running Thread-2 Method 1 calledMethod 1 doneMethod 2 calledMethod 2 done

좋은 웹페이지 즐겨찾기