Livelock ในการพัฒนาโมบาย: มันคืออะไร ความแตกต่างจาก Deadlock และหลักการทำงาน

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

Livelock (การล็อกแบบแอคทีฟ) คือสถานการณ์ในการเขียนโปรแกรมแบบมัลติเธรดที่เธรดไม่ได้ถูกบล็อกแต่ตอบสนองต่อการกระทำของกันและกันอย่างไม่รู้จบโดยไม่ทำงานที่เป็นประโยชน์ ตาม Baeldung (Java Concurrency Guide, 2024) ใน Livelock เธรดจะเปลี่ยนสถานะอย่างต่อเนื่องเพื่อตอบสนองต่อสถานะของเธรดข้างเคียง แต่ไม่มีเธรดใดบรรลุเป้าหมายของมัน แตกต่างจาก Deadlock Livelock ใช้ CPU 100% ซึ่งทำให้แบตเตอรี่ของอุปกรณ์มือถือหมดอย่างรวดเร็ว

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

  • Livelock คือสถานะที่เธรดทำงานอยู่แต่ไม่คืบหน้า ตอบสนองต่อความขัดแย้งอย่างไม่รู้จบ
  • แตกต่างจาก Deadlock ใน Livelock เธรดไม่ได้ถูกบล็อก — พวกมันสลับระหว่างสถานะอย่างต่อเนื่อง
  • การล็อกแบบแอคทีฟ ใช้เวลา CPU และพลังงาน ทำให้ประสิทธิภาพของแอปพลิเคชันลดลง
  • ขีดจำกัดการลองใหม่ (retry limit) เป็นวิธีที่ง่ายที่สุดในการป้องกัน Livelock ไม่รู้จบ
  • ความล่าช้าแบบสุ่ม (exponential backoff) ทำลายวงจรการตอบสนองแบบซิงโครนัสระหว่างเธรด

Livelock คืออะไร?

Livelock (การล็อกแบบแอคทีฟ) คือสถานการณ์ในระบบมัลติเธรดที่เธรดไม่ได้ถูกบล็อกแต่ก็ไม่ได้ทำงานที่เป็นประโยชน์เช่นกัน แต่ละเธรดตรวจจับว่ามันไม่สามารถดำเนินการต่อได้และพยายามแก้ไข แต่การกระทำของมันกระตุ้นปฏิกิริยาเดียวกันในเธรดอื่น ๆ ผลลัพธ์คือระบบสลับระหว่างสถานะอย่างไม่รู้จบโดยไม่คืบหน้า

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

ในการพัฒนาโมบาย Livelock เป็นอันตรายอย่างยิ่งเพราะมันไม่มีใครสังเกตเห็น: แอปไม่ค้าง อินเทอร์เฟซไม่ถูกบล็อก แต่ แบตเตอรี่หมด เร็วขึ้น 2-3 เท่าเนื่องจากโหลด CPU 100% จากเธรดพื้นหลัง ตามการทดสอบของ Google (Android Battery Optimization, 2023) Livelock ใน Service พื้นหลังสามารถลดอายุการใช้งานแบตเตอรี่ของอุปกรณ์ได้ถึง 40%

Livelock เกิดขึ้นได้อย่างไร

ปฏิกิริยาแบบซิงโครนัสต่อความขัดแย้ง

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

ขาดความสุ่มในการลองใหม่

เมื่อเธรดใช้ ความล่าช้าคงที่ ก่อนลองใหม่ พวกมันอาจเข้าสู่วงจรซิงโครนัส หากเธรดทั้งสองรอเวลาเท่ากัน พวกมันจะพยายามรับทรัพยากรอีกครั้งพร้อมกันและปล่อยมันอีกครั้งพร้อมกัน ปัญหาแก้ไขได้โดยใช้ exponential backoff กับองค์ประกอบสุ่ม (jitter) เช่นเดียวกับในอัลกอริทึม CSMA/CD ใน Ethernet

การออกแบบคิวที่ไม่ถูกต้อง

ในการพัฒนาโมบาย Livelock มักเกิดขึ้นเนื่องจาก การใช้งานคิวงานที่ไม่ถูกต้อง ตัวอย่างเช่น เมื่อเธรดทำงานประมวลผลข้อความเสร็จแต่เนื่องจากตรรกะการจัดลำดับความสำคัญ ส่งต่อการควบคุมให้กับเธรดทำงานอื่นที่ทำสิ่งเดียวกันอย่างต่อเนื่อง สถานการณ์เหล่านี้เป็นเรื่องปกติสำหรับ ThreadPoolExecutor ที่กำหนดเองด้วยนโยบาย RejectedExecutionHandler ที่ไม่ได้มาตรฐาน

