ใช้ wait () และแจ้ง () เพื่อให้เกิดการทำงานร่วมกันระหว่างเธรด
1. รอ () และแจ้ง ()/แจ้งเตือน ()
เมื่อเรียกว่า Sleep () และ Dielt () การล็อคจะไม่ถูกปล่อยออกมาและการโทร Wait () จะปล่อยล็อค ด้วยวิธีนี้งานอื่น (เธรด) สามารถรับล็อคของวัตถุปัจจุบันได้ดังนั้นจึงป้อนวิธีการซิงโครไนซ์ คุณสามารถดำเนินการดำเนินการต่อจากการรอ () ผ่านการแจ้งเตือน ()/notifyall () หรือเวลาหมดอายุ
รอ (), แจ้ง () และแจ้งเตือน () สามารถเรียกใช้ในวิธีการควบคุมการซิงโครไนซ์หรือบล็อกการซิงโครไนซ์เท่านั้น หากวิธีการเหล่านี้ถูกเรียกในวิธีการแบบอะซิงโครนัสข้อยกเว้นที่ผิดกฎหมาย regainalMonitorStateException จะถูกโยนลงในรันไทม์
2. จำลองการปลุกหลายเธรดด้วยเธรดเดียว
จำลองการทำงานร่วมกันระหว่างเธรด คลาสเกมมี 2 วิธีการซิงโครไนซ์เตรียม () และไป () การเริ่มต้นธงจะใช้เพื่อตรวจสอบว่าเธรดปัจจุบันต้องการรอ () หรือไม่ อินสแตนซ์ของคลาสเกมเริ่มต้นอินสแตนซ์คลาส Athele ทั้งหมดก่อนและป้อนสถานะการรอ () หลังจากระยะเวลาหนึ่งให้เปลี่ยนบิตธงและแจ้งเตือน () เธรด Athele ทั้งหมดในสถานะรอ
game.java
แพ็คเกจพร้อมกันนำเข้า java.util.collection; นำเข้า java.util.collections; นำเข้า java.util.hashset; นำเข้า java.util.iterator; นำเข้า java.util.set; นักกีฬาชั้นเรียน เกมเกมส่วนตัว นักกีฬาสาธารณะ (ID int, เกมเกม) {this.id = id; this.game = game; } บูลีนสาธารณะเท่ากับ (วัตถุ o) {ถ้า (! (o อินสแตนซ์ของนักกีฬา)) คืนค่าเท็จ; นักกีฬานักกีฬา = (นักกีฬา) o; return id == athlete.id; } สตริงสาธารณะ toString () {return "นักกีฬา <" + id + ">"; } public int hashCode () {ส่งคืนจำนวนเต็มใหม่ (id) .hashCode (); } public void run () {ลอง {game.prepare (นี่); } catch (interruptedException e) {system.out.println (this + "ออกจากเกม"); }}} เกมคลาสสาธารณะใช้งาน Runnable {Private Set <Athlete> Players = new HashSet <Athlete> (); บูลีนส่วนตัวเริ่มต้น = เท็จ; โมฆะสาธารณะ addplayer (Athlete One) {players.add (หนึ่ง); } โมฆะสาธารณะ removePlayer (Athlete One) {players.remove (หนึ่ง); } คอลเล็กชันสาธารณะ <Sthlete> getPlayers () {return collections.unmodifiableset (ผู้เล่น); } โมฆะสาธารณะเตรียม (นักกีฬานักกีฬา) พ่น InterruptedException {System.out.println (นักกีฬา + "พร้อม!"); ซิงโครไนซ์ (นี่) {ในขณะที่ (เริ่มต้น) รอ (); if (start) system.out.println (นักกีฬา + "go!"); }} โมฆะที่ซิงโครไนซ์สาธารณะไป () {notifyAll (); } โมฆะสาธารณะพร้อม () {iterator <Sthlete> iter = getPlayers (). iterator (); ในขณะที่ (iter.hasnext ()) เธรดใหม่ (iter.next ()). start (); } โมฆะสาธารณะเรียกใช้ () {start = false; System.out.println ("พร้อม ... "); System.out.println ("พร้อม ... "); System.out.println ("พร้อม ... "); พร้อม(); เริ่ม = จริง; System.out.println ("Go!"); ไป(); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {game game = new game (); สำหรับ (int i = 0; i <10; i ++) game.addplayer (นักกีฬาใหม่ (i, game)); เธรดใหม่ (เกม). start (); - ผลลัพธ์:
พร้อม ... พร้อม ... พร้อม ... นักกีฬา <0> พร้อมแล้ว! Athlete <1> พร้อมแล้ว! Athlete <2> พร้อมแล้ว! Athlete <3> Ready! Athlete <4> พร้อมแล้ว! Athlete <5> พร้อม! Athlete <6> พร้อม! ไป! นักกีฬา <5> ไป! นักกีฬา <4> ไป! นักกีฬา <3> ไป! นักกีฬา <2> ไป! นักกีฬา <1> ไป! นักกีฬา <0> ไป!
3. จำลองกระบวนการรอคอยที่วุ่นวาย
อินสแตนซ์ของคลาส MyObject คือผู้สังเกตการณ์ เมื่อเหตุการณ์การสังเกตเกิดขึ้นมันจะแจ้งอินสแตนซ์ของคลาสจอภาพ (วิธีการเปลี่ยนธง) อินสแตนซ์ของคลาสจอภาพนี้ตรวจสอบอย่างต่อเนื่องว่าบิตธงจะเปลี่ยนแปลงโดยการรอคอยหรือไม่
busy waiting.java
นำเข้า java.util.concurrent.timeUnit; Class MyObject ดำเนินการ unnable {จอภาพส่วนตัว สาธารณะ myObject (จอภาพตรวจสอบ) {this.monitor = monitor; } public void run () {ลอง {timeUnit.seconds.sleep (3); System.out.println ("ฉันกำลังจะไป"); monitor.gotMessage (); } catch (interruptedException e) {e.printStackTrace (); }}} การตรวจสอบคลาสใช้งาน runnable {บูลีนผันผวนส่วนตัว GO = false; โมฆะสาธารณะ gotMessage () พ่น InterruptedException {go = true; } การดูโมฆะสาธารณะ () {ในขณะที่ (ไป == เท็จ); System.out.println ("เขาไปแล้ว"); } public void run () {ดู (); }} คลาสสาธารณะ Busy Waiting {โมฆะสาธารณะคงที่หลัก (String [] args) {monitor monitor = new Monitor (); myObject o = ใหม่ myObject (จอภาพ); เธรดใหม่ (o). start (); เธรดใหม่ (มอนิเตอร์). start (); - ผลลัพธ์:
ฉันจะไปแล้วเขาไปแล้ว
4. ใช้ wait () และแจ้ง () เพื่อเขียนตัวอย่างข้างต้นใหม่
ตัวอย่างต่อไปนี้แทนที่กลไกการรอคอยที่ไม่ว่างผ่านการรอ () เมื่อได้รับข้อความการแจ้งเตือนให้แจ้งเธรดคลาสการตรวจสอบปัจจุบัน
รอ. java
แพ็คเกจพร้อมกันการรอ; นำเข้า java.util.concurrent.timeUnit; คลาส MyObject ดำเนินการ unnable {มอนิเตอร์ส่วนตัว; สาธารณะ myObject (จอภาพตรวจสอบ) {this.monitor = monitor; - เริ่มเธรดเป็นประจำ
นี่คือสองวิธีในการเริ่มต้นเธรดหลังจากเวลาที่กำหนด ก่อนอื่นมันถูกนำไปใช้ผ่าน java.util.concurrent.delayqueue; ประการที่สองมันถูกนำไปใช้ผ่าน java.util.concurrent.scheduledthreadpoolexecutor
1. java.util.concurrent.delayqueue
คลาส delayqueue เป็นคิวการบล็อกที่ไม่มีขอบเขตซึ่งองค์ประกอบสามารถสกัดได้ก็ต่อเมื่อความล่าช้าหมดอายุ ยอมรับอินสแตนซ์ที่ใช้อินเทอร์เฟซล่าช้าเป็นองค์ประกอบ
<< อินเทอร์เฟซ >> ล่าช้า Java
แพ็คเกจ java.util.concurrent; นำเข้า java.util.*; อินเตอร์เฟสสาธารณะล่าช้าขยายการเปรียบเทียบ <layed> {long getDelay (หน่วย TimeUnit);}GetDelay () ส่งคืนเวลาหน่วงเวลาที่เหลือที่เกี่ยวข้องกับวัตถุนี้ซึ่งแสดงในหน่วยเวลาที่กำหนด การใช้งานอินเทอร์เฟซนี้จะต้องกำหนดวิธีการเปรียบเทียบที่ให้การเรียงลำดับที่สอดคล้องกับวิธี getDelay ของอินเทอร์เฟซนี้
หัวของคิวการหน่วงเวลาเป็นองค์ประกอบที่ล่าช้าด้วยเวลาการจัดเก็บที่ยาวที่สุดหลังจากการหน่วงเวลาหมดอายุ การหมดอายุเกิดขึ้นเมื่อวิธีการ getDelay (timeUnit.nanoseconds) ขององค์ประกอบส่งคืนค่าน้อยกว่าหรือเท่ากับ 0
2. การออกแบบคิวที่มีลักษณะการหน่วงเวลา
คลาส DelayedTasker ดูแลคิว DelayQueue <LeLyEdTask> ซึ่ง DelayEdTask ใช้อินเทอร์เฟซที่ล่าช้าและถูกกำหนดโดยคลาสภายใน ทั้งคลาสภายนอกและคลาสภายในใช้อินเทอร์เฟซที่รันได้ สำหรับคลาสภายนอกวิธีการเรียกใช้จะนำงานออกมาในคิวตามลำดับตามเวลาที่กำหนดและงานเหล่านี้เป็นอินสแตนซ์ของคลาสภายใน วิธีการเรียกใช้ของคลาสภายในกำหนดตรรกะเฉพาะของแต่ละเธรด
สาระสำคัญของการออกแบบนี้คือการกำหนดรายการงานเธรดที่มีลักษณะเวลาและรายการอาจมีความยาวใด ๆ ระบุเวลาเริ่มต้นทุกครั้งที่คุณเพิ่มงาน
delayedtasker.java
แพ็คเกจ com.zj.timedtask; นำเข้า java.util.concurrent.timeunit.seconds; นำเข้า java.util.concurrent.timeunit.nanoseconds; นำเข้า java.util.collection; java.util.concurrent.delayed; นำเข้า java.util.concurrent.executorservice; นำเข้า java.util.concurrent.executors; นำเข้า java.util.concurrent.timeUnit; โมฆะสาธารณะ AddTask (delayedTask e) {queue.put (e); } โมฆะสาธารณะ remoVetask () {queue.poll (); } คอลเลกชันสาธารณะ <layedTask> getAllTasks () {return collections.unmodifiablecollection (คิว); } สาธารณะ int getTaskQuantity () {return queue.size (); } public void run () {ในขณะที่ (! queue.isempty ()) ลอง {queue.take (). run (); } catch (interruptedException e) {system.out.println ("ขัดจังหวะ"); } system.out.println ("เสร็จสิ้นการล่าช้า task"); } คลาสสแตติกระดับสาธารณะล่าช้าใช้งานล่าช้า, ล่าช้า, runnable {counter int คงที่ส่วนตัว = 0; ID int สุดท้ายส่วนตัว = ตัวนับ ++; Delta int สุดท้ายส่วนตัว; ทริกเกอร์ยาวส่วนตัว Public DelayedTask (int delayInseconds) {delta = delayInseconds; trigger = system.nanotime () + nanoseconds.convert (เดลต้า, วินาที); } Public Long GetDelay (หน่วย TimeUnit) {return unit.Convert (Trigger - System.nanotime (), nanoseconds); } public int compereto (ล่าช้า arg) {delayedtask ที่ = (delayedtask) arg; if (trigger <thatt.trigger) return -1; if (trigger> that.trigger) return 1; กลับ 0; } public void run () {// เรียกใช้ทั้งหมดที่คุณต้องการทำ system.out.println (นี่); } สตริงสาธารณะ toString () {return "[" + delta + "s]" + "task" + id; }} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สุ่มแรนด์ = ใหม่สุ่ม (); ExecutorService exec = executors.newcachedthreadpool (); DelayedTasker Tasker = new DelayedTasker (); สำหรับ (int i = 0; i <10; i ++) tasker.addtask (ใหม่ delayedTask (rand.nextint (5))); exec.execute (Tasker); Exec.Shutdown (); - ผลลัพธ์:
[0S] ภารกิจ 1 [0S] ภารกิจ 2 [0S] ภารกิจ 3 [1S] ภารกิจ 6 [2S] ภารกิจ 5 [3S] งาน 8 [4S] งาน 0 [4S] งาน 4 [4S] งาน 7 [4S] ภารกิจ 9
3. java.util.concurrent.ScheduledThreadPoolexecutor
คลาสนี้สามารถกำหนดให้เรียกใช้งาน (เธรด) หลังจากความล่าช้าที่กำหนดหรือทำงานเป็นประจำ (ทำซ้ำ) ในตัวสร้างคุณต้องรู้ขนาดของพูลเธรด วิธีหลักคือ:
[1] กำหนดการ
Public ScheduledFuture <?> กำหนดเวลา (คำสั่ง runnable, ล่าช้ายาว, timeunit unit)
สร้างและดำเนินการเปิดใช้งานครั้งเดียวหลังจากความล่าช้าที่กำหนด
กำหนดโดย:
- กำหนดเวลาในอินเตอร์เฟส scheduleDexecutorservice;
พารามิเตอร์:
-Command - ภารกิจที่จะดำเนินการ;
-Delay - เวลาที่จะชะลอการดำเนินการต่อจากนี้ไป;
-unit - หน่วยเวลาของพารามิเตอร์การหน่วงเวลา;
กลับ:
- ระบุ scheduledfuture ที่ระงับงานและวิธีการรับ () จะส่งคืน null หลังจากเสร็จสิ้น
[2] scheduleatfixedrate
Public ScheduledFuture <?> ScheduleatFixedrate (
คำสั่ง Runnable, Long Initialdelay, ระยะยาว, หน่วย TimeUnit)
สร้างและดำเนินการเป็นระยะที่เปิดใช้งานครั้งแรกหลังจากการล่าช้าเริ่มต้นที่กำหนดโดยการดำเนินการครั้งต่อไปมีระยะเวลาที่กำหนด นั่นคือมันจะเริ่มต้นหลังจาก initialdelay จากนั้นหลังจากระยะเวลา initialdelay + จากนั้นหลังจากระยะเวลา initialdelay + 2 * และอื่น ๆ หากการดำเนินการใด ๆ ของงานพบข้อยกเว้นการดำเนินการที่ตามมาจะถูกยกเลิก มิฉะนั้นงานจะถูกยกเลิกได้โดยการดำเนินการยกเลิกหรือการเลิกจ้างของโปรแกรมเท่านั้น หากการดำเนินการใด ๆ ของงานนี้ใช้เวลานานกว่าวัฏจักรการดำเนินการในภายหลังจะถูกเลื่อนออกไป แต่ไม่พร้อมกัน
กำหนดโดย:
- scheduleatfixedrate ในอินเตอร์เฟส scheduledexecutorservice;
พารามิเตอร์:
-Command - ภารกิจที่จะดำเนินการ;
-initialdelay - เวลาหน่วงสำหรับการดำเนินการครั้งแรก;
-Period - ระยะเวลาระหว่างการประหารชีวิตอย่างต่อเนื่อง
-unit - หน่วยเวลาของพารามิเตอร์เริ่มต้นและระยะเวลา
กลับ:
- ระบุว่า scheduledfuture ของงานที่ถูกระงับเสร็จสมบูรณ์และวิธีการรับ () จะทำการยกเว้นหลังจากถูกยกเลิก
4. การออกแบบเธรดผู้ดำเนินการที่มีลักษณะการหน่วงเวลาเวลา
class scheduleTasked เชื่อมโยง scheduledThreadPoolexCutor ซึ่งสามารถระบุขนาดของพูลเธรด รู้เธรดและเวลาหน่วงเวลาผ่านวิธีกำหนดเวลาและปิดพูลเธรดผ่านวิธีการปิดระบบ ตรรกะของงานเฉพาะ (เธรด) มีความยืดหยุ่นบางอย่าง (เมื่อเทียบกับการออกแบบก่อนหน้าการออกแบบก่อนหน้านี้จะต้องกำหนดตรรกะของเธรดล่วงหน้า แต่การออกแบบตรรกะเฉพาะของเธรดสามารถแก้ไขได้โดยการสืบทอดหรือการตกแต่ง)
scheduletasker.java
แพ็คเกจ com.zj.timedtask; นำเข้า java.util.concurrent.scheduledThreadPoolexecutor; นำเข้า java.util.concurrent.timeUnit; scheduletasker ชั้นเรียนสาธารณะ {ส่วนตัว int corepoolsize = 10; ScheduledThreadPoolexecutor Scheduler; Public ScheduleTasker () {Scheduler = ใหม่ ScheduleDTHREADPOOLEXECUTOR (COREPOOLSIZE); } Public ScheduleTasker (ปริมาณ int) {corePoolSize = ปริมาณ; scheduler = ใหม่ scheduledThreadPoolexecutor (corePoolsize); } ตารางโมฆะสาธารณะ (เหตุการณ์ที่สามารถเรียกใช้งานได้นาน) {Scheduler.schedule (เหตุการณ์, ล่าช้า, TimeUnit.Seconds); } โมฆะสาธารณะ Shutdown () {scheduler.shutdown (); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {scheduleTasker tasker = new ScheduleTasker (); tasker.schedule (ใหม่ runnable () {public void run () {system.out.println ("[1S] ภารกิจ 1");}}, 1); tasker.schedule (ใหม่ runnable () {public void run () {system.out.println ("[2S] งาน 2");}}, 2); tasker.schedule (ใหม่ runnable () {public void run () {system.out.println ("[4S] ภารกิจ 3");}}, 4); tasker.schedule (ใหม่ runnable () {public void run () {public void run () {system.out.println ("[10s] งาน 4");}}, 10); tasker.shutdown (); - ผลลัพธ์:
[1S] ภารกิจ 1 [2S] ภารกิจ 2 [4S] ภารกิจ 3 [10S] งาน 4 โมฆะสาธารณะเรียกใช้ () {ลอง {timeUnit.seconds.sleep (3); System.out.println ("ฉันกำลังจะไป"); monitor.gotMessage (); } catch (interruptedException e) {e.printStackTrace (); - การตรวจสอบคลาสใช้งานได้ {บูลีนผันผวนส่วนตัว GO = FALSE; เป็นโมฆะที่ซิงโครไนซ์สาธารณะ gotMessage () พ่น InterruptedException {go = true; แจ้ง(); } โมฆะที่ซิงโครไนซ์สาธารณะ () พ่น InterruptedException {ในขณะที่ (ไป == เท็จ) รอ (); System.out.println ("เขาไปแล้ว"); } public void run () {ลอง {ดู (); } catch (interruptedException e) {e.printStackTrace (); }}} คลาสสาธารณะรอ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {มอนิเตอร์มอนิเตอร์ = ใหม่จอภาพ (); myObject o = ใหม่ myObject (จอภาพ); เธรดใหม่ (o). start (); เธรดใหม่ (มอนิเตอร์). start (); - ผลลัพธ์:
ฉันจะไปแล้วเขาไปแล้ว