Synchronized: คืออะไร หลักการทำงานและการใช้งานใน Java

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-03-19 เวลาอ่าน: 8 นาที

Synchronized เป็นกลไกการซิงโครไนซ์ในตัวของภาษา Java ที่ให้การเข้าถึงส่วนสำคัญของโค้ดแบบเอกสิทธิ์เฉพาะบุคคล ตามข้อมูลของ Oracle, 2024 ตัวปรับแต่ง synchronized รับประกันว่าเพียงเธรดเดียวเท่านั้นที่สามารถดำเนินการเมธอดหรือบล็อกที่ทำเครื่องหมายไว้ใน ช่วงเวลาใดเวลาหนึ่ง กลไกนี้มีพื้นฐานมาจากมอนิเตอร์ (monitor) ซึ่งเป็นแนวคิดพื้นฐานของระบบปฏิบัติการที่ช่วยให้การทำงานที่ถูกต้องของแอปพลิเคชันแบบหลายเธรดทุกระดับความซับซ้อน

ประเด็นสำคัญ

  • Synchronized — คำสำคัญของ Java สำหรับการเข้าถึงข้อมูลอย่างปลอดภัยระหว่างเธรด
  • มอนิเตอร์ออบเจกต์ — กลไกภายในที่เป็นพื้นฐานของการซิงโครไนซ์
  • เมธอด synchronized ล็อกทั้งเมธอดในระดับอินสแตนซ์หรือคลาส
  • บล็อก synchronized อนุญาตให้ซิงโครไนซ์เพียงบางส่วนของโค้ด
  • Deadlock — หนึ่งในปัญหาหลักของการซิงโครไนซ์แบบซ้อน

Synchronized คืออะไร?

Synchronized เป็นคำสำคัญใน Java ที่รับประกันว่าเพียงเธรดเดียวเท่านั้นที่ดำเนินการส่วนของโค้ดที่ได้รับการป้องกันในเวลาใดเวลาหนึ่ง ป้องกันการเสียหายของข้อมูลระหว่างการเข้าถึงพร้อมกัน ปรากฏใน Java เวอร์ชันแรกและยังคงเป็นวิธีที่ง่ายที่สุดในการรับประกันความปลอดภัยของเธรดสำหรับนักพัฒนาทุกระดับ

คำจำกัดความและบทบาทใน Java

ตัวปรับแต่ง synchronized แก้ปัญหาสองอย่าง: การแยกส่วนร่วมกัน (mutual exclusion) และ การมองเห็นการเปลี่ยนแปลง (visibility) เมื่อเธรดออกจากบล็อก synchronized การเปลี่ยนแปลงทั้งหมดจะมองเห็นได้กับเธรดอื่นที่เข้าสู่บล็อกที่ซิงโครไนซ์บนออบเจกต์เดียวกัน

Synchronized สามารถนำไปใช้กับเมธอดทั้งหมดหรือกับ บล็อกโค้ด ใดก็ได้โดยระบุออบเจกต์มอนิเตอร์ ในทั้งสองกรณี JVM จะแทรกคำสั่ง monitorenter และ monitorexit ในระดับไบต์โค้ด

ที่มาของแนวคิด

ในแอปพลิเคชันแบบหลายเธรดที่ไม่มีการซิงโครไนซ์ จะเกิด สภาวะการแข่งขัน (race condition) เมื่อสองเธรดแก้ไขข้อมูลเดียวกันพร้อมกัน ทำให้เกิดผลลัพธ์ที่คาดเดาไม่ได้ Synchronized กลายเป็นเครื่องมือแรกและหลักของ Java ในการแก้ปัญหานี้ โดยให้รูปแบบคำสั่งแบบประกาศที่เรียบง่ายซึ่งนักพัฒนาทุกคนสามารถเข้าถึงได้

Synchronized ทำงานอย่างไร?

กลไก synchronized มีพื้นฐานมาจากแนวคิดของมอนิเตอร์ (monitor) ซึ่งเป็นสิ่งดั้งเดิมสำหรับการซิงโครไนซ์ระดับสูงที่สร้างอยู่ในทุกออบเจกต์ Java มอนิเตอร์จะเชื่อมโยงกับออบเจกต์เมื่อมีการใช้บล็อก synchronized ครั้งแรกบนออบเจกต์นั้น

มอนิเตอร์ออบเจกต์

ทุกออบเจกต์ใน Java มี มอนิเตอร์ ที่เกี่ยวข้อง เมื่อเธรดเข้าสู่บล็อก synchronized เธรดจะได้รับการครอบครองมอนิเตอร์ของออบเจกต์ หากมอนิเตอร์ถูกครอบครองโดยเธรดอื่นอยู่แล้ว เธรดจะถูกบล็อกจนกว่าจะปล่อย ในไบต์โค้ด สิ่งนี้สอดคล้องกับคำสั่งคู่ monitorenter และ monitorexit

สถานะการล็อก (biased locking)

JVM ปรับปรุงประสิทธิภาพของ synchronized ผ่านหลายระดับ: biased locking (การล็อกแบบเอนเอียง) สำหรับการเข้าถึงเธรดเดียว lightweight locking (การล็อกแบบเบา) สำหรับการแข่งขันต่ำ และ heavyweight locking (การล็อกแบบหนัก) สำหรับการแข่งขันสูงที่เกี่ยวข้องกับระบบปฏิบัติการ ระดับเหล่านี้ช่วยเพิ่มประสิทธิภาพโดยไม่ต้องเปลี่ยนโค้ด

java
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

กฎ happens-before

Synchronized สร้างความสัมพันธ์แบบ happens-before: การกระทำทั้งหมดในเธรดก่อนออกจากบล็อก synchronized จะมองเห็นได้กับเธรดอื่นหลังจากเข้าสู่บล็อกที่ซิงโครไนซ์บนออบเจกต์เดียวกัน สิ่งนี้รับประกันไม่เพียงแต่การแยกส่วนร่วมกัน แต่ยังรวมถึงความสอดคล้องของข้อมูลสำหรับทุกเธรด

เมธอด synchronized vs บล็อก

Java มีสองวิธีในการใช้ synchronized: ในระดับเมธอดและในระดับบล็อก การเลือก ระหว่างทั้งสองส่งผลต่อประสิทธิภาพและความละเอียดของการซิงโครไนซ์

เมธอด synchronized

การทำเครื่องหมายเมธอดด้วยตัวปรับแต่ง synchronized จะซิงโครไนซ์มันบน อินสแตนซ์ปัจจุบัน โดยอัตโนมัติ (สำหรับเมธอดอินสแตนซ์) หรือบนออบเจกต์ Class (สำหรับเมธอดแบบสแตติก) นี่เป็นวิธีที่ง่ายที่สุดในการรับประกันการแยกส่วนร่วมกัน แต่มักจะมากเกินไปหากส่วนสำคัญเป็นเพียงส่วนเล็กของเมธอดและโค้ดที่เหลือไม่ต้องการการซิงโครไนซ์

บล็อก synchronized

บล็อก synchronized ให้การควบคุมที่แม่นยำ: คุณระบุ ออบเจกต์มอนิเตอร์ และซิงโครไนซ์เฉพาะส่วนของโค้ดที่จำเป็น โดยปล่อยให้เมธอดที่เหลืออยู่นอกการล็อก ซึ่งช่วยลดระยะเวลาการถือครองมอนิเตอร์และปรับปรุงประสิทธิภาพโดยรวมของแอปพลิเคชันในสภาพแวดล้อมแบบหลายเธรด เนื่องจากเธรดอื่นสามารถดำเนินการโค้ดที่ไม่เกี่ยวข้องแบบขนานได้โดยไม่ต้องรอการปล่อยมอนิเตอร์

java
class DataProcessor {
    private final Object lock = new Object();

