ขั้นแรก เราจะอธิบายว่าการซิงโครไนซ์คืออะไร และปัญหาของการไม่ซิงโครไนซ์คืออะไร จากนั้น เราจะหารือเกี่ยวกับมาตรการที่สามารถนำมาใช้เพื่อควบคุมการซิงโครไนซ์ได้ ต่อไป เราจะสร้าง "เธรดพูล" ฝั่งเซิร์ฟเวอร์เหมือนกับที่เราตรวจสอบเครือข่าย การสื่อสาร JDK ให้ชุดเครื่องมือ Concurrent ขนาดใหญ่แก่เรา ในที่สุดเราจะสำรวจเนื้อหาภายใน
ทำไมต้องซิงโครไนซ์เธรด?
เมื่อพูดถึงการซิงโครไนซ์เธรด ในกรณีส่วนใหญ่ เรากำลังพูดถึงสถานการณ์ " single object multi-thread " ซึ่งโดยทั่วไปจะแบ่งออกเป็นสองส่วน ส่วนแรกเกี่ยวกับ "ตัวแปรที่ใช้ร่วมกัน" และอีกส่วนเกี่ยวกับ "ขั้นตอนการดำเนินการ"
ตัวแปรที่ใช้ร่วมกัน
เมื่อเรากำหนดตัวแปรส่วนกลางในวัตถุเธรด (Runnable) และวิธีการเรียกใช้แก้ไขตัวแปร หากหลายเธรดใช้วัตถุเธรดพร้อมกัน ค่าของตัวแปรส่วนกลางจะถูกแก้ไขพร้อมกันทำให้เกิดข้อผิดพลาด . ลองดูรหัสต่อไปนี้:
การรันโมฆะสาธารณะ ()
-
System.out.println(Thread.currentThread().getName() + " เริ่ม");
สำหรับ (int i = 1; i <= 100; i++)
-
ผลรวม += ฉัน;
-
พยายาม {
เธรด.สลีป(500);
} จับ (InterruptedException e) {
e.printStackTrace();
-
System.out.println(Thread.currentThread().getName() + " --- ค่าของผลรวมคือ " + ผลรวม);
System.out.println(Thread.currentThread().getName() + " สิ้นสุด");
-
-
โมฆะคงที่ส่วนตัว sharedVaribleTest() พ่น InterruptedException
-
MyRunner runner = MyRunner ใหม่();
เธรด thread1 = เธรดใหม่ (รองชนะเลิศ);
เธรด thread2 = เธรดใหม่ (รองชนะเลิศ);
thread1.setDaemon(จริง);
thread2.setDaemon(จริง);
thread1.start();
thread2.start();
thread1.join();
thread2.เข้าร่วม();
-
เมื่อเราทำงานกับหลายเธรด เราอาจจำเป็นต้องรวมการดำเนินการบางอย่างเข้าด้วยกันเป็น "การดำเนินการแบบอะตอมมิก" กล่าวคือ การดำเนินการเหล่านี้ถือได้ว่าเป็น "เธรดเดี่ยว" ตัวอย่างเช่น เราอาจต้องการให้ผลลัพธ์เอาต์พุตมีลักษณะเช่นนี้ : :
โมฆะคงที่ส่วนตัว syncTest() พ่น InterruptedException
-
MyNonSyncRunner runner = MyNonSyncRunner ใหม่ ();
เธรด thread1 = เธรดใหม่ (รองชนะเลิศ);
เธรด thread2 = เธรดใหม่ (รองชนะเลิศ);
thread1.setDaemon(จริง);
thread2.setDaemon(จริง);
thread1.start();
thread2.start();
thread1.join();
thread2.เข้าร่วม();
-
เนื่องจากการซิงโครไนซ์เธรดมีปัญหาข้างต้น เราควรแก้ไขอย่างไร เราสามารถใช้กลยุทธ์ที่แตกต่างกันสำหรับปัญหาการซิงโครไนซ์ที่เกิดจากสาเหตุที่ต่างกัน
ควบคุมตัวแปรที่ใช้ร่วมกัน
เราสามารถควบคุมตัวแปรที่ใช้ร่วมกันได้ 3 วิธี
เปลี่ยน "วัตถุเดี่ยวหลายเธรด" เป็น "หลายวัตถุหลายเธรด"
ดังที่ได้กล่าวไว้ข้างต้น ปัญหาการซิงโครไนซ์โดยทั่วไปเกิดขึ้นในสถานการณ์ "วัตถุเดี่ยวแบบหลายเธรด" ดังนั้นวิธีที่ง่ายที่สุดในการจัดการกับปัญหานี้คือการปรับเปลี่ยนโมเดลที่ทำงานเป็น "หลายวัตถุแบบหลายเธรด" สำหรับปัญหาการซิงโครไนซ์ในตัวอย่างข้างต้น , แก้ไขโค้ดสุดท้ายเป็นดังนี้:
เนื่องจากปัญหาเกิดจากตัวแปรที่แชร์ เราสามารถเปลี่ยนตัวแปรที่แชร์เป็น "ไม่แชร์" นั่นคือแก้ไขให้เป็นตัวแปรในเครื่อง ซึ่งยังสามารถแก้ปัญหาได้ สำหรับตัวอย่างข้างต้น รหัสสำหรับโซลูชันนี้เป็นดังนี้:
โมฆะคงที่ส่วนตัว sharedVaribleTest3() พ่น InterruptedException
-
MyRunner2 runner = MyRunner2(); ใหม่
เธรด thread1 = เธรดใหม่ (รองชนะเลิศ);
เธรด thread2 = เธรดใหม่ (รองชนะเลิศ);
thread1.setDaemon(จริง);
thread2.setDaemon(จริง);
thread1.start();
thread2.start();
thread1.join();
thread2.เข้าร่วม();
-
ThreadLocal เป็นกลไกที่ JDK นำมาใช้ ใช้เพื่อแก้ตัวแปรที่ใช้ร่วมกันระหว่างเธรด ตัวแปรที่ประกาศโดยใช้ ThreadLocal เป็นตัวแปรส่วนกลางในเธรด
เราสามารถแปลงโค้ดข้างต้นได้ดังนี้:
การรันโมฆะสาธารณะ ()
-
System.out.println(Thread.currentThread().getName() + " เริ่ม");
สำหรับ (int i = 0; i <= 100; i++)
-
ถ้า (tl.get() == null)
-
tl.set(จำนวนเต็มใหม่(0));
-
int sum = ((จำนวนเต็ม)tl.get()).intValue();
ผลรวม+= ฉัน;
tl.set(จำนวนเต็มใหม่(ผลรวม));
พยายาม {
เธรด.สลีป(10);
} จับ (InterruptedException e) {
e.printStackTrace();
-
-
System.out.println(Thread.currentThread().getName() + " --- ค่าของผลรวมคือ " + ((Integer)tl.get()).intValue());
System.out.println(Thread.currentThread().getName() + " สิ้นสุด");
-
-
โมฆะคงที่ส่วนตัว sharedVaribleTest4() พ่น InterruptedException
-
MyRunner3 runner = MyRunner3(); ใหม่
เธรด thread1 = เธรดใหม่ (รองชนะเลิศ);
เธรด thread2 = เธรดใหม่ (รองชนะเลิศ);
thread1.setDaemon(จริง);
thread2.setDaemon(จริง);
thread1.start();
thread2.start();
thread1.join();
thread2.เข้าร่วม();
-
ควบคุมขั้นตอนการดำเนินการ
เมื่อพูดถึงขั้นตอนการดำเนินการ เราสามารถใช้คำสำคัญที่ซิงโครไนซ์เพื่อแก้ไขได้
โมฆะคงที่ส่วนตัว syncTest2 () พ่น InterruptedException
-
MySyncRunner runner = MySyncRunner ใหม่ ();
เธรด thread1 = เธรดใหม่ (รองชนะเลิศ);
เธรด thread2 = เธรดใหม่ (รองชนะเลิศ);
thread1.setDaemon(จริง);
thread2.setDaemon(จริง);
thread1.start();
thread2.start();
thread1.join();
thread2.เข้าร่วม();
-
เธรด thread1 = เธรดใหม่ ()
-
การรันโมฆะสาธารณะ ()
-
System.out.println(Thread.currentThread().getName() + " เริ่ม");
สุ่ม r = สุ่มใหม่ (100);
ซิงโครไนซ์ (รายการ)
-
สำหรับ (int i = 0; i < 5; i++)
-
list.add(จำนวนเต็มใหม่(r.nextInt()));
-
System.out.println("ขนาดของรายการคือ" + list.size());
-
พยายาม
-
เธรด.สลีป(500);
-
จับ (InterruptedException เช่น)
-
เช่น printStackTrace();
-
System.out.println(Thread.currentThread().getName() + " สิ้นสุด");
-
-
เธรด thread2 = เธรดใหม่ ()
-
การรันโมฆะสาธารณะ ()
-
System.out.println(Thread.currentThread().getName() + " เริ่ม");
สุ่ม r = สุ่มใหม่ (100);
ซิงโครไนซ์ (รายการ)
-
สำหรับ (int i = 0; i < 5; i++)
-
list.add(จำนวนเต็มใหม่(r.nextInt()));
-
System.out.println("ขนาดของรายการคือ" + list.size());
-
พยายาม
-
เธรด.สลีป(500);
-
จับ (InterruptedException เช่น)
-
เช่น printStackTrace();
-
System.out.println(Thread.currentThread().getName() + " สิ้นสุด");
-
-
thread1.start();
thread2.start();
thread1.join();
thread2.เข้าร่วม();
-
สร้างกลุ่มเธรด
เราได้สร้างพูลการเชื่อมต่อซ็อกเก็ตใน <Application Analysis of Network Communication Based on Java Review> ที่นี่เราสร้างเธรดพูลบนพื้นฐานนี้เพื่อดำเนินการสตาร์ทอัพ สลีป ปลุก และหยุดการทำงานขั้นพื้นฐาน
แนวคิดพื้นฐานคือการรักษาชุดของเธรดในรูปแบบของอาร์เรย์ ไคลเอ็นต์จะส่งคำสั่งไปยังเซิร์ฟเวอร์ผ่านการสื่อสารของซ็อกเก็ต เมื่อเซิร์ฟเวอร์ได้รับคำสั่ง ไคลเอ็นต์จะดำเนินการเธรดในอาร์เรย์ของเธรดตามคำสั่งที่ได้รับ
รหัสของไคลเอนต์ Socket ยังคงไม่เปลี่ยนแปลง และรหัสที่ใช้เมื่อสร้างพูลการเชื่อมต่อซ็อกเก็ตยังคงใช้อยู่ เราเน้นที่ฝั่งเซิร์ฟเวอร์เป็นหลัก
ขั้นแรก เราต้องกำหนดวัตถุเธรดซึ่งใช้ในการดำเนินธุรกิจของเรา เพื่อความง่าย เราจะปล่อยให้เธรดอยู่ในโหมดสลีปเท่านั้น
แจกแจง ThreadTask
-
เริ่ม,
หยุด,
นอน,
ตื่น
-
คลาส MyThread ขยายเธรด
-
สถานะ ThreadStatus สาธารณะ = ThreadStatus.Initial;
งาน ThreadTask สาธารณะ
การรันโมฆะสาธารณะ ()
-
สถานะ = ThreadStatus.Running;
ในขณะที่(จริง)
-
พยายาม {
เธรด.สลีป(3000);
ถ้า (สถานะ == ThreadStatus.Sleeping)
-
System.out.println(Thread.currentThread().getName() + "เข้าสู่สถานะสลีป");
นี้.รอ();
-
} จับ (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "เกิดข้อผิดพลาดระหว่างการดำเนินการ");
status = ThreadStatus.Stopped;
-
-
-
-
โมฆะสาธารณะคงที่ ManageThread (เธรด MyThread งาน ThreadTask)
-
ถ้า (งาน == ThreadTask.Start)
-
ถ้า (thread.status == ThreadStatus.Running)
-
กลับ;
-
ถ้า (thread.status == ThreadStatus.Stopped)
-
เธรด = MyThread ใหม่ ();
-
thread.status = ThreadStatus.Running;
เธรด.เริ่มต้น();
-
อย่างอื่นถ้า (งาน == ThreadTask.Stop)
-
ถ้า (thread.status != ThreadStatus.Stopped)
-
เธรด.ขัดจังหวะ();
thread.status = ThreadStatus.หยุด;
-
-
อย่างอื่นถ้า (งาน == ThreadTask.Sleep)
-
thread.status = ThreadStatus.Sleeping;
-
อย่างอื่นถ้า (งาน == ThreadTask.Wakeup)
-
thread.notify();
thread.status = ThreadStatus.Running;
-
-
สาธารณะสตริงคงที่ getThreadStatus (เธรด MyThread [])
-
StringBuffer sb = StringBuffer ใหม่ ();
สำหรับ (int i = 0; i < threads.length; i++)
-
sb.append(threads[i].getName() + "สถานะ: " + threads[i].status).append("/r/n");
-
กลับ sb.toString();
-
-
โมฆะคงที่สาธารณะ main (String [] args) พ่น IOException
-
MyThreadPool พูล = MyThreadPool ใหม่ (5);
-
เธรด int ส่วนตัว;
เธรด MyThread[] ส่วนตัว = null;
MyThreadPool สาธารณะ (จำนวน int) พ่น IOException
-
this.threadCount = นับ;
เธรด = MyThread ใหม่ [นับ];
สำหรับ (int i = 0; i < threads.length; i++)
-
เธรด [i] = MyThread ใหม่ ();
กระทู้[i].เริ่มต้น();
-
เริ่มต้น();
-
โมฆะส่วนตัว Init() พ่น IOException
-
ServerSocket serverSocket = ServerSocket ใหม่ (5678);
ในขณะที่(จริง)
-
ซ็อกเก็ตซ็อกเก็ตสุดท้าย = serverSocket.accept ();
เธรดเธรด = เธรดใหม่ ()
-
การรันโมฆะสาธารณะ ()
-
พยายาม
-
System.out.println("ตรวจพบการเชื่อมต่อซ็อกเก็ตใหม่");
BufferedReader br = BufferedReader ใหม่ (InputStreamReader ใหม่ (socket.getInputStream()));
PrintStream ps = PrintStream ใหม่ (socket.getOutputStream());
เส้นสตริง = null;
ในขณะที่ ((line = br.readLine()) != null)
-
System.out.println (บรรทัด);
ถ้า (line.equals("นับ"))
-
System.out.println("มี 5 เธรดในเธรดพูล");
-
อย่างอื่นถ้า (line.equals("Status"))
-
สถานะสตริง = MyThreadManager.getThreadStatus(threads);
System.out.println (สถานะ);
-
อย่างอื่นถ้า (line.equals("StartAll"))
-
MyThreadManager.manageThread (เธรด, ThreadTask.Start);
-
อย่างอื่นถ้า (line.equals("StopAll"))
-
MyThreadManager.manageThread (เธรด, ThreadTask.Stop);
-
อย่างอื่นถ้า (line.equals("SleepAll"))
-
MyThreadManager.manageThread (เธรด, ThreadTask.Sleep);
-
อย่างอื่นถ้า (line.equals("WakeupAll"))
-
MyThreadManager.manageThread (เธรด, ThreadTask.Wakeup);
-
อย่างอื่นถ้า (line.equals("End"))
-
หยุดพัก;
-
อื่น
-
System.out.println("คำสั่ง:" + บรรทัด);
-
PS.println("ตกลง");
PS.ฟลัช();
-
-
จับ (ข้อยกเว้นเช่น)
-
เช่น printStackTrace();
-
-
-
เธรด.เริ่มต้น();
-
-
-
เพื่อลดความซับซ้อนของภาระงานของนักพัฒนาในระหว่างการพัฒนาแบบมัลติเธรดและลดข้อบกพร่องในโปรแกรม JDK ได้จัดเตรียมชุดเครื่องมือที่ทำงานพร้อมกัน ซึ่งเราสามารถใช้เพื่อพัฒนาโปรแกรมแบบมัลติเธรดได้อย่างสะดวก
พูลเธรด
เราใช้เธรดพูลที่ "เรียบง่าย" ด้านบนนี้ เธรดพูลมีให้ในชุดเครื่องมือที่ทำงานพร้อมกันด้วย และสะดวกต่อการใช้งานมาก
เธรดพูลในชุดเครื่องมือที่เกิดขึ้นพร้อมกันแบ่งออกเป็น 3 ประเภท: ScheduledThreadPool, FixThreadPool และ CachedThreadPool
ขั้นแรกเรากำหนดวัตถุ Runnable
ThreadPool ที่กำหนดเวลาไว้
ซึ่งคล้ายกับ ScheduledTask ที่เรามักใช้ หรือเหมือนกับ Timer ซึ่งอาจทำให้เธรดเริ่มทำงานภายในระยะเวลาที่กำหนด และรันอีกครั้งหลังจากช่วงระยะเวลาอื่นจนกว่าเธรดพูลจะปิด
รหัสตัวอย่างจะเป็นดังนี้:
MyRunner runner = MyRunner ใหม่();
ScheduledFuture สุดท้าย<?> handler1 = scheduler.scheduleAtFixedRate (รองชนะเลิศ, 1, 10, TimeUnit.SECONDS);
ScheduledFuture สุดท้าย <?> handler2 = scheduler.scheduleWithFixedDelay (นักวิ่ง, 2, 10, TimeUnit.SECONDS);
scheduler.schedule (รันใหม่ ()
-
การรันโมฆะสาธารณะ ()
-
handler1.cancel (จริง);
handler2.cancel (จริง);
กำหนดการปิดเครื่อง();
-
}, 30, หน่วยเวลา วินาที
-
-
นี่คือเธรดพูลที่มีความจุที่ระบุ กล่าวคือ เราสามารถระบุได้ว่าสามารถรันเธรดได้มากที่สุดหลายเธรดพร้อมกัน เธรดส่วนเกินจะมีโอกาสรันเฉพาะเมื่อมีเธรดที่ไม่ได้ใช้งานอยู่ในนั้น พูลเธรด
พิจารณารหัสต่อไปนี้:
นี่เป็นพูลเธรดอื่นที่ไม่ต้องการความจุที่ระบุ และจะสร้างเธรดใหม่เมื่อใดก็ตามที่จำเป็น
การใช้งานคล้ายกับ FixThreadPool มาก ดูโค้ดต่อไปนี้:
ในบางกรณี เราจำเป็นต้องใช้ค่าส่งคืนของเธรด ในโค้ดข้างต้นทั้งหมด เธรดจะดำเนินการบางอย่างโดยไม่มีค่าส่งคืน
วิธีการทำเช่นนี้? เราสามารถใช้ Callable<T> และ CompletionService<T> ใน JDK อันแรกส่งคืนผลลัพธ์ของเธรดเดียว และอันหลังส่งคืนผลลัพธ์ของกลุ่มของเธรด
ส่งกลับผลลัพธ์จากเธรดเดียว
ลองดูที่รหัส:
คุณต้องใช้ CompletionService<T> ที่นี่ รหัสจะเป็นดังนี้:
เธรด.สลีป(1,000);
สำหรับ(int i = 0; i < 10; i++)
-
ผลลัพธ์ในอนาคต <String> = service.take();
System.out.println("ค่าที่ส่งคืนของเธรดคือ" + result.get());
-
ดำเนินการปิดเครื่อง();
-
เราทุกคนควรจะคุ้นเคยกับโมเดลของผู้ผลิตและผู้บริโภค และเรามักจะใช้โครงสร้างข้อมูลบางประเภทเพื่อนำไปใช้ ในชุดเครื่องมือที่เกิดขึ้นพร้อมกัน เราสามารถใช้ BlockingQueue เพื่อปรับใช้โมเดลผู้ผลิตและผู้บริโภคได้ ดังนี้:
โมฆะสาธารณะคง main (String [] args)
-
การปิดกั้น QueueTest();
-
โมฆะคงที่ส่วนตัว blockingQueueTest ()
-
สุดท้าย BlockingQueue<Integer> คิว = ใหม่ LinkedBlockingQueue<Integer>();
int สุดท้าย maxSleepTimeForSetter = 10;
int สุดท้าย maxSleepTimerForGetter = 10;
ตัวตั้งค่า Runnable = Runnable ใหม่ ()
-
การรันโมฆะสาธารณะ ()
-
สุ่ม r = สุ่มใหม่ ();
ในขณะที่(จริง)
-
ค่า int = r.nextInt(100);
พยายาม
-
Queue.put(จำนวนเต็มใหม่(ค่า));
System.out.println(Thread.currentThread().getName() + "---ใส่ค่าลงในคิว" + ค่า);
Thread.sleep(r.nextInt(maxSleepTimeForSetter) * 1,000);
-
จับ (ข้อยกเว้นเช่น)
-
เช่น printStackTrace();
-
-
-
-
Runnable getter = Runnable ใหม่ ()
-
การรันโมฆะสาธารณะ ()
-
สุ่ม r = สุ่มใหม่ ();
ในขณะที่(จริง)
-
พยายาม
-
ถ้า (queue.size() == 0)
-
System.out.println(Thread.currentThread().getName() + "---คิวว่างเปล่า");
-
อื่น
-
ค่า int = Queue.take().intValue();
System.out.println(Thread.currentThread().getName() + "---รับค่าจากคิว" + ค่า);
-
Thread.sleep(r.nextInt(maxSleepTimerForGetter) * 1,000);
-
จับ (ข้อยกเว้นเช่น)
-
เช่น printStackTrace();
-
-
-
-
ExecutorService exec = Executors.newFixedThreadPool(2);
exec.execute (ตัวตั้งค่า);
exec.execute (ทะเยอทะยาน);
-
-
ผลลัพธ์การดำเนินการที่เป็นไปได้มีดังนี้:
ใช้เซมาฟอร์เพื่อควบคุมเธรด
JDK จัดให้มีเซมาฟอร์เพื่อใช้ฟังก์ชัน "เซมาฟอร์" โดยมีสองวิธีในการรับและปล่อยเซมาฟอร์: รับและปล่อยโค้ดตัวอย่างมีดังนี้:
สำหรับ (int i = 0; i < 10; i++)
-
Runnable runner = Runnable ใหม่ ()
-
การรันโมฆะสาธารณะ ()
-
พยายาม
-
semp.ได้รับ();
System.out.println(วันที่ใหม่() + " " + Thread.currentThread().getName() + "กำลังดำเนินการ");
เธรด.สลีป(5000);
semp.ปล่อย();
-
จับ (ข้อยกเว้นเช่น)
-
เช่น printStackTrace();
-
-
-
exec.execute (รองชนะเลิศ);
-
ดำเนินการปิดเครื่อง();
-
ก่อนหน้านี้ เราได้กล่าวไว้ว่าคีย์เวิร์ดที่ซิงโครไนซ์สามารถใช้เพื่อควบคุมขั้นตอนการดำเนินการในเธรดเดียว ดังนั้นหากเราต้องการควบคุมขั้นตอนการดำเนินการของเธรดทั้งหมดในกลุ่มเธรด เราควรนำไปใช้อย่างไร
เรามีสองวิธี วิธีหนึ่งคือใช้ CyclicBarrier และอีกวิธีคือใช้ CountDownLatch
CyclicBarrier ใช้กลไกที่คล้ายกับ Object.wait ตัวสร้างของมันจำเป็นต้องได้รับหมายเลขจำนวนเต็มเพื่อระบุจำนวนเธรดที่ต้องการควบคุม เมื่อมีการเรียกเมธอด await ในเมธอด run ของเธรด จะทำให้แน่ใจได้ว่าเท่านั้น เธรดได้มาถึงขั้นตอนนี้แล้ว พวกเขาจะดำเนินการขั้นตอนต่อ ๆ ไปต่อไป
รหัสตัวอย่างจะเป็นดังนี้:
โมฆะสาธารณะวิ่ง () {
สุ่ม r = สุ่มใหม่ ();
พยายาม
-
สำหรับ (int i = 0; i < 3; i++)
-
Thread.sleep(r.nextInt(10) * 1,000);
System.out.println(วันที่ใหม่() + "--" + Thread.currentThread().getName() + "--th" + (i + 1) + "รอ");
สิ่งกีดขวาง.รอ();
-
-
จับ (ข้อยกเว้นเช่น)
-
เช่น printStackTrace();
-
-
-
โมฆะคงที่ส่วนตัว cyclicBarrierTest ()
-
สิ่งกีดขวาง CyclicBarrier = CyclicBarrier ใหม่ (3);
ExecutorService exec = Executors.newFixedThreadPool(3);
สำหรับ (int i = 0; i < 3; i++)
-
exec.execute (MyRunner2 ใหม่ (อุปสรรค));
-
ดำเนินการปิดเครื่อง();
-
CountDownLatch ใช้กลไกที่คล้ายกับ "ตัวนับถอยหลัง" เพื่อควบคุมเธรดในกลุ่มเธรด โดยมีสองวิธี: CountDown และ Await รหัสตัวอย่างจะเป็นดังนี้: