6. แคนคาเรนซีคืออะไร?
Concurrencyเป็นไลบรารีคลาสใน Java ที่มีคลาสพิเศษที่ปรับให้เหมาะสมสำหรับการทำงานกับหลายเธรดjava.util.concurrent
คลาสเหล่า นี้ถูกรวบรวมไว้ในแพ็คเกจ สามารถแบ่งตามแผนผังตามฟังก์ชันการทำงานได้ดังนี้: java.util
แพ็คเกจ แทนที่จะใช้ wrapper พื้นฐานCollections.synchronizedList
ที่บล็อกการเข้าถึงคอลเลกชันทั้งหมด จะใช้การล็อกเซ็กเมนต์ข้อมูล หรืองานได้รับการปรับให้เหมาะสมสำหรับการอ่านข้อมูลแบบขนานโดยใช้อัลกอริธึมที่ไม่ต้องรอ Queues - การไม่บล็อกและการบล็อกคิวด้วยการสนับสนุนแบบมัลติเธรด คิวที่ไม่ปิดกั้นได้รับการออกแบบเพื่อความรวดเร็วและการทำงานโดยไม่ปิดกั้นเธรด คิวการบล็อกจะใช้เมื่อคุณต้องการ "ชะลอ" เธรด "ผู้ผลิต" หรือ "ผู้บริโภค" หากไม่ตรงตามเงื่อนไขบางประการ เช่น คิวว่างเปล่าหรือล้น หรือไม่มี "ผู้บริโภค" ที่ว่าง Synchronizersเป็นยูทิลิตี้เสริมสำหรับการซิงโครไนซ์เธรด พวกมันเป็นอาวุธอันทรงพลังในการประมวลผลแบบ "คู่ขนาน" ผู้ดำเนินการ - มีเฟรมเวิร์กที่ยอดเยี่ยมสำหรับการสร้างเธรดพูล การกำหนดเวลางานอะซิงโครนัส และการได้รับผลลัพธ์ การล็อค - แสดงถึงกลไกการซิงโครไนซ์ เธรดทางเลือกและมีความยืดหยุ่นมากกว่าเมื่อเปรียบเทียบกับ synchronized
กลไกwait
พื้นฐาน Atomics - คลาสที่รองรับการดำเนินการของอะตอมในแบบดั้งเดิมและการอ้างอิง แหล่งที่มา:notify
notifyAll
7.คุณรู้จักวิชาอะไรจากกันคะเรนสีบ้าง?
คำตอบสำหรับคำถามนี้ระบุไว้อย่างสมบูรณ์ในบทความนี้ ฉันไม่เห็นประเด็นที่จะต้องพิมพ์ซ้ำทั้งหมดที่นี่ ดังนั้น ฉันจะให้คำอธิบายเฉพาะชั้นเรียนที่ฉันได้รับเกียรติให้ทำความคุ้นเคยสั้นๆ เท่านั้น ConcurrentHashMap<K, V> - ต่างจากHashtable
และบล็อกsynhronized
บนHashMap
ข้อมูลจะถูกนำเสนอในรูปแบบของเซ็กเมนต์ โดยแบ่งออกเป็นแฮชของคีย์ เป็นผลให้ข้อมูลถูกเข้าถึงโดยส่วนต่างๆ แทนที่จะเป็นวัตถุเดี่ยว นอกจากนี้ ตัววนซ้ำจะแสดงข้อมูลในช่วงระยะเวลาหนึ่งและไม่ต้องConcurrentModificationException
โยน AtomicBoolean, AtomicInteger, AtomicLong, AtomicIntegerArray, AtomicLongArray - จะเกิดอะไรขึ้นถ้าในคลาสที่คุณต้องการซิงโครไนซ์การเข้าถึงตัวแปรประเภทง่าย ๆ ตัวเดียวint
? คุณสามารถใช้โครงสร้างด้วยsynchronized
และเมื่อใช้การดำเนินการแบบอะตอมมิset/get
ก volatile
แต่คุณสามารถทำได้ดียิ่งขึ้นโดยใช้คลาสAtomic*
ใหม่ เนื่องจากการใช้ CAS การดำเนินการกับคลาสเหล่านี้จึงเร็วกว่าการซิงโครไนซ์ผ่านsynchronized/volatile
. นอกจากนี้ยังมีวิธีการบวกอะตอมมิกตามจำนวนที่กำหนด เช่นเดียวกับการเพิ่ม/ลด
8. คลาส ConcurrentHashMap ทำงานอย่างไร
เมื่อถึงเวลาเปิดตัวConcurrentHashMap
นักพัฒนา Java จำเป็นต้องมีการใช้งานแผนที่แฮชต่อไปนี้:
- ความปลอดภัยของด้าย
- ไม่มีการล็อคทั้งโต๊ะขณะเข้าถึง
- เป็นที่พึงประสงค์ว่าไม่มีการล็อกตารางเมื่อดำเนินการอ่าน
ConcurrentHashMap
มีดังนี้:
-
องค์ประกอบแผนที่
ต่างจากองค์ประกอบ
HashMap
in ถูกประกาศเป็นEntry
. นี่เป็นคุณลักษณะที่สำคัญ เนื่องจากการเปลี่ยนแปลงในJMMConcurrentHashMap
volatile
static final class HashEntry<K, V> { final K key; final int hash; volatile V value; final HashEntry<K, V> next; HashEntry(K key, int hash, HashEntry<K, V> next, V value) { this .key = key; this .hash = hash; this .next = next; this .value = value; } @SuppressWarnings("unchecked") static final <K, V> HashEntry<K, V>[] newArray(int i) { return new HashEntry[i]; } }
-
ฟังก์ชันแฮช
ConcurrentHashMap
นอกจากนี้ยังใช้ฟังก์ชันการแฮชที่ได้รับการปรับปรุงอีกด้วยฉันขอเตือนคุณว่ามันเป็นอย่างไรใน
HashMap
JDK 1.2:static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
เวอร์ชันจาก ConcurrentHashMap JDK 1.5:
private static int hash(int h) { h += (h << 15) ^ 0xffffcd7d; h ^= (h >>> 10); h += (h << 3); h ^= (h >>> 6); h += (h << 2) + (h << 14); return h ^ (h >>> 16); }
เหตุใดจึงต้องทำให้ฟังก์ชันแฮชซับซ้อนยิ่งขึ้น ตารางในแฮชแมปมีความยาวที่กำหนดด้วยกำลังสอง สำหรับรหัสแฮชที่มีการแทนค่าไบนารี่ไม่แตกต่างกันในตำแหน่งต่ำและสูง เราจะเกิดการชนกัน การเพิ่มความซับซ้อนของฟังก์ชันแฮชเพียงช่วยแก้ปัญหานี้ โดยลดโอกาสที่จะเกิดการชนกันในแผนที่
-
เซ็กเมนต์
แผนที่ถูกแบ่งออกเป็นส่วนต่างๆ N ส่วน (โดยค่าเริ่มต้น 16 ส่วน ค่าสูงสุดสามารถเป็น 16 บิตและเป็นกำลังของสอง) แต่ละส่วนเป็นตารางองค์ประกอบแผนที่แบบปลอดภัยต่อเธรด การเพิ่มจำนวนเซ็กเมนต์จะส่งเสริมการดำเนินการแก้ไขให้ขยายหลายเซ็กเมนต์ ซึ่งลดโอกาสที่จะถูกบล็อกขณะรันไทม์
-
ระดับการทำงานพร้อมกัน
พารามิเตอร์นี้ส่งผลต่อการใช้งานการ์ดหน่วยความจำและจำนวนเซ็กเมนต์ในการ์ด
จำนวนเซ็กเมนต์จะถูกเลือกเป็นกำลังที่ใกล้ที่สุดของสองที่มากกว่าระดับพร้อมกัน การลดระดับการทำงานพร้อมกันทำให้มีแนวโน้มมากขึ้นที่เธรดจะบล็อกส่วนของแผนที่เมื่อเขียน การประมาณค่าตัวบ่งชี้ที่สูงเกินไปทำให้การใช้หน่วยความจำไม่มีประสิทธิภาพ หากมีเพียงหนึ่งเธรดที่จะแก้ไขแผนที่ และส่วนที่เหลือจะอ่านได้ ขอแนะนำให้ใช้ค่า 1
-
ทั้งหมด
ดังนั้น ข้อดีหลักและคุณสมบัติการใช้งาน
ConcurrentHashMap
:hashmap
แผนที่มี ส่วนต่อประสานการโต้ตอบคล้ายกับ- การดำเนินการอ่านไม่จำเป็นต้องล็อกและดำเนินการแบบขนาน
- การดำเนินการเขียนมักจะสามารถดำเนินการแบบขนานโดยไม่มีการปิดกั้น
- เมื่อสร้าง จะต้องระบุสิ่งที่จำเป็น
concurrencyLevel
กำหนดโดยการอ่านและการเขียนสถิติ - องค์ประกอบแผนที่มีค่า
value
ประกาศเป็นvolatile
9. คลาสล็อคคืออะไร?
เพื่อควบคุมการเข้าถึงทรัพยากรที่ใช้ร่วมกัน เราสามารถใช้การล็อคเป็นทางเลือกแทนตัวดำเนินการที่ซิงโครไนซ์ได้ ฟังก์ชั่นการล็อคบรรจุอยู่ในjava.util.concurrent.locks
. ขั้นแรก เธรดพยายามเข้าถึงทรัพยากรที่ใช้ร่วมกัน หากว่างก็จะทำการล็อคบนด้าย เมื่องานเสร็จสิ้น การล็อกทรัพยากรที่ใช้ร่วมกันจะถูกปลดล็อก หากทรัพยากรไม่ว่างและมีการล็อคไว้แล้ว เธรดจะรอจนกว่าการล็อคนี้จะถูกปลดล็อค คลาสล็อคใช้อินเทอร์เฟซLock
ที่กำหนดวิธีการต่อไปนี้:
void lock():
รอจนกว่าจะได้รับล็อคboolean tryLock():
พยายามรับการล็อค หากได้รับการล็อค มันจะคืนค่าtrue หากไม่ได้รับล็อค ก็จะส่งกลับfalse ต่างจากวิธีการนี้ตรงที่lock()
จะไม่รอที่จะได้รับการล็อคหากไม่มีการล็อคvoid unlock():
ถอดล็อคออกCondition newCondition():
ส่งคืนวัตถุCondition
ที่เกี่ยวข้องกับการล็อคปัจจุบัน
lock()
และหลังจากเสร็จสิ้นการทำงานกับทรัพยากรที่ใช้ร่วมกัน วิธีการนั้นจะถูกเรียกว่าunlock()
ซึ่งจะปล่อยการล็อค วัตถุCondition
ช่วยให้คุณจัดการการบล็อกได้ ตามกฎแล้วในการทำงานกับการล็อคจะใช้คลาสReentrantLock
จากแพ็คเกจjava.util.concurrent.locks.
คลาสนี้ใช้อินเทอร์เฟLock
ซ มาดูการใช้ Java Lock API โดยใช้โปรแกรมขนาดเล็กเป็นตัวอย่าง: สมมติว่าเรามีคลาสที่มีResource
เมธอดเธรดที่ปลอดภัยสองสามวิธี และเมธอดที่ไม่จำเป็นต้องมีความปลอดภัยของเธรด
public class Resource {
public void doSomething(){
// пусть здесь происходит работа с базой данных
}
public void doLogging(){
// потокобезопасность для логгирования нам не требуется
}
}
ตอนนี้เรามาดูคลาสที่ใช้อินเทอร์เฟซRunnable
และใช้วิธีการเรียนResource
กัน
public class SynchronizedLockExample implements Runnable{
// экземпляр класса Resource для работы с методами
private Resource resource;
public SynchronizedLockExample(Resource r){
this.resource = r;
}
@Override
public void run() {
synchronized (resource) {
resource.doSomething();
}
resource.doLogging();
}
}
ตอนนี้เรามาเขียนโปรแกรมด้านบนใหม่โดยใช้ Lock API แทนsynchronized
.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// класс для работы с Lock API. Переписан с приведенной выше программы,
// но уже без использования ключевого слова synchronized
public class ConcurrencyLockExample implements Runnable{
private Resource resource;
private Lock lock;
public ConcurrencyLockExample(Resource r){
this.resource = r;
this.lock = new ReentrantLock();
}
@Override
public void run() {
try {
// лочим на 10 секунд
if(lock.tryLock(10, TimeUnit.SECONDS)){
resource.doSomething();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//убираем лок
lock.unlock();
}
// Для логгирования не требуется потокобезопасность
resource.doLogging();
}
}
ดังที่คุณเห็นจากโปรแกรม เราใช้วิธีนี้tryLock()
เพื่อให้แน่ใจว่าเธรดจะรอเพียงระยะเวลาหนึ่งเท่านั้น หากไม่ได้รับการล็อคบนอ็อบเจ็กต์ มันก็จะบันทึกและออก อีกประเด็นสำคัญ คุณต้องใช้บล็อกtry-finally
เพื่อให้แน่ใจว่าการล็อคจะถูกปล่อยแม้ว่าเมธอดdoSomething()
จะส่งข้อยกเว้น ก็ตาม แหล่งที่มา:
11. มิวเท็กซ์คืออะไร?
mutexเป็นอ็อบเจ็กต์พิเศษสำหรับการซิงโครไนซ์เธรด/กระบวนการ อาจใช้เวลาสองรัฐ - ไม่ว่างและฟรี เพื่อให้ง่ายขึ้น mutex คือตัวแปรบูลีนที่รับค่าสองค่า: ไม่ว่าง (จริง) และว่าง (เท็จ) เมื่อเธรดต้องการความเป็นเจ้าของแต่เพียงผู้เดียวของอ็อบเจ็กต์ เธรดจะทำเครื่องหมาย mutex ว่าไม่ว่าง และเมื่อเธรดทำงานกับเธรดนั้นเสร็จสิ้น จะทำเครื่องหมาย mutex ว่าว่าง mutex ถูกแนบไปกับทุกอ็อบเจ็กต์ใน Java เฉพาะเครื่อง Java เท่านั้นที่สามารถเข้าถึง mutex ได้โดยตรง มันถูกซ่อนจากโปรแกรมเมอร์12. จอภาพคืออะไร?
จอภาพเป็นกลไกพิเศษ (โค้ดชิ้นหนึ่ง) ซึ่งเป็นส่วนเสริมบน mutex ซึ่งช่วยให้มั่นใจได้ถึงการทำงานที่ถูกต้อง ท้ายที่สุดแล้ว การทำเครื่องหมายว่าอ็อบเจ็กต์ไม่ว่างนั้นไม่เพียงพอ เราต้องตรวจสอบให้แน่ใจด้วยว่าเธรดอื่นไม่พยายามใช้อ็อบเจ็กต์ไม่ว่าง ใน Java มอนิเตอร์ถูกใช้งานโดยใช้synchronized
คีย์เวิร์ด เมื่อเราเขียนบล็อกที่ซิงโครไนซ์ คอมไพลเลอร์ Java จะแทนที่ด้วยโค้ดสามชิ้น:
- ที่จุดเริ่มต้นของบล็อก
synchronized
โค้ดจะถูกเพิ่มเพื่อทำเครื่องหมาย mutex ว่าไม่ว่าง - ที่ส่วนท้ายของบล็อก
synchronized
จะมีการเพิ่มโค้ดที่ทำเครื่องหมาย mutex ว่าว่าง - ก่อนบล็อก
synchronized
จะมีการเพิ่มโค้ดเพื่อตรวจสอบว่า mutex ไม่ว่างหรือไม่ จากนั้นเธรดต้องรอให้รีลีส
GO TO FULL VERSION