    public void process() {
        // โค้ดนอกส่วนสำคัญ - ไม่มีการซิงโครไนซ์
        prepareData()

        synchronized (lock) {
            // เฉพาะบล็อกนี้ที่ได้รับการป้องกัน
            updateSharedState()
        }

        // ดำเนินการต่อโดยไม่ล็อก
        cleanup()
    }
}
เกณฑ์เมธอด synchronizedบล็อก synchronized
มอนิเตอร์this (อินสแตนซ์) หรือ Classออบเจกต์ใดก็ได้
ความละเอียดทั้งเมธอดเฉพาะโค้ดที่จำเป็น
ความสามารถในการอ่านสูงปานกลาง
ประสิทธิภาพต่ำกว่าสำหรับเมธอดขนาดใหญ่สูงกว่าสำหรับส่วนสำคัญขนาดเล็ก

Synchronized ใน Android

ในการพัฒนา Android synchronized ถูกใช้อย่างแพร่หลายเพื่อปกป้อง SharedPreferences การเข้าถึงฐานข้อมูล และส่วนประกอบ UI อย่างไรก็ตาม การใช้งานบนเธรดหลักไม่แนะนำอย่างยิ่งเนื่องจากความเสี่ยงที่อินเทอร์เฟซจะค้าง

การใช้งานกับ SharedPreferences

SharedPreferences ใน Android ให้ ความปลอดภัยพื้นฐานของเธรด แต่เมื่อแก้ไขจากหลายเธรดผ่าน Editor อาจจำเป็นต้องมีการซิงโครไนซ์ภายนอก บล็อก synchronized ที่มีออบเจกต์ล็อกแยกต่างหากรับประกันความสอดคล้องของการเปลี่ยนแปลง

kotlin
class PreferencesManager(private val prefs: SharedPreferences) {
    private val lock = Any()

    fun writeToken(token: String) {
        synchronized (lock) {
            prefs.edit()
                .putString("auth_token", token)
                .apply()
        }
    }
}

ข้อจำกัดในแอปพลิเคชัน Android

ข้อจำกัดหลักของ synchronized บน Android คือ การบล็อกเธรด ซึ่งแตกต่างจาก coroutine ที่มี Mutex synchronized จะบล็อกเธรดระบบทั้งหมด บนเธรดหลัก สิ่งนี้ทำให้เกิด ANR ในการพัฒนา Android สมัยใหม่ แนะนำให้แทนที่ synchronized ด้วย coroutine (suspend Mutex) หรือประเภทอะตอมมิก (AtomicInteger)

ทางเลือกของ Synchronized

Java และ Kotlin สมัยใหม่มีทางเลือกหลายอย่างสำหรับ synchronized ซึ่งแต่ละทางแก้ปัญหาเดียวกันด้วยข้อจำกัดน้อยกว่าหรือประสิทธิภาพที่ดีกว่า

Lock จาก java.util.concurrent

อินเทอร์เฟซ Lock ที่มีการใช้งาน ReentrantLock และ ReadWriteLock ให้การหมดเวลา การรอที่สามารถขัดจังหวะได้ และคิว Condition หลายรายการ มันยืดหยุ่นกว่า synchronized แต่ต้องปล่อยอย่างชัดเจนใน finally ซึ่งเพิ่มความเสี่ยงของข้อผิดพลาดเมื่อลืม unlock

คลาสอะตอมมิก

AtomicInteger, AtomicLong, AtomicReference และคลาสอื่นใช้ขั้นตอนวิธีแบบ Lock-Free ที่อิงตาม CAS (Compare-And-Swap) พวกมันเร็วกว่า synchronized อย่างมากในสถานการณ์ที่มีการแข่งขันปานกลางเนื่องจากไม่บล็อกเธรด แต่ทำการลองใหม่แบบมองโลกในแง่ดีโดยไม่ต้องสลับบริบทของเคอร์เนลระบบปฏิบัติการ

ThreadLocal และความปลอดภัยของเธรด

ThreadLocal ให้แนวทางอื่น: ตัวแปร ThreadLocal แต่ละตัวถูกแยกภายใน เธรดเดียว และไม่ต้องการการซิงโครไนซ์สำหรับการอ่านและเขียน ซึ่งช่วยขจัดความจำเป็นในการใช้ synchronized สำหรับข้อมูลที่ไม่ควรแชร์ระหว่างเธรดโดยสมบูรณ์ ThreadLocal ถูกใช้อย่างแข็งขันในเฟรมเวิร์ก (Spring, Hibernate) สำหรับจัดเก็บบริบทของธุรกรรมและเซสชัน

