이 기사는 주로 Java 멀티 스레딩의 스레드 안전 문제를 요약하기 위해 주로 멀티 스레딩의 두 기사를 따릅니다.
1. 일반적인 Java 스레드 안전 예
공개 클래스 스레드 테스트 {public static void main (String [] args) {계정 계정 = 새 계정 ( "123456", 1000); DrawMoneyRunnable DrawMoneyRunnable = New DrawMoneyRunnable (계정, 700); 스레드 mythread1 = new Thread (DrawMoneyRunnable); 스레드 Mythread2 = 새 스레드 (DrawMoneyRunnable); Mythread1.start (); mythread2.start (); }} 클래스 DrawMoneyRunnable 구현 실행 가능 {개인 계정 계정; 개인 이중 드로 아 마운트; Public DrawMoneyRunnable (계정 계정, Double DrawAmount) {super (); this.account = 계정; this.drawamount = drawamount; } public void run () {if (ac Double Balance = Account.getBalance () - DrawAmount; 계정 .setBalance (밸런스); System.out.println ( "밸런스 :" + 밸런스); }}} 클래스 계정 {private String AccountNo; 개인 이중 잔액; 공개 계정 () {} 공개 계정 (String AccountNo, Double Balance) {this.accountno = AccountNo; this.balance = 균형; } public String getAccountno () {return AccountNo; } public void setAccountno (String AccountNo) {this.accountno = AccountNo; } public double getBalance () {return Balance; } public void setBalance (Double Balance) {this.balance = 밸런스; }}위의 예는 이해하기 쉽습니다. 잔액이 1,000 인 은행 카드가 있습니다. 이 프로그램은 귀하와 귀하의 아내가 ATM에서 동시에 돈을 인출하는 장면을 시뮬레이션합니다. 이 프로그램을 여러 번 실행하면 출력 결과가 여러 가지 다른 조합을 가질 수 있습니다. 가능한 출력 중 하나는 다음과 같습니다.
1 자금 철회가 성공적이며, 돈 철수는 다음과 같습니다. 700.0
2 균형은 : 300.0
3 돈 철수가 성공적이며, 돈 철수는 다음과 같습니다. 700.0
4 균형은 다음과 같습니다. -400.0
다시 말해, 잔액이 1,000 명인 은행 카드의 경우 총 1,400을 인출 할 수 있습니다. 이는 분명히 문제입니다.
분석 후, 문제는 Java 다중 스레드 환경에서 실행의 불확실성에 있습니다. CPU는 준비된 상태의 여러 스레드간에 무작위로 전환 될 수 있으므로 다음과 같은 상황이 발생할 가능성이 높습니다. Thread1이 // 1에서 코드를 실행할 때 판단 조건은 참입니다. 현재 CPU는 Thread2로 전환하여 // 1에서 코드를 실행하고 여전히 사실임을 알게됩니다. 그런 다음 Thread2가 실행 된 다음 Thread1로 전환 한 다음 실행이 완료됩니다. 현재 위의 결과가 나타납니다.
따라서 스레드 안전 문제와 관련하여 실제로 다중 스레드 환경에서 공유 리소스에 액세스하면이 공유 리소스에서 불일치가 발생할 수 있습니다. 따라서 스레드 안전 문제를 피하기 위해 다중 스레드 환경 에서이 공유 리소스에 대한 동시 액세스를 피해야합니다.
2. 동기화 방법
동기화 된 키워드 수정은 공유 리소스에 액세스하기위한 메소드 정의에 추가 되어이 메소드를 동기화 메소드라고합니다. 이 방법이 잠겨 있음을 간단히 이해할 수 있으며 잠긴 객체는 현재 방법이 위치한 객체 자체입니다. 다중 스레드 환경 에서이 방법을 실행할 때 먼저이 동기화 잠금을 얻어야합니다 (그리고 최대 하나의 스레드 만 얻을 수 있습니다). 스레드 가이 동기화 메소드를 실행할 때만 잠금 객체가 해제되고 다른 스레드는이 동기화 잠금 등을 얻을 수 있습니다.
위의 예에서 공유 리소스는 계정 개체이며 동기화 방법을 사용할 때 스레드 안전 문제를 해결할 수 있습니다. run () 메소드 전에 동기화 된 키워드를 추가하십시오.
public synchronized void run () {// ....}3. 코드 블록 동기화
위에서 분석 한 바와 같이, 스레드 안전 문제를 해결하려면 공유 리소스에 대한 액세스 불확실성을 제한하면됩니다. 동기화 방법을 사용하면 전체 메소드 본체가 동기 실행 상태가되어 동기화 범위가 발생할 수 있습니다. 따라서 동기화가 필요한 코드에 대해 다른 동기화 방법 인 동기화 코드 블록을 직접 해결할 수 있습니다.
동기 코드 블록의 형식은 다음과 같습니다.
동기화 (OBJ) {// ...}그중에서도 OBJ는 잠금 객체이므로 잠긴 객체를 선택하는 것이 중요합니다. 일반적 으로이 공유 리소스 개체는 잠금 객체로 선택됩니다.
위의 예에서와 같이 계정 객체를 잠금 객체로 사용하는 것이 가장 좋습니다. (물론, 생성 스레드는 런닝 가능한 메소드를 사용하기 때문에 이것을 선택할 수도 있습니다. 스레드 메소드를 직접 상속하는 스레드 인 경우이 객체를 동기화 잠금으로 사용하면 다른 객체이기 때문에 실제로 역할을 수행하지 않으므로 동기화 잠금을 선택할 때 더 조심해야합니다 ...)
4. 객체 동기화 잠금
위에서 볼 수 있듯이 동기식 잠금 객체의 선택에주의를 기울여야하기 때문에 간단한 솔루션이 있습니까? 공유 리소스에서 동기식 잠금 객체를 분리하는 동시에 스레드 안전 문제를 잘 해결할 수 있습니다.
잠금 객체를 사용하면 동기화 잠금 이이 문제를 쉽게 해결할 수 있습니다. 유일한 것은 잠금 객체가 자원 객체와 일대일 관계를 가져야한다는 것입니다. 잠금 객체 동기화 잠금의 일반적인 형식은 다음과 같습니다.
클래스 x {// 공유 리소스 개인 최종 잠금 잠금과 일대일 관계를 갖는 잠금 동기화 잠금을 정의하는 객체를 표시합니다. public void m () {// lock.lock (); // ... 스레드 -SAFE 동기화가 필요한 코드 // 잠금 잠금 잠금 장치를 릴리스합니다. }}5. Wait ()/notify ()/notifyall () 스레드 통신
이 세 가지 방법은 블로그 게시물 "Java Summary Series : java.lang.object"에 언급되어 있습니다. 이 세 가지 방법은 주로 멀티 스레딩에 사용되지만 실제로는 객체 클래스의 로컬 메소드입니다. 따라서 이론적으로 모든 객체 객체는이 세 가지 방법의 주요 톤으로 사용할 수 있습니다. 실제 멀티 스레딩 프로그래밍에서, 잠금 객체를 동기화 하여이 세 가지 방법을 조정하여 여러 스레드 간의 스레드 통신이 완료 될 수 있습니다.
대기 () : 현재 스레드가 대기하고 대기 차단 상태로 들어가게합니다. 다른 스레드가 동기식 잠금 오브젝트의 notify () 또는 notifyall () 메소드를 호출하여 스레드를 깨우을 때까지.
notify () :이 동기식 잠금 객체에서 대기 대기 한 단일 스레드를 깨우십시오. 이 동기식 잠금 오브젝트에서 여러 스레드가 대기중인 경우 웨이크 업 작동을 위해 스레드 중 하나가 선택됩니다. 현재 스레드가 동기식 잠금 객체의 잠금 장치를 포기할 때만 깨어 난 스레드를 실행할 수 있습니다.
notifyall () :이 동기식 잠금 객체에서 대기하는 모든 스레드를 깨우십시오. 현재 스레드가 동기식 잠금 객체의 잠금 장치를 포기할 때만 깨어 난 스레드를 실행할 수 있습니다.
package com.qqyumidi; public class 스레드 테스트 {public static void main (String [] args) {계정 계정 = 새 계정 ( "123456", 0); Thread DrawMoneythread = New DrawMoneythread ( "Get Money Thread", Account, 700); 스레드 퇴적물 모니 스레드 = 새 예금 모니 스레드 ( "저장 스레드", 계정, 700); DrawMoneythread.start (); 예금 moneythread.start (); }} class drawMoneythread는 스레드 {private 계정 계정; 개인 이중 금액; public drawmoneythread (String ThreadName, 계정 계정, 이중 금액) {super (ThreadName); this.account = 계정; this.amount = 금액; } public void run () {for (int i = 0; i <100; i ++) {ac }}} class epositeMoneythread는 스레드 {private 계정 계정; 개인 이중 금액; public intestmoneythread (String ThreadName, 계정 계정, 이중 금액) {super (ThreadName); this.account = 계정; this.amount = 금액; } public void run () {for (int i = 0; i <100; i ++) {ac }}} 클래스 계정 {private String AccountNo; 개인 이중 잔액; // 계정에 이미 입금이 있는지 여부를 식별하십시오. 개인 부울 플래그 = false; 공개 계정 () {} 공개 계정 (String AccountNo, Double Balance) {this.accountno = AccountNo; this.balance = 균형; } public String getAccountno () {return AccountNo; } public void setAccountno (String AccountNo) {this.accountno = AccountNo; } public double getBalance () {return Balance; } public void setBalance (Double Balance) {this.balance = 밸런스; } / ** * 돈을 절약 * * @param 퇴적물 Atamount * / public synchronized void 보증금 (double epostamount, int i) {if (flag) {// 계정의 누군가가 이미 비용을 절약했으며 현재 스레드는 {system.out.currentthread (). 기다리다(); // 1 system.out.println (thread.currentThread (). getName () + "수행 대기 작업" + " - i =" + i); } catch (InterruptedException e) {e.printstacktrace (); }} else {// system.out.println (struch.currentThread (). getName () + "예금 :" + restamount + " - i =" + i); SetBalance (밸런스 + 퇴적물); flag = true; // 다른 스레드를 깨우십시오. notifyall (); // 2 try {thread.sleep (3000); } catch (InterruptedException e) {e.printstacktrace (); } system.out.println (Thread.currentThread (). getName () + "- 비용을 저장- 실행 완료" + "- i =" + i); }} / ** * 돈 인출 * * @param drawamount * / public synchronized void draw (double drawamount, int i) {if (! flag) {// 계정의 어느 누구도 아직 돈을 절약하지 못했으며 현재 스레드는 {system.out.println (strook. 나); 기다리다(); System.out.println (thread.currentThread (). getName () + "Execute wain 작동" + "Execute Wait Operation" + " - i =" + i); } catch (InterruptedException e) {e.printstacktrace (); }} else {// Money System.out.out.println (ride.currentThread (). getName () + "돈 인출 :" + drawAmount + " - i =" + i); setBalance (getBalance () - DrawAmount); flag = false; // 다른 스레드를 깨우십시오. notifyall (); System.out.println (thread.currentthread (). getName () + "-돈 인출-실행이 완료됩니다." + "-i =" + i); // 3}}} 위의 예는 Wait ()/notify ()/notifyall ()의 사용을 보여줍니다. 일부 출력 결과는 다음과 같습니다.
돈 철수 스레드가 대기 작업을 실행하고 대기 작업을 실행하기 시작합니다- i = 0
스레드 보증금 저장 : 700.0 -i = 0
돈을 저장 스레드 사원 돈 실행-i = 0
돈 절약 스레드는 대기 작업을 수행해야합니다.-i = 1
돈 철수 스레드는 대기 작업 및 대기 작업을 실행합니다- i = 0
돈을 철회하는 금액 금액 : 700.0 -i = 1
돈 철수 스레드-실행-실행-i = 1
돈을 인출하는 스레드는 대기 작업을 실행하기 시작하고 대기 작업을 실행해야합니다- i = 2
돈 절약 스레드는 대기 작업을 실행합니다.-i = 1
스레드 보증금 저장 : 700.0 -i = 2
돈을 저장 스레드 사원 돈 실행-i = 2
인출 스레드는 대기 작업을 실행하고 대기 작업을 실행합니다- i = 2
돈 스레드를 철회하면 돈을 철수하십시오 : 700.0 -i = 3
돈 철수 실-실행-실행-i = 3
돈을 인출하는 스레드는 대기 작업을 실행하고 대기 작업을 수행해야합니다- i = 4
스레드 보증금 저장 : 700.0 -i = 3
돈을 저축 스레드 사원 돈 실행-i = 3
돈 절약 스레드는 대기 작업을 수행해야합니다.-i = 4
돈 철수 스레드는 대기 작업 및 대기 작업을 실행합니다- i = 4
돈 스레드 철수 돈을 철수하십시오. 돈 : 700.0 -i = 5
돈 철수 스레드-실행-실행-i = 5
돈을 철회하는 스레드는 대기 작업을 수행하고 대기 작업을 수행하기 시작해야합니다.-i = 6
돈 절약 스레드는 대기 작업을 실행합니다- i = 4
스레드 보증금 저장 : 700.0 -i = 5
돈을 저축 스레드 사원 돈 실행-i = 5
돈 절약 스레드는 대기 작업을 수행해야합니다.-i = 6
돈 철수 스레드는 대기 작업 및 대기 작업을 실행합니다- i = 6
돈 스레드 철수 돈을 철수하십시오 : 700.0 -i = 7
돈 철수 스레드-실행-실행-i = 7
돈 철수 스레드는 대기 작업을 실행하고 대기 작업을 실행하기 시작합니다-I = 8
머니 저축 스레드는 대기 작업을 실행합니다- i = 6
스레드 보증금 저장 : 700.0 -i = 7
따라서 다음 사항에주의를 기울여야합니다.
1. Wait () 메소드가 실행 된 후, 현재 스레드는 즉시 대기 차단 상태로 들어가고 후속 코드는 실행되지 않습니다.
2. Notify ()/notifyall () 메소드가 실행되면이 동기화 잠금 객체의 스레드 개체 (any-notify ()/all-notifyall ())가 깨어납니다. 그러나 현재 동기화 잠금 객체는 해제되지 않습니다. 즉, notify ()/notifyall () 뒤에 코드가 있으면 계속 진행됩니다. 현재 스레드가 실행될 때만 동기화 잠금 객체가 해제됩니다.
3. Notify ()/notifyall ()이 실행 된 후 오른쪽에 sleep () 메소드가 있으면 현재 스레드가 차단 상태로 들어가지만 동기화 객체 잠금이 해제되지 않으며 여전히 자체적으로 유지됩니다. 그런 다음 스레드는 일정 기간이 지나면 다음 2를 계속 실행합니다.
4. WAIT ()/notify ()/nitifyall ()은 다른 객체 잠금을 기반으로 스레드 간의 통신 또는 협업을 완료합니다. 따라서 다른 동기화 객체 잠금 장치 인 경우 의미를 잃게됩니다. 동시에, 동기화 객체 잠금은 공유 리소스 객체와 일대일 서신을 유지하는 데 가장 적합합니다.
5. 대기 스레드가 깨어나고 실행되면 지난번에 실행 된 대기 () 메소드 코드가 계속 실행됩니다.
물론 위의 예는 대기 ()/notify ()/noitifyall () 메소드를 단순히 사용하는 것만으로도 비교적 간단하지만 본질적으로는 이미 간단한 생산자 소비자 모델입니다.
일련의 기사 :
Java 다중 스레드 인스턴스의 설명 (i)
Java 다중 스레드 인스턴스에 대한 자세한 설명 (II)
Java 다중 스레드 인스턴스에 대한 자세한 설명 (III)