키워드 동기화
동기화 된 키는 함수 내에서 함수와 문을 수정할 수 있습니다. 메소드 나 객체에 추가 되든, 획득하는 잠금은 코드 나 기능을 잠금으로 처리하는 대신 객체입니다.
1. 동시 스레드가 동일한 객체에서 동기화 된 코드 블록에 액세스하면 한 스레드 만 일정 시간 동안 실행할 수 있으며 다른 스레드는 현재 스레드가 실행 된 후에 만 실행할 수 있습니다.
2. 스레드가 객체에서 동기화 된 (이) 동기화 된 코드 블록에 액세스하면 다른 스레드는이 객체의 다른 비 동기화 (이) 코드 블록에 여전히 액세스 할 수 있습니다.
3. 여기서 스레드가 객체의 동기화 된 (이) 코드 블록에 액세스 할 때이 객체의 다른 동기화 된 (이) 동기화 된 코드 블록에 액세스 할 수없는 다른 스레드가 차단됩니다.
4. 위는 다른 동기화 코드 블록에도 적용됩니다. 또한 각 객체 (즉, 클래스 인스턴스)는 잠금에 해당합니다. 속성이 차단되면 메소드가 실행되면 잠금이 메소드에서 돌아와서 실행 가능한 상태를 다시 입력 할 때까지 독점적으로 점유됩니다. 이 메커니즘은 동시에, 각 객체에 대해 동시에 동기화 된 모든 멤버 기능 중 하나가 실행 가능 상태에 있음을 보장합니다 (대부분의 스레드는 객체의 잠금을 얻을 수 있기 때문에 클래스 멤버 변수 충돌에 대한 액세스를 피합니다. .
동기화 된 방법의 단점 :
동기화 된 것은이 동기화 방법을 호출하는 객체를 잠그기 때문에, 즉 스레드 P1이 다른 스레드 에서이 메소드를 실행할 때 상호 배제를 형성하여 동기화 효과를 달성합니다. 그러나 여기서이 객체의 다른 클래스 객체는 동기화 된 키워드가 추가 된 상태 에서이 메소드를 임의로 호출 할 수 있다는 점에 주목해야합니다. 동기화 방법의 본질은 P1 객체 잠금을 얻은 스레드만이 동기화 된 방법을 호출 할 수 있습니다 이 경우. 메커니즘의 제어는 데이터 혼란을 유발합니다. 우리는이 상황을 아래에서 자세히 설명 할 것입니다.
먼저 동기화 된 키워드와 함께 잠긴 객체 두 개를 소개하겠습니다. 객체 및 클래스 - 동기화 된 클래스 잠금 장치는이 클래스의 모든 객체 (인스턴스)에 작동 할 수 있으며 개체 잠금은 지정된 개체에 대한 잠금입니다. 이 클래스 에서이 클래스의 다른 객체는 여전히 이전 객체를 잠긴 동기화 된 메소드를 사용할 수 있습니다.
우리가 여기서 논의하는 주요 문제 중 하나는 다음과 같습니다. "동일한 클래스와 다른 인스턴스가 동일한 방법을 부를 것인가, 동기화 문제가 있습니까?"
동기화 문제는 리소스와 관련이 있으며 리소스가 정적인지 여부에 따라 다릅니다. 동일한 정적 데이터의 경우 동일한 기능이 다른 스레드에 속하며 동시에 읽습니다 당신. 어떤 종류의 동기화가 필요한지. CPU의 두 가지 다른 코어로 실행되고 동시에 메모리 주소를 작성하는 두 개의 다른 코드가 있더라도 캐시 메커니즘은 L2에서 먼저 잠겨 있습니다. 그런 다음 다른 코어와 업데이트하고 공유하면 오류가 없으면 인텔 또는 AMD가 헛된 것입니다.
따라서 두 코드가 공유하는 것과 동일한 리소스 나 변수가없는 한 데이터 불일치가 없습니다. 또한, 동일한 클래스의 다른 개체에 대한 호출은 완전히 다른 스택을 가지고 있으며 완전히 관련이 없습니다.
여기서 우리는 예제를 사용하여 티켓 판매 프로세스를 설명합니다. 공유 리소스는 나머지 티켓 수입니다.
com.test; public class threadsafetest는 runnable {stringsafetest (string name) {public void run () {sell (getName ()); void sell (string name) {if (num> 0) {name + ": out.println (약 5 초 안에 완료); } e.printstacktrace () {else.println : 티켓 중지} {private static void printnuminfo () "시스템 :" + num) {println args []) {새로운 ThreadSafetest ( "티켓 판매자 li xx". {e.printstacktrace (); 위의 코드를 실행하면 우리가 얻는 출력은 다음과 같습니다.
티켓 지휘자 LI XX : 테스트 티켓의 수는 0보다 큽니다. 티켓 지휘자 LI XX : 지불이 수집됩니다 (약 5 초 안에 완료). . . 티켓 판매자 킹 X : 테스트 티켓 수는 0보다 큽니다. 티켓 판매자 X : 지불이 수집됩니다 (약 5 초 안에 완료). . . 티켓 판매자 LI XX : 청구서, 티켓 판매 완료 시스템 인쇄 : 현재 투표 수 : 0 티켓 판매자 Wang X : 청구서 인쇄, 티켓 판매 완료 시스템 : 현재 투표 수 : -1 경고 : 투표 수는 낮습니다. 0, 음수가 나타납니다
출력 결과에 따라 나머지 투표는 -1이고 동기화 오류 문제가 있음을 알 수 있습니다. 그 이유는 우리가 만든 두 인스턴스 객체가 공유 정적 자원 정적 int num = 1을 동시에 수정했기 때문입니다. 그런 다음 위 코드의 상자에서 수정 자 정적을 제거한 다음 프로그램을 실행하여 다음을 수행합니다.
티켓 지휘자 LI XX : 테스트 티켓의 수는 0보다 큽니다. 티켓 지휘자 LI XX : 지불이 수집됩니다 (약 5 초 안에 완료). . . 티켓 판매자 킹 X : 테스트 티켓 수는 0보다 큽니다. 티켓 판매자 X : 지불이 수집됩니다 (약 5 초 안에 완료). . . 티켓 판매자 LI XX : 청구서, 티켓 판매 완료 시스템 인쇄 : 현재 티켓 수 : 0 티켓 판매자 Wang X : 청구서 인쇄, 티켓 판매 완료 시스템 : 현재 티켓 수 : 0
학위를 수정 한 후 프로그램은 문제없이 실행됩니다. 그러나 이는 여러 스레드가 공유 리소스를 동시에 처리 할 수 있다는 기대에 위배됩니다 (정적 후에는 공유 리소스에서 각 인스턴스가 소유 한 멤버 변수로 NUM이 변경됨)는 분명히 원하는 것이 아닙니다.
위의 두 코드에서 채택해야 할 주요 사항은 객체를 잠그는 것입니다. 앞에서 언급 한 이유로, 클래스의 두 가지 다른 사례가 동일한 공유 리소스를 수정하면 CPU가 프로그램의 논리에 기본적으로 기본적으로 제공됩니다. 따라서, 우리는 잠금의 범위를 변경해야합니다. 대상이 인스턴스 인 경우이 문제는 클래스의 범위가 전체 클래스 인 경우에만 불가피합니다. 동시에 수정 문제.
com.test; public class threadsafetest는 runnable {stringsafetest (string name) {public void run () {sell (getName ()); static void sell (string name) {if (num> 0) {name + ": out.println (name +”: /t 결제 (약 5 초); (); e.printstacktrace ()} else.println : private static void printnuminfo () .println (System : 현재 투표 수 : if <0) {경고 : 투표는 0보다 낮습니다}} String args []) {새로운 The Screadsafetest (Tickets Seller Li XX) .Slea (2000); ) {e.printstacktrace (); 실행 결과를 얻으려면 위와 같이 프로그램을 만드십시오.
티켓 지휘자 LI XX : 테스트 티켓의 수는 0보다 큽니다. 티켓 지휘자 LI XX : 지불이 수집됩니다 (약 5 초 안에 완료). . . 티켓 판매자 LI XX : 티켓, 티켓 판매 완료 시스템 인쇄 : 현재 티켓 수 : 0 티켓 판매자 Wang X : 티켓 없음, 티켓 판매 중지
정적 수정자가 Sell () 메소드에 추가되므로 클래스의 인스턴스가 공유 변수에서 작동하면 클래스의 다른 인스턴스가 그 작업을 차단합니다. 이것은 우리가 원하는대로 원하는 결과를 얻을 것입니다.
요약 :
1. 동기화 된 키워드의 두 가지 사용이 있습니다 : 동기화 된 메소드 및 동기화 된 블록.
2. Java에서는 클래스 인스턴스 일뿐 만 아니라 각 클래스가 동기화 된 키워드를 사용할 때 다음과 같은 점에 해당 할 수 있습니다.
1. 동기화 된 키워드를 상속받을 수 없습니다. 동기화 된 방법을 정의하는 데 동기화 될 수 있지만 동기화 된 것은 메소드 정의의 일부에 속하지 않으므로 동기화 된 키워드를 상속받을 수 없습니다. 부모 클래스의 메소드가 동기화 된 키워드를 사용하고 서브 클래스 가이 메소드를 대체하는 경우, 기본적으로 서브 클래스 의이 메소드는 동기화되지 않으며 서브 클래스에서만 메소드를 추가 할 수 있습니다 . 물론, 서브 클래스의 메소드는 동기화되지 않지만 서브 클래스의 동기식 메소드를 호출하지만 서브 클래스의 메소드는 서브 클래스에서 해당 메소드를 호출 할 수 있습니다. 동기화되고 있습니다. 좋다,
서브 클래스에 동기화 된 키워드 추가 :
class parent {public synchronized void method () {}} class child 확장 부모 {public synchronized void method () {}} 상위 클래스 방법을 호출하십시오.
Class Parent {public synchronized void method () {}} class child는 parent {public void method () {super.method ()}; 2. 인터페이스 메소드를 정의 할 때 동기화 된 키워드를 사용할 수 없습니다.
3. 생성자는 동기화 된 키워드를 사용할 수 없지만 동기화 된 블록을 동기화하는 데 사용할 수 있습니다.
4. 동기화 된 위치는 자유롭게 배치 될 수 있지만 방법의 리턴 유형 뒤에 배치 할 수는 없습니다.
5. 동기화 된 키워드를 사용하여 다음 코드가 올바르지 않은 변수를 동기화 할 수 없습니다.
public synchronized int n = 0;
6. 동기화 된 키워드를 사용하는 것이 가장 안전한 동기화 방법이지만 대량으로 사용하는 경우 불필요한 자원 소비 및 성능 손실도 발생합니다. 표면적으로 동기화 된 방법은 메소드를 잠그지 만 동기화 된 키워드는 메소드 중 하나와 메소드를 실행할 때 사용됩니다. 실행. 정적 방법은 비 정적 방법과 유사합니다. 그러나 정적 메소드와 비 정적 메소드는 서로 영향을 미치지 않습니다. 다음 코드를 참조하십시오.
공개 클래스 Mythread1은 {public String MethodName; ");} public synchronized void method2 () {method ("non-static method2 method ");} public static synchronized void method3 () {method ("static method3 method ");} public static synchronized void method4 () {메소드 ( "static method4 method"); public void run () {getclass (). ) 예외 {mythread1 = new Mythread1 (int i = 1; i <= 4; i ++) {mythread1. 수면 (100); 실행 결과는 다음과 같습니다.
비 정적 방법 1 메소드 정적 방법 3 메소드
위의 실행 결과로부터, 우리는 method2 및 method4가 방법 1 및 method3가 완료 될 때까지 실행되지 않는다는 것을 알 수있다. 따라서 클래스에서 비 정적 메소드를 정의하기 위해 동기화 된 경우이 클래스의 모든 동기화 된 정적 메소드에 영향을 미치는 경우 다음 방법에 영향을 미칩니다. 이 클래스에서는 동기화로 정의 된 정적 메소드입니다. 이것은 레코드가 수정되면 전체 테이블을 잠그는 것과 비슷합니다. 따라서이 동기화 방법을 많이 사용하면 프로그램의 성능이 크게 줄어 듭니다.
공유 리소스에 대한 안전한 액세스를위한 팁 :
1. 공개/보호의 인스턴스 변수를 정의하는 대신 private + get 메소드의 인스턴스 변수를 정의하십시오. 변수가 공개로 정의되면 객체는 외부 세계에서 동기화 방법의 제어를 우회하여 변경할 수 있습니다. 이것은 또한 Javabean의 표준 구현 중 하나입니다.
2. 인스턴스 변수가 배열 또는 배열리스트와 같은 객체 인 경우 외부 세계가 GET 메소드를 통해 인스턴스 개체에 대한 참조를 가져 와서 다른 객체를 가리키기 때문에 위의 메소드는 여전히 안전하지 않습니다. 개인 변수도 변경되었으며 매우 위험하지 않습니까? 이 시점에서는 메소드를 얻기 위해 동기화 된 추가 및이 개인 객체의 클론 () 만 반환해야합니다. 이런 식으로 발신자가 얻는 것은 객체 사본에 대한 참조 일뿐입니다.
객체 모니터 (잠금) 및 notify ()를 얻는 세 가지 방법
스레드 메소드에서는 대기 () 및 notify ()가 객체 객체를 지정해야하며 스레드에는 객체 객체의 모니터가 있어야합니다. 객체 모니터를 얻는 가장 쉬운 방법은 객체에서 동기화 된 키워드를 사용하는 것입니다. Wait () 메소드를 호출 한 후 스레드는 객체 잠금 장치를 해제하고 수면 상태로 들어갑니다. 다른 스레드가 Notify () 메소드를 호출하면 동일한 객체 객체를 사용해야합니다.
객체에 의해 잠겨있는 여러 메소드의 경우 Notify () 메소드를 호출 할 때 중 하나가 깨어나도록 선택되며 NotifyAll ()은 대기 스레드를 모두 깨우게됩니다.
PACKED.MindView.util; import Javax.swing.jframe; public static void main (string [] args) {waitandnotifyjframe 프레임 (); frame.setDefaultCloseOperation (Jframe. exit_on_close). 300, 100); SetLocation (250, 250); JBUTTON START = New JBUTTON ( "Start") {public void ActionPerformed (ActionEven T event) {if (t == null). {t = waitandnotifyjframe (t.start); .add (start); Jbutton pause = new jbutton ( "pause") {public void actionperformed (actionevent e) {if (t! = null) {t. iswait = true;}}); (일시 정지); jbutton end = new Jbutton ( "end") {public void actionPerformed (actionEvent e) {if (t! = null) {t.interrupt (); t = null;}}); .add (end) = f; count = 0; if (iswait) wait ()}} catch (예외 e) {public void n () {notify ()}; 위의 예제 상자의 코드에서와 같이 동기 코드 블록이 제거되면 실행에 java.lang.ilegalmonitorstateException 예외가 발생합니다.
JDK를 살펴보면이 예외의 이유는 현재 스레드 가이 객체 모니터의 소유자가 아니기 때문입니다.
이 메소드는이 객체 모니터의 소유자 인 스레드에서만 호출해야합니다.
1.이 개체의 동기 인스턴스 메소드를 실행하여 :
public synchronized void n () {notify (); 2.이 개체에서 동기화 된 동기화 된 문의 본체를 실행함으로써 :
public void n () {synchronized (this) {notify ()}; 3. 클래스 유형의 객체의 경우이 클래스의 동기식 정적 메소드를 실행할 수 있습니다.
정적 메소드를 호출 할 때 반드시 인스턴스 객체를 생성 할 필요는 없습니다. 따라서 이것은 정적 메소드를 동기화하는 데 사용될 수 없으므로 클래스 객체를 사용하여 정적 메소드를 동기화해야합니다. 설명 할 또 다른 예 :
공개 클래스 동기화 된 스타트 핑 가능한 {private static boolean flag = true; // 클래스 객체 동기화 메소드 1 : // 정적 수정의 동기화 메소드에주의를 기울이십시오. I = 0; i ++; // 클래스 객체 동기화 방법 2 : 개인 void testsyncBlock () {// display는 클래스를 모니터로 사용합니다. 정적 동기화 된 메소드가 암시 적으로 클래스 모니터를 가져 오는 것과 동일합니다. 동기화 된 (int i = 0; i <100; ( "TestSyncBlock :" + i)} public void run () {// 플래그는 정적 변수입니다. 따라서 다른 스레드는 다른 방법을 실행할 것이므로 이러한 방식으로 만 다른 잠금 효과를 볼 수 있습니다. if (flug) {false} testsyncblock (); = 새로운 SynchronizedStatic RT1; 위의 코드는 동시에 100 개의 숫자를 인쇄하는 두 개의 동기화 방법을 실행하여 정적 동기화 메소드입니다 블록. 그것은 클래스입니다. 두 가지 방법은 비슷합니다. 메소드 1과 메소드 2의 범위는 두 가지 클래스이므로,이 두 가지 메소드 중 하나를 호출 할 때 나머지 비밀 방법은 다른 스레드를 형성합니다. 따라서 프로그램의 실행 결과는 다음과 같습니다.
TestSyncMethod : 0TestSyncMethod : 1 ... ... TestSyncMethod : 99TestSyncBlock : 0 ... ... TestSyncBlock : 99
그러나 방법 2의 동기화 된 클래스를 범위가 없기 때문에이 두 가지 방법은 상호 배제를 형성하지 않으며 프로그램의 출력 결과는 다음과 같습니다.
TestSyncBlock : 0TestSyncMethod : 0TestSyncBlock : 1TestSyncMethod : 1 ... ... TestSyncMethod : 99TestSyncBlock : 99
자물쇠의 두 가지 범위가 있으며, 하나는 클래스의 대상이고 다른 하나는 클래스 자체입니다. 위의 코드에서는 동일한 클래스의 다른 객체간에 동기화를 완료 할 수 있도록 클래스를 클래스로 만들기위한 두 가지 방법이 제공됩니다.
위의 내용을 요약하려면 다음 사항에 주목해야합니다.
1. 대기 (), notify () 및 notifyall ()은 객체 모니터를 갖는 전제에 따라 실행해야합니다.
2. 여러 스레드가 동시에 하나의 물체에서 대기 할 수 있습니다.
3. Notify ()는 객체에서 대기하는 스레드를 무작위로 깨우는 것입니다.
4. notify ()가 깨어 난 스레드는 Notify ()가 실행 된 직후에 깨어나지 않고 Notify () 스레드가 객체 모니터를 릴리스 한 후에 만 깨어납니다.
5. 이러한 물체의 방법은 여전히 실의 수면과 인터럽트 방법과는 거리가 멀으므로 혼동하지 마십시오.