Coroutine และแนวทาง compose

ในโปรเจกต์ Kotlin สำหรับ Android ทางเลือกของ synchronized คือ Mutex จาก kotlinx.coroutines มันไม่บล็อกเธรดของระบบปฏิบัติการ แต่ระงับ coroutine จนกว่าล็อกจะถูกปล่อย ซึ่งช่วยให้ใช้เธรดในพูลได้อย่างมีประสิทธิภาพและหลีกเลี่ยง ANR ระหว่างรอการปล่อยทรัพยากรเป็นเวลานาน

kotlin
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

val mutex = Mutex()
var counter = 0

suspend fun safeIncrement() {
    mutex.withLock {
        counter++
    }
}

ประสิทธิภาพของ Synchronized

ประสิทธิภาพของ synchronized เปลี่ยนแปลงอย่างมีนัยสำคัญในเวอร์ชัน Java ล่าสุด ก่อนหน้านี้ถือว่าเป็นกลไก “หนัก” แต่ JVM สมัยใหม่ได้ขจัดค่าใช้จ่ายส่วนใหญ่ด้วยการเพิ่มประสิทธิภาพคอมไพเลอร์ JIT ขั้นสูง มาดูรายละเอียดว่าเครื่องเสมือนเร่งความเร็วโค้ดที่ซิงโครไนซ์ในรันไทม์ได้อย่างไร

Biased Locking และ Lock Coarsening

คอมไพเลอร์ JIT ของ JVM ใช้การเพิ่มประสิทธิภาพหลายอย่าง: biased locking จะกำจัดการซิงโครไนซ์หากล็อกถูกครอบครองโดยเธรดเดียวกันเสมอ; lock coarsening จะรวมบล็อก synchronized ที่อยู่ติดกันเป็นหนึ่งเดียว; lock elimination จะลบการซิงโครไนซ์หากออบเจกต์สามารถเข้าถึงได้โดยเธรดเดียวเท่านั้น การเพิ่มประสิทธิภาพเหล่านี้ทำให้ synchronized แทบไม่มีค่าใช้จ่ายเมื่อมีการแข่งขันต่ำ

การวัดการแข่งขันและการเลือกการเพิ่มประสิทธิภาพ

JVM กำหนดระดับการแข่งขันสำหรับแต่ละออบเจกต์: เมื่อไม่มี การแข่งขัน biased locking จะถูกเปิดใช้งาน; เมื่อเธรดที่สองปรากฏขึ้น ล็อกจะเปลี่ยนเป็นโหมดเบาพร้อมการรอแบบหมุน (spin); และเฉพาะในระหว่างการรอนานเท่านั้นที่จะขยายไปเป็นโหมดหนักด้วยมูเท็กซ์ของระบบ การขยายนี้เกิดขึ้นโดยอัตโนมัติและนักพัฒนาไม่จำเป็นต้องเลือกกลยุทธ์ด้วยตนเอง

การเปรียบเทียบกับ Lock และคลาสอะตอมมิก

ในการวัดประสิทธิภาพสมัยใหม่ (Java 17+) synchronized แสดงประสิทธิภาพที่เทียบเท่ากับ ReentrantLock ในการแข่งขันต่ำและปานกลาง ในการแข่งขันสูง Lock อาจได้เปรียบด้วยคิวรอที่มีประสิทธิภาพมากกว่าพร้อมการรองรับหมดเวลาและการขัดจังหวะ สำหรับระบบที่มีโหลดสูงซึ่งการแข่งขันคงที่ ReentrantLock ในโหมด fair ให้พฤติกรรมที่คาดเดาได้มากกว่า