ตัวอย่าง Livelock ในโค้ด Kotlin

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

kotlin
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.TimeUnit

class LivelockWorker(private val name: String,
                     private val lock1: ReentrantLock,
                     private val lock2: ReentrantLock) {

    fun execute() {
        while (true) {
            if (lock1.tryLock(50, TimeUnit.MILLISECONDS)) {
                if (lock2.tryLock(50, TimeUnit.MILLISECONDS)) {
                    println("$name — เสร็จสิ้น!")
                    lock2.unlock()
                    lock1.unlock()
                    return
                } else {
                    lock1.unlock()  // ปล่อยและลองใหม่
                }
            }
            Thread.sleep(50)  // ความล่าช้าเดียวกัน — ปัจจัยหลักของ Livelock
        }
    }
}

หากอินสแตนซ์สองตัวของ LivelockWorker ถูกเรียกใช้ด้วยลำดับการรับ lock1 และ lock2 ที่ต่างกัน พวกมันจะเข้าสู่การล็อกแบบแอคทีฟ แต่ละตัวจะรับทรัพยากรแรก ไม่ได้รับทรัพยากรที่สอง ปล่อยทรัพยากรแรก รอ 50 ms และลองใหม่ — อย่างไม่รู้จบ ใช้ CPU การแก้ไขคือเพิ่มองค์ประกอบสุ่มในความล่าช้า (jitter) และจำกัดจำนวนครั้งในการลองใหม่

เวอร์ชันที่แก้ไขแล้วใช้ exponential backoff พร้อม jitter แบบสุ่ม หลังจากแต่ละครั้งที่ล้มเหลว เวลารอจะเพิ่มขึ้นด้วยตัวคูณสุ่ม ซึ่งทำลายการซิงโครไนซ์ระหว่างเธรด

kotlin
fun executeWithBackoff() {
    var delay = 10L
    var attempts = 0

    while (attempts < 5) {
        if (lock1.tryLock(delay, TimeUnit.MILLISECONDS)) {
            if (lock2.tryLock(delay, TimeUnit.MILLISECONDS)) {
                println("สำเร็จ!")
                lock2.unlock(); lock1.unlock()
                return
            }
            lock1.unlock()
        }
        delay = (delay * 2 + (0..50).random())
        attempts++
    }
    println("ล้มเหลวหลังจาก 5 ครั้ง")
}

Livelock เทียบกับ Deadlock: ความแตกต่างหลัก

แม้จะมีความคล้ายคลึงภายนอก Livelock และ Deadlock มีกลไกและผลลัพธ์ที่แตกต่างกันโดยพื้นฐาน ใน Deadlock เธรดถูกบล็อกและไม่ใช้ CPU — แอปพลิเคชันเพียงแค่ค้าง ใน Livelock เธรดทำงานอยู่ ใช้ CPU 100% แต่ไม่ทำงานที่เป็นประโยชน์ การเลือกกลยุทธ์การแก้ไขขึ้นอยู่กับการระบุประเภทการล็อกที่ถูกต้อง

พารามิเตอร์DeadlockLivelock
สถานะเธรดBLOCKED / WAITINGRUNNABLE
การใช้ CPUน้อยที่สุดสูง (90-100%)
การใช้แบตเตอรี่ต่ำสูง
การตรวจจับThread DumpCPU Profiler + การวิเคราะห์ด้วยภาพ
สาเหตุทั่วไปลำดับการรับล็อกที่ต่างกันกลยุทธ์ตอบสนองต่อความขัดแย้งแบบเดียวกัน
การแก้ไขลำดับชั้นของล็อกขีดจำกัดการลองใหม่ + exponential backoff

ในการพัฒนาโมบาย ความแตกต่างในทางปฏิบัตินั้นมหาศาล Deadlock นำไปสู่ ANR และการรีสตาร์ทแอป — มันถูกตรวจจับและรายงานผ่าน Google Play Console Livelock ไม่มีใครสังเกตเห็น: แอปดูเหมือนทำงาน แต่แบตเตอรี่หมดภายในหนึ่งชั่วโมง และผู้ใช้เพียงแค่ลบแอปทิ้ง ตาม Firebase Analytics (App Retention Report, 2024) ผู้ใช้ 68% ลบแอปหากมันใช้แบตเตอรี่มากเกินไปในพื้นหลัง

วิธีตรวจจับ Livelock

การตรวจจับ Livelock ยากกว่า Deadlock เพราะระบบไม่ส่งสัญญาณชัดเจน — ไม่มีข้อยกเว้น ไม่มี ANR ไม่มีข้อความแสดงข้อผิดพลาด วิธีการวินิจฉัยหลักคือ CPU Profiler ใน Android Studio หากเธรดอยู่ในสถานะ RUNNABLE อย่างต่อเนื่องแต่ไม่ได้ดำเนินการ I/O หรือการคำนวณที่เป็นประโยชน์ — นั่นคือข้อสงสัยว่าเป็น Livelock

ตัวบ่งชี้เพิ่มเติมคือ การใช้แบตเตอรี่ผิดปกติ เมื่อแอปไม่ได้ทำงาน Android Battery Historian (เครื่องมือจาก Android SDK) สร้างกราฟการใช้พลังงานตามส่วนประกอบ หาก CPU Wakelock ถูกยึดไว้โดยไม่มีเหตุผลชัดเจน — ให้รัน Method Tracing และวิเคราะห์สแต็กการเรียกของเธรดที่น่าสงสัย

ในระดับโค้ด การบันทึกการลองใหม่ พร้อม threadId และการประทับเวลาช่วยได้ หากบันทึกแสดงการลองใหม่หลายพันครั้งต่อวินาทีโดยไม่มีความสำเร็จแม้แต่ครั้งเดียว — นั่นคือ Livelock ขอแนะนำให้นำ circuit breaker แบบ Hystrix หรือตัวนับการลองใหม่ที่มีเกณฑ์มาใช้ ซึ่งเมื่อเกินจะปิดการทำงานและแจ้งให้นักพัฒนาทราบผ่าน Crashlytics

วิธีการป้องกันการล็อกแบบแอคทีฟ

ขีดจำกัดการลองใหม่ (Retry Limit)

วิธีที่ง่ายและน่าเชื่อถือที่สุดคือ จำกัดจำนวนครั้ง ในการรับทรัพยากร หากการดำเนินการล้มเหลวหลังจาก N ครั้ง เธรดจะย้ายไปยังสถานะข้อผิดพลาดและแจ้งให้ผู้ใช้ทราบ N ถูกเลือกโดยการทดลอง: สำหรับแอปบนมือถือ โดยทั่วไปคือ 3-5 ครั้ง สิ่งนี้กำจัด Livelock ไม่รู้จบอย่างสมบูรณ์ ด้วยค่าใช้จ่ายของการแจ้งเตือนผิดพลาดที่หาได้ยากภายใต้โหลดสูง

Exponential Backoff พร้อม Jitter

แทนที่จะใช้ความล่าช้าคงที่ระหว่างการลอง จะใช้ การหยุดชั่วคราวที่เพิ่มขึ้นแบบเอกซ์โปเนนเชียล พร้อมองค์ประกอบสุ่ม สูตร: delay = min(baseDelay * 2^attempt, maxDelay) + random(0, jitter) วิธีการนี้ไม่เพียงทำลายการซิงโครไนซ์ของเธรด แต่ยังลดภาระโดยรวมของระบบภายใต้การแข่งขันสูง ใช้ในอัลกอริทึมโปรโตคอลเครือข่ายและแนะนำโดย Google สำหรับตรรกะการลองใหม่ของ Firebase Realtime Database

ลำดับความสำคัญและตรรกะไม่สมมาตร

การกำหนด กลยุทธ์ที่แตกต่าง ให้กับเธรดที่แตกต่างกันกำจัดสาเหตุหลักของ Livelock — การตอบสนองต่อความขัดแย้งที่เหมือนกัน ตัวอย่างเช่น เธรดที่มีลำดับความสำคัญสูงรับทรัพยากรโดยไม่ปล่อย ในขณะที่เธรดที่มีลำดับความสำคัญต่ำปล่อยและรอ ในการพัฒนาโมบาย เธรด UI สามารถมีลำดับความสำคัญในการรับล็อก ในขณะที่เธรดทำงานพื้นหลังใช้ TryLock พร้อมหมดเวลา

หลีกเลี่ยงการปล่อยแบบวนซ้ำ

ในสถาปัตยกรรมบางอย่าง Livelock ถูกป้องกันในระดับการออกแบบ: ปล่อยทรัพยากรในทิศทางเดียวเท่านั้น ตัวอย่างเช่น หากเธรด A ส่งต่อการควบคุมไปยังเธรด B ผ่านช่องทางคงที่เสมอ และ B ไม่เคยพยายามส่งคืนการควบคุมไปยัง A — วงจรปฏิกิริยาเป็นไปไม่ได้ สถาปัตยกรรมไปป์ไลน์ที่มีขั้นตอนการประมวลผลทางเดียวกำจัด Livelock ระหว่างขั้นตอนที่อยู่ติดกันใน Android CameraX และ MediaPipe อย่างสมบูรณ์

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

วิธีแยกแยะ Livelock จากลูปไม่รู้จบ?

ลูปไม่รู้จบ ไม่ขึ้นอยู่กับปัจจัยภายนอกและทำซ้ำการดำเนินการเดียวโดยไม่โต้ตอบกับเธรดอื่น Livelock คือปฏิกิริยาต่อการกระทำของเธรดอื่นเสมอ: เธรดเปลี่ยนพฤติกรรมเพื่อตอบสนองต่อสถานะของเธรดข้างเคียง สร้างวงจรป้อนกลับแบบปิด Thread Dump ในกรณีของ Livelock แสดงการสลับบริบทอย่างต่อเนื่อง

Livelock ในบริบทของฐานข้อมูลคืออะไร?

ในฐานข้อมูล Livelock เกิดขึ้นเมื่อ ธุรกรรมถูกเลื่อนออกไปอย่างต่อเนื่อง เนื่องจากการล็อกโดยธุรกรรมอื่น ตัวอย่างเช่น DBMS ใช้อัลกอริทึม wait-die: หากธุรกรรมที่มีเวลาเริ่มต้นน้อยกว่าขัดแย้งกับธุรกรรมใหม่กว่า มันจะย้อนกลับและเริ่มต้นใหม่ แต่ทุกครั้งก็เจอความขัดแย้งเดียวกัน แก้ไขได้ด้วยการหน่วงเวลาเริ่มต้นใหม่แบบสุ่ม

เมื่อใดที่ Livelock มีประโยชน์?

ในบางระบบ Livelock ดีกว่า Deadlock เพราะเธรดยังคงทำงานและสามารถตรวจจับปัญหาได้ ตัวอย่างเช่น ในอัลกอริทึมการล็อกแบบมองโลกในแง่ดี (optimistic locking) พฤติกรรมคล้าย livelock เป็นที่ยอมรับได้ตราบเท่าที่ขีดจำกัดการลองใหม่รับประกันความสำเร็จในที่สุด นี่คือการแลกเปลี่ยนระหว่างประสิทธิภาพและการรับประกันความคืบหน้า

Livelock ส่งผลต่อการทดสอบอย่างไร?

Livelock ยากมากที่จะทำซ้ำในการทดสอบ เพราะมันต้องการการจับคู่เวลาที่แม่นยำของเธรด การทดสอบหน่วยทำงานแบบกำหนดขึ้นและแทบไม่พบการล็อกแบบแอคทีฟ แนะนำให้ใช้การทดสอบความเครียดด้วยการเรียกใช้ซ้ำหลายครั้งภายใต้โหลดและการตรวจสอบการใช้ CPU ในโปรไฟเลอร์

Livelock บน Android แตกต่างจาก Livelock บนเซิร์ฟเวอร์อย่างไร?

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

สรุป

  • Livelock คือสถานะการล็อกแบบแอคทีฟที่เธรดไม่ได้ถูกบล็อกแต่ตอบสนองต่อความขัดแย้งอย่างไม่รู้จบโดยไม่คืบหน้า
  • แตกต่างจาก Deadlock ใน Livelock เธรดใช้ CPU 100% ซึ่งเป็นสิ่งสำคัญสำหรับอุปกรณ์มือถือ
  • สาเหตุหลัก คือกลยุทธ์การตอบสนองต่อความขัดแย้งแบบเดียวกันและการขาดความสุ่มในความล่าช้า
  • Exponential backoff พร้อม jitter ทำลายวงจรซิงโครนัสและป้องกันการล็อกแบบแอคทีฟ
  • ขีดจำกัดการลองใหม่ (3-5 ครั้ง) กำจัด Livelock ไม่รู้จบอย่างสมบูรณ์
  • CPU Profiler ใน Android Studio และ Battery Historian เป็นเครื่องมือวินิจฉัยหลักสำหรับ Livelock
  • ตรรกะการรับล็อกแบบไม่สมมาตร สำหรับเธรดที่แตกต่างกันกำจัดความเป็นไปได้ของการล็อกแบบแอคทีฟ

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

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

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

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