ก่อนที่ Java 5 จะใช้คำหลักที่ซิงโครไนซ์เพื่อใช้ฟังก์ชันล็อค
คำหลักที่ซิงโครไนซ์สามารถใช้เป็นตัวดัดแปลง (วิธีการซิงโครไนซ์) หรือเป็นคำสั่งภายในฟังก์ชั่น (บล็อกรหัสที่ซิงโครไนซ์)
ในการซิงโครไนซ์หลักกุญแจสำคัญคือการใช้สิ่งนั้นเป็นล็อค สำหรับวิธีการที่ไม่คงที่ (วิธีการสมาชิก) ของคลาสนั้นหมายถึงการล็อคของอินสแตนซ์ของวัตถุ สำหรับวิธีการคงที่ (วิธีการคลาส) ของคลาสมีความจำเป็นที่จะต้องได้รับการล็อคของวัตถุคลาส สำหรับบล็อกรหัสแบบซิงโครนัสจำเป็นต้องระบุว่าล็อคของวัตถุใดได้รับ วิธีการที่ไม่คงที่แบบซิงโครไนซ์สามารถพิจารณาได้ว่าเป็นบล็อกรหัสที่ซิงโครไนซ์ (……} ที่มีวิธีการทั้งหมด
ไม่ว่าจะเป็นบล็อกรหัสแบบซิงโครนัสหรือวิธีการซิงโครไนซ์มีเพียงเธรดเดียวเท่านั้นที่สามารถป้อนได้ในแต่ละครั้ง (ส่วนใหญ่หนึ่งเธรดหนึ่งจะดำเนินการเซ็กเมนต์รหัสในเวลาเดียวกัน) และหากเธรดอื่นพยายามที่จะป้อน (ไม่ว่าจะเป็นบล็อกซิงโครนัสเดียวกันหรือบล็อกซิงโครไนซ์ที่แตกต่างกัน) JVM จะแขวนไว้ โครงสร้างนี้เรียกว่าส่วนที่สำคัญในทฤษฎีพร้อมกัน
ใน JVM เพื่อปรับปรุงประสิทธิภาพแต่ละเธรดที่ทำงานในเวลาเดียวกันจะมีสำเนาแคชของข้อมูลที่กำลังประมวลผล เมื่อเราใช้ซิงโครไนซ์สำหรับการซิงโครไนซ์สิ่งที่ซิงโครไนซ์จริงๆคือบล็อกหน่วยความจำที่แสดงถึงวัตถุที่ล็อคในเธรดที่แตกต่างกัน (ข้อมูลการคัดลอกจะยังคงซิงโครไนซ์กับหน่วยความจำหลักตอนนี้เรารู้แล้วว่าทำไมการซิงโครไนซ์คำ) พูดง่ายๆหลังจากใช้วิธีการซิงโครไนซ์บล็อกหรือวิธีการซิงโครไนซ์แล้วการแก้ไขใด ๆ ที่ทำกับวัตถุที่ล็อคจะต้องถูกเขียนกลับไปยังหน่วยความจำหลักก่อนที่จะปล่อยล็อค หลังจากป้อนบล็อกการซิงโครไนซ์และรับล็อคข้อมูลของวัตถุที่ล็อคจะถูกอ่านจากหน่วยความจำหลักและสำเนาข้อมูลของเธรดที่ถือล็อคจะต้องซิงโครไนซ์กับมุมมองข้อมูลในหน่วยความจำหลัก
ต่อไปนี้เป็นตัวอย่างเฉพาะเพื่อแสดงให้เห็นถึงสถานการณ์ต่าง ๆ ของการซิงโครไนซ์
วิธีการซิงโครไนซ์ซิงโครไนซ์
ก่อนอื่นมาดูตัวอย่างของวิธีการซิงโครไนซ์:
คลาสสาธารณะ SynchronizedTest1 ขยายเธรด {private synchronized void testsynchronizedMethod () {สำหรับ (int i = 0; i <10; i ++) {system.out.println (thread.currentthread () getName () + "testsynchronizedMethod:" + i); ลอง {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}} @Override โมฆะสาธารณะเรียกใช้ () {testsynchronizedMethod (); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {synchronizedTest1 t = ใหม่ synchronizedTest1 (); T.Start (); T.TestsynchronizedMethod (); -รันเอาต์พุตโปรแกรม:
TestsynchronizedMethod: 0 Main TestsynchronizedMethod: 1 Main TestsynchronizedMethod: 2 Main TestsynchronizedMethod: 3 Main TestsynchronizedMethod: 4 Main TestsynchronizedMethod: 5 TestsynchronizedMethod TestsynchronizedMethod: 9 Thread-0 TestsynchronizedMethod: 0 Thread-0 TestsynchronizedMethod: 1 Thread-0 TestsynchronizedMethod: 2 เธรด -0 TestsynchronizedMethod: 3 thread-0 testsynchronizedMethod: 4 เธรด TestsynchronizedMethod: 7 Thread-0 TestsynchronizedMethod: 8 Thread-0 TestsynchronizedMethod: 9
คุณจะเห็นได้ว่าวิธีการ testsynchronizedMethod นั้นถูกดำเนินการแบบซิงโครนัสระหว่างสองเธรด
หากวิธีการหลักได้รับการแก้ไขตามข้อมูลต่อไปนี้สองเธรดไม่สามารถดำเนินการแบบซิงโครนัสได้เนื่องจากการตรวจสอบการซิงโครไนซ์ของสองเธรดไม่ได้เป็นวัตถุเดียวกันและไม่สามารถเล่นบทบาทซิงโครนัสได้
โมฆะคงที่สาธารณะหลัก (สตริง [] args) {เธรด t = ใหม่ synchronizedTest1 (); T.Start (); เธรด t1 = ใหม่ synchronizedTest1 (); t1.start (); -ผลลัพธ์ผลลัพธ์มีดังนี้:
Thread-0 TestsynchronizedMethod: 0 Thread-1 TestsynchronizedMethod: 0 Thread-0 TestsynchronizedMethod: 1 Thread-1 TestsynchronizedMethod: 1 thread-0 testsynchronizedMethod: 2 thread-1 testsynchronizedMethod: 2 เธรด -0 -0 TestsynchronizedMethod: 4 Thread-1 TestsynchronizedMethod: 4 Thread-0 TestsynchronizedMethod: 5 Thread-1 TestsynchronizedMethod: 5 Thread-0 TestsynchronizedMethod: 6 Thread-1 testsynchronizedMethod: 6 เธรด -0 TestsynchronizedMethod: 8 Thread-1 TestsynchronizedMethod: 8 Thread-0 TestsynchronizedMethod: 9 Thread-1 TestsynchronizedMethod: 9
หากวิธีการหลักที่ได้รับการแก้ไขสามารถเรียกใช้แบบซิงโครนัสระหว่างสองเธรดวิธี testsynchronizedMethod จะต้องถูกประกาศเป็นวิธีการคงที่เพื่อให้การตรวจสอบของสองเธรดเป็นวัตถุเดียวกัน (วัตถุคลาส) และสามารถดำเนินการแบบซิงโครนัสได้ รหัสที่แก้ไขมีลักษณะเช่นนี้:
คลาสสาธารณะ SynchronizedTest1 ขยายเธรด {ส่วนตัวแบบคงที่แบบคงที่ซิงโครไนซ์ Void TestsynchronizedMethod () {สำหรับ (int i = 0; i <10; i ++) {system.out.println (thread.currentthread () getName () + "testsynchronizedMethod:" + i); ลอง {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}} @Override โมฆะสาธารณะเรียกใช้ () {testsynchronizedMethod (); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {เธรด t = ใหม่ synchronizedTest1 (); T.Start (); เธรด t1 = ใหม่ synchronizedTest1 (); t1.start (); -ผลลัพธ์ผลลัพธ์มีดังนี้:
Thread-0 TestsynchronizedMethod: 0 Thread-0 TestsynchronizedMethod: 1 Thread-0 TestsynchronizedMethod: 2 Thread-0 TestsynchronizedMethod: 3 Thread-0 TestsynchronizedMethod: TestsynchronizedMethod: 5 เธรด -0 TestsynchronizedMethod: 8 Thread-0 TestsynchronizedMethod: 9 Thread-1 TestsynchronizedMethod: 0 Thread-1 TestsynchronizedMethod: 1 Thread-1 testsynchronizedMethod: 2 tread-1 testsynchronizedMethod: 3 เธรด -1 testsynchronizedmethod: TestsynchronizedMethod: 7 Thread-1 TestsynchronizedMethod: 8 Thread-1 TestsynchronizedMethod: 9
สถานการณ์ของบล็อกแบบซิงโครนัสนั้นคล้ายกับวิธีการซิงโครไนซ์ยกเว้นว่าบล็อกซิงโครนัสจะลดความละเอียดของการควบคุมการซิงโครไนซ์ซึ่งสามารถออกแรงให้มีประสิทธิภาพของการดำเนินการแบบขนานแบบหลายเธรดได้ดีขึ้น
ใช้วัตถุนี้เพื่อควบคุมการซิงโครไนซ์ระหว่างอินสแตนซ์ของวัตถุเดียวกัน:
คลาสสาธารณะ SynchronizedTest2 ขยายเธรด {โมฆะส่วนตัว testsynchronizedBlock () {ซิงโครไนซ์ (นี่) {สำหรับ (int i = 0; i <10; i ++) {system.out.println (thread.currentthread () getName () + ลอง {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}}} @Override โมฆะสาธารณะเรียกใช้ () {testsynchronizedBlock (); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {synchronizedTest2 t = ใหม่ synchronizedTest2 (); T.Start (); T.TestsynchronizedBlock (); -ผลลัพธ์ผลลัพธ์:
TestsynchronizedBlock หลัก: 0 Main TestsynchronizedBlock: 1 Main TestsynchronizedBlock: 2 Main TestsynchronizedBlock: 3 Main TestsynchronizedBlock: 4 Main TestsynchronizedBlock: 5 TestsynchronizedBlock: 6 Main TestsynchronizedBlock: 7 TestsynchronizedBlock: 0 Thread-0 TestsynchronizedBlock: 1 Thread-0 TestsynchronizedBlock: 2 เธรด -0 TestsynchronizedBlock: 3 Thread-0 TestsynchronizedBlock: 4 Thread-0 TestsynchronizedBlock: 5 เธรด -0 TestsynchronizedBlock: 9
ใช้วัตถุคลาสเพื่อควบคุมการซิงโครไนซ์ระหว่างอินสแตนซ์ที่แตกต่างกัน:
คลาสสาธารณะ SynchronizedTest2 ขยายเธรด {โมฆะส่วนตัว testsynchronizedBlock () {ซิงโครไนซ์ (synchronizedTest2.class) {สำหรับ (int i = 0; i <10; i ++) {system.out.println (Thread.currentthread () ลอง {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}}} @Override โมฆะสาธารณะเรียกใช้ () {testsynchronizedBlock (); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {เธรด t = ใหม่ synchronizedTest2 (); T.Start (); เธรด t2 = ใหม่ synchronizedTest2 (); t2.start (); -ผลลัพธ์ผลลัพธ์:
Thread-0 TestsynchronizedBlock: 0 Thread-0 TestsynchronizedBlock: 1 Thread-0 TestsynchronizedBlock: 2 Thread-0 TestsynchronizedBlock: 3 Thread-0 TestsynchronizedBlock: 4 Thread-0 TestsynchronizedBlock: 5 Thread-0 testsynchronizedblock: Thread-0 TestsynchronizedBlock: 9 Thread-1 TestsynchronizedBlock: 0 Thread-1 TestsynchronizedBlock: 1 Thread-1 TestsynchronizedBlock: 2 Thread-1 TestsynchronizedBlock: 3 Thread-1 TestsynchronizedBlock: 4 Thread-1 TestsynchronizedBlock: 5 เธรด -1 TestsynchronizedBlock: 8 Thread-1 TestsynchronizedBlock: 9
เมื่อใช้คำหลักที่ซิงโครไนซ์สำหรับการควบคุมการซิงโครไนซ์คุณต้องเข้าใจการตรวจสอบวัตถุ เฉพาะกระบวนการที่ได้รับจอภาพเท่านั้นที่สามารถทำงานได้และทุกอย่างอื่น ๆ จะต้องรอเพื่อรับจอภาพ วัตถุที่ไม่ใช่ NULL ใด ๆ สามารถใช้เป็นจอภาพวัตถุ เมื่อซิงโครไนซ์ทำหน้าที่ในวิธีการอินสแตนซ์ของวัตถุจะถูกล็อค เมื่อทำหน้าที่กับวิธีการคงที่อินสแตนซ์ของวัตถุจะถูกล็อคสอดคล้องกับวัตถุ
วิธีการแบบซิงโครนัสสำหรับสองเธรดเพื่อเข้าถึงวัตถุในเวลาเดียวกัน
เมื่อสองเธรดพร้อมกันเข้าถึงวิธีการซิงโครนัสของวัตถุเดียวกันสามารถดำเนินการเธรดได้เพียงหนึ่งเธรด เธรดอื่นจะต้องรอให้เธรดปัจจุบันดำเนินการนี้ก่อนที่จะสามารถดำเนินการได้
คลาสสาธารณะ twothread {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สุดท้าย twothread twothread = new twothread (); เธรด t1 = เธรดใหม่ (ใหม่ runnable () {public void run () {twothread.syncmethod ();}}, "a"); เธรด t2 = เธรดใหม่ (ใหม่ runnable () {public void run () {twothread.syncmethod ();}}, "b"); t1.start (); t2.start (); } public synchronized void syncMethod () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (thread.currentthread (). getName () + ":" + i); ลอง {thread.sleep (500); } catch (interruptedException ie) {}}}}ผลลัพธ์ผลลัพธ์:
A: 0a: 1a: 2a: 3a: 4b: 0b: 1b: 2b: 3b: 4
วิธีการซิงโครไนซ์ของสองวัตถุนั้นเข้าถึงได้โดยสองเธรด
ในกรณีนี้การซิงโครไนซ์ไม่ทำงานเหมือนกับวิธีการธรรมดา เพราะล็อคที่สอดคล้องกันเป็นวัตถุที่เกี่ยวข้อง
คลาสสาธารณะ twoObject {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สุดท้ายสองวัตถุ object1 = ใหม่ twoobject (); เธรด t1 = เธรดใหม่ (ใหม่ runnable () {public void run () {object1.syncmethod ();}}, "Object1"); t1.start (); สอง object 2 = ใหม่ twoobject (); เธรด t2 = เธรดใหม่ (ใหม่ runnable () {public void run () {public void run () {object2.syncmethod ();}}, "Object2"); t2.start (); } public synchronized void syncMethod () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (thread.currentthread (). getName () + ":" + i); ลอง {thread.sleep (500); } catch (interruptedException ie) {}}}}หนึ่งในผลลัพธ์ที่เป็นไปได้:
Object2: 0Object1: 0Object1: 1Object2: 1Object2: 2Object1: 2Object2: 3Object1: 3Object1: 4Object2: 4
สองเธรดเข้าถึงวิธีการแบบซิงโครไนซ์
ในกรณีนี้เนื่องจากคลาสถูกล็อคได้ตลอดเวลามีเพียงหนึ่งเธรดเดียวเท่านั้นที่สามารถเรียกใช้วิธีการคงที่
การเข้าถึงวิธีการแบบซิงโครนัสและวิธีการแบบอะซิงโครนัสพร้อมกันเมื่อเธรดหนึ่งเข้าถึงวิธีการซิงโครไนซ์หนึ่งวิธีของวัตถุเธรดอื่นยังสามารถเข้าถึงวิธีการแบบอะซิงโครนัสในวัตถุนั้น
คลาสสาธารณะ syncandnosync {public static void main (string [] args) {สุดท้าย syncandnosync syncandnosync = syncandnosync () ใหม่ (); เธรด t1 = เธรดใหม่ (ใหม่ runnable () {public void run () {syncandnosync.syncmethod ();}}, "a"); t1.start (); เธรด t2 = เธรดใหม่ (ใหม่ runnable () {public void run () {syncandnosync.nosyncmethod ();}}, "b"); t2.start (); } public synchronized void syncMethod () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (thread.currentthread (). getName () + "ที่ syncMethod ():" + i); ลอง {thread.sleep (500); } catch (interruptedException ie) {}}} โมฆะสาธารณะ nosyncMethod () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (thread.currentthread () getName () + "ที่ nosyncMethod ():" + i); ลอง {thread.sleep (500); } catch (interruptedException ie) {}}}}เอาต์พุตที่เป็นไปได้อย่างหนึ่ง:
B ที่ nosyncMethod (): 0a ที่ syncmethod (): 0b ที่ nosyncmethod (): 1a ที่ syncmethod (): 1b ที่ nosyncmethod (): 2a ที่ syncmethod (): 2b ที่ nosyncmethod (): 3a at syncmethod (): 3a nosyncMethod (): 4
วิธีการซิงโครไนซ์ที่แตกต่างกันสำหรับการเข้าถึงวัตถุเดียวกัน
เมื่อเธรดเข้าถึงวิธีการซิงโครไนซ์ A ของวัตถุเธรดอื่น ๆ จะเข้าถึงวิธีการซิงโครไนซ์อื่น ๆ ทั้งหมดในวัตถุจะถูกบล็อก เนื่องจากเธรดแรกได้รับการล็อควัตถุและเธรดอื่น ๆ ไม่สามารถรับล็อคได้แม้ว่ามันจะเข้าถึงวิธีที่แตกต่างกัน แต่ก็ไม่ได้รับการล็อคและไม่สามารถเข้าถึงได้
ระดับสาธารณะ twosyncMethod {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สุดท้าย twosyncMethod twosyncMethod = ใหม่ twosyncMethod (); เธรด t1 = เธรดใหม่ (ใหม่ runnable () {public void run () {twosyncMethod.syncMethod1 ();}}, "a"); t1.start (); เธรด t2 = เธรดใหม่ (ใหม่ runnable () {public void run () {twosyncMethod.syncMethod2 ();}}, "b"); t2.start (); } public synchronized void syncMethod1 () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (thread.currentthread (). getName () + "ที่ syncMethod1 ():" + i); ลอง {thread.sleep (500); } catch (interruptedException IE) {}}} โมฆะที่ซิงโครไนซ์สาธารณะ syncMethod2 () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (thread.currentthread () getName () + ลอง {thread.sleep (500); } catch (interruptedException ie) {}}}}ผลลัพธ์ผลลัพธ์:
A ที่ syncMethod1 (): 0a ที่ syncMethod1 (): 1a ที่ syncMethod1 (): 2a ที่ syncMethod1 (): 3a ที่ syncMethod1 (): 4b ที่ syncMethod2 (): 0B ที่ syncMethod2 () syncMethod2 (): 4