คลาสอะตอมมิก (AtomicInteger, AtomicReference) ยังคงเร็วที่สุดสำหรับตัวนับและแฟลกง่ายๆ ด้วย การใช้งาน Lock-Free ที่อิงตาม CAS พวกมันไม่บล็อกเธรดเลย — เมื่อเกิดความขัดแย้ง การดำเนินการก็แค่ลองใหม่ในลูป สิ่งนี้ให้ประสิทธิภาพที่เพิ่มขึ้น 3-5 เท่าเมื่อเทียบกับ synchronized ในการดำเนินการเพิ่มตัวนับด้วย 4-8 เธรด

คำถามที่พบบ่อย

ความแตกต่างระหว่าง synchronized และ volatile คืออะไร?

Synchronized ให้ทั้งการแยกส่วนร่วมกันและการมองเห็น Volatile รับประกันเฉพาะการมองเห็นการเปลี่ยนแปลง — การเขียนไปยังตัวแปร volatile จะมองเห็นได้กับทุกเธรด แต่ไม่ได้ป้องกันการแก้ไขพร้อมกัน กล่าวคือ ไม่ป้องกันสภาวะการแข่งขัน

Synchronized สามารถทำให้เกิด deadlock ได้หรือไม่?

ได้ deadlock เป็นไปได้กับการซิงโครไนซ์แบบซ้อนโดยใช้ลำดับมอนิเตอร์ต่างกัน ตัวอย่างเช่น เธรดหนึ่งเรียก synchronized(a) { synchronized(b) } ขณะที่อีกเธรดเรียก synchronized(b) { synchronized(a) } หลีกเลี่ยงบล็อก synchronized แบบซ้อนหรือกำหนดลำดับมอนิเตอร์ที่สอดคล้องกัน

มอนิเตอร์ใน Java คืออะไร?

มอนิเตอร์ เป็นกลไกการซิงโครไนซ์ที่เกี่ยวข้องกับทุกออบเจกต์ Java มันรับประกันว่าเพียงเธรดเดียวเท่านั้นที่ดำเนินการโค้ด synchronized บนออบเจกต์นั้น มอนิเตอร์ประกอบด้วยล็อก คิวรอ และกลุ่มของเธรดที่รอการแจ้งเตือนผ่าน wait/notify

Lock เร็วกว่า synchronized หรือไม่?

ในเวอร์ชัน Java สมัยใหม่ (17+) synchronized ไม่ด้อยกว่า Lock ในด้านประสิทธิภาพด้วยการเพิ่มประสิทธิภาพ JIT (biased locking, lock coarsening) Lock ถูกเลือกไม่ใช่เพราะความเร็ว แต่เพราะความสามารถเพิ่มเติม: การหมดเวลา การรอที่สามารถขัดจังหวะได้ และคิว Condition หลายรายการ

Synchronized ทำงานกับเมธอดสแตติกอย่างไร?

เมธอดสแตติก synchronized ใช้มอนิเตอร์ของออบเจกต์ Class ของคลาสที่กำหนด ไม่ใช่อินสแตนซ์ ซึ่งหมายความว่าการซิงโครไนซ์มีผลกับทุกอินสแตนซ์ของคลาส เมธอดที่ไม่ใช่สแตติกและสแตติก synchronized ใช้มอนิเตอร์ต่างกันและไม่บล็อกซึ่งกันและกัน

สรุป

  • Synchronized — กลไกการซิงโครไนซ์ในตัวของ Java ที่อิงตามมอนิเตอร์
  • มอนิเตอร์ออบเจกต์ — โครงสร้างภายในของ JVM ที่รับประกันการแยกส่วนร่วมกัน
  • เมธอด wait/notify ใช้ภายในบล็อกหรือเมธอด synchronized เท่านั้น
  • บล็อก synchronized เหนือกว่าเมธอดเนื่องจากความละเอียดการซิงโครไนซ์ที่ดีกว่า
  • Happens-before รับประกันการมองเห็นการเปลี่ยนแปลงระหว่างเธรดเมื่อซิงโครไนซ์บนออบเจกต์เดียวกัน
  • Deadlock — ความเสี่ยงหลักของการซิงโครไนซ์แบบซ้อนด้วยลำดับมอนิเตอร์ต่างกัน
  • ทางเลือก — Lock, คลาสอะตอมมิก และ coroutine กับ suspending Mutex

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม