PlusMagi's Blog By Pitt Phunsanit Backend,Concurrency Java Concurrency: การจัดการ Thread Synchronization ด้วย synchronized keyword และ ReentrantLock

Java Concurrency: การจัดการ Thread Synchronization ด้วย synchronized keyword และ ReentrantLock

ในโลกของการพัฒนาซอฟต์แวร์ระดับองค์กร (Enterprise Applications) ระบบส่วนใหญ่มักถูกออกแบบให้ทำงานแบบขนาน (Concurrent) เพื่อเพิ่มประสิทธิภาพและความเร็วในการประมวลผล การที่หลายเธรด (Thread) เข้าถึงและแก้ไขข้อมูลชุดเดียวกันพร้อมกันจึงเป็นสถานการณ์ที่ทรงพลัง แต่ก็เต็มไปด้วยความเสี่ยงร้ายแรงที่จะเกิดภาวะข้อมูลเสียหาย (Data Corruption) หรือ Race Condition หากไม่มีกลไกควบคุมการเข้าถึงทรัพยากรร่วมกันอย่างเหมาะสม

หัวใจของการจัดการ Concurrency: Mutual Exclusion

หลักการสำคัญในการแก้ไขปัญหานี้คือ Mutual Exclusion หรือการกีดกันซึ่งกันและกัน ซึ่งหมายความว่า ณ เวลาใดเวลาหนึ่ง จะต้องมีเธรดเพียงตัวเดียวเท่านั้นที่สามารถเข้าถึงส่วนของโค้ดที่จัดการกับข้อมูลที่แชร์อยู่ (Shared Resource) ได้ ส่วนนี้ของโค้ดเรียกว่า Critical Section

เครื่องมือหลัก: synchronized Keyword และ ReentrantLock

ใน Java เรามีกลไกในการบังคับใช้ Mutual Exclusion หลักๆ สองตัว คือคีย์เวิร์ด synchronized และคลาส ReentrantLock ซึ่งทั้งคู่ทำหน้าที่เป็น Lock เพื่อควบคุมการเข้าถึงทรัพยากร แต่มีความแตกต่างในด้านความยืดหยุ่นและการควบคุมที่สำคัญ

1. การใช้ synchronized Keyword (Monitor Lock)

synchronized เป็นวิธีที่ง่ายที่สุดและเป็นธรรมชาติที่สุดในการสร้าง Monitor Lock ใน Java มันจะทำงานโดยการผูก Object ตัวใดตัวหนึ่งเข้ากับเธรด ทำให้มั่นใจได้ว่าโค้ดบล็อกนั้นๆ จะถูกเข้าถึงโดยเธรดเดียวในเวลาใดเวลาหนึ่ง การใช้งานนี้เหมาะสำหรับกรณีที่ต้องการความเรียบง่ายและไม่ซับซ้อน

2. การใช้ ReentrantLock (Explicit Lock)

ReentrantLock จากแพ็กเกจ java.util.concurrent.locks มอบความยืดหยุ่นและประสิทธิภาพที่เหนือกว่าอย่างเห็นได้ชัด เนื่องจากมันให้การควบคุมแบบ Explicit มากกว่า เช่น เราสามารถกำหนด Timeout ในการรอ Lock, หรือสามารถปลดล็อก (Unlock) ได้ด้วยตนเองในบล็อก finally ซึ่งเป็นสิ่งสำคัญในการเขียนโค้ดระดับสถาปัตยกรรม

Java

import java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\npublic class ResourceCounter {\n    private int count = 0;\n    // ใช้ ReentrantLock เพื่อการควบคุมที่ละเอียดกว่า\n    private final Lock lock = new ReentrantLock();\n\n    public void increment() {\n        lock.lock(); // เข้าสู่ Critical Section: ต้องเรียกใช้ด้วยตนเอง\n        try {\n            count++;\n            System.out.println("Count: " + count);\n        } finally {\n            // สำคัญมาก: ต้องมั่นใจว่ามีการปลดล็อกเสมอ แม้เกิด Exception\n            lock.unlock(); \n        }\n    }\n}

สรุปการเลือกใช้: เมื่อไหร่ควรใช้ตัวไหน?

  • เลือก synchronized เมื่อ: โค้ดของคุณมีความเรียบง่าย ไม่ซับซ้อน และคุณต้องการความรวดเร็วในการพัฒนา (Readability) เพราะมันจัดการการ Lock/Unlock ให้โดยอัตโนมัติ
  • เลือก ReentrantLock เมื่อ: คุณต้องการควบคุมกระบวนการ Lock อย่างละเอียด เช่น ต้องการกำหนด Timeout, ต้องใช้ Condition Variables สำหรับการรอคอยแบบมีเงื่อนไขที่ซับซ้อน หรือเมื่อคุณต้องมั่นใจว่าการปลดล็อกจะเกิดขึ้นอย่างแน่นอนในบล็อก finally

การทำความเข้าใจกลไก Synchronization ไม่ใช่แค่เรื่องของ Syntax แต่คือการคิดเชิงสถาปัตยกรรม (Architectural Thinking) ที่ต้องคำนึงถึงขอบเขตของการเข้าถึงข้อมูลอย่างรอบคอบ การเลือกใช้เครื่องมือที่เหมาะสมจะช่วยให้ระบบของคุณมีความเสถียร ปลอดภัย และสามารถรองรับปริมาณงานพร้อมกันในระดับสูงได้อย่างแท้จริง

Exit mobile version