Semaphore: apa itu, prinsip kerja dan penerapan dalam sinkronisasi thread

Penulis: IT Sectr Diterbitkan: 2026-03-19 Waktu membaca: 8 mnt

Semaphore adalah primitif sinkronisasi yang mengelola akses ke sumber daya bersama melalui penghitung dan antrian thread yang menunggu. Menurut Wikipedia, 2024, semaphore diusulkan oleh Edsger Dijkstra pada tahun 1965 untuk menyelesaikan masalah interaksi multithread. Alat ini memungkinkan membatasi jumlah thread yang bekerja secara bersamaan dengan seksi kritis.

Poin Utama

  • Semaphore — primitif sinkronisasi yang mengelola akses melalui penghitung izin.
  • Semaphore biner mengambil nilai 0 dan 1, berfungsi seperti bendera penguncian.
  • Semaphore penghitung memungkinkan akses simultan sejumlah thread yang ditentukan.
  • Berbeda dengan mutex, semaphore tidak terikat pada thread pemilik.
  • Deadlock — salah satu bahaya utama dalam penggunaan semaphore yang tidak tepat.

Apa itu Semaphore?

Semaphore — adalah primitif sinkronisasi yang menggunakan penghitung untuk mengelola akses ke sumber daya bersama. Konsep ini diusulkan oleh Edsger Dijkstra pada tahun 1965 dan menjadi fondasi semua mekanisme sinkronisasi modern dalam sistem operasi.

Definisi dan Tujuan

Semaphore adalah variabel bilangan bulat dengan dua operasi atomik: wait (acquire) dan signal (release). Operasi wait mengurangi penghitung, dan signal meningkatkannya. Ketika penghitung mencapai nol, thread yang memanggil wait akan diblokir sampai signal dijalankan oleh thread lain.

Tujuan utama semaphore adalah melindungi seksi kritis dari akses simultan oleh beberapa thread. Berbeda dengan mutex, semaphore tidak memerlukan keterikatan pada thread pemilik, yang membuatnya cocok untuk berbagai tugas koordinasi yang lebih luas.

Sejarah dan Dasar Teoretis

Konsep semaphore muncul dalam konteks sistem operasi THE, yang dikembangkan di Technische Hogeschool Eindhoven. Dijkstra memformalkan semaphore sebagai abstraksi matematika, membuktikan kecukupannya untuk mengimplementasikan primitif sinkronisasi apa pun.

Bagaimana cara kerja semaphore?

Mekanisme semaphore didasarkan pada dua operasi atomik dan antrian tunggu internal. Saat memanggil acquire, thread memeriksa nilai penghitung dan melanjutkan eksekusi atau diblokir sampai sumber daya dibebaskan.

Penghitung dan Operasi Atomik

Saat membuat semaphore, nilai awal penghitung izin ditetapkan. Setiap panggilan acquire mengurangi penghitung sebesar 1. Jika setelah itu penghitung menjadi negatif, thread akan diblokir. Operasi release meningkatkan penghitung dan membangunkan salah satu thread yang menunggu.

kotlin
import java.util.concurrent.Semaphore

val semaphore = Semaphore(3)

fun accessResource() {
    semaphore.acquire()
    try {
        println("${Thread.currentThread().name} bekerja")
    } finally {
        semaphore.release()
    }
}

Antrian Tunggu dan Penjadwalan

Ketika thread memanggil acquire dengan penghitung nol, OS menempatkannya dalam antrian FIFO semaphore. Thread beralih ke status BLOCKED, tidak menghabiskan waktu prosesor. Setelah panggilan release, thread pertama dalam antrian beralih ke status RUNNABLE dan mendapatkan akses ke sumber daya.

Jenis-jenis semaphore

Dalam teori sinkronisasi, dua jenis utama semaphore dibedakan: biner (binary) dan penghitung (counting). Pemilihan jenis tergantung pada tugas spesifik manajemen akses ke sumber daya.

Semaphore Biner (Binary Semaphore)

Semaphore biner hanya mengambil nilai 0 dan 1. Perilakunya menyerupai mutex, tetapi tanpa persyaratan kepemilikan — thread mana pun dapat mengeksekusi release. Semaphore semacam itu berguna untuk mengimplementasikan bendera kesiapan dan peristiwa antar thread.

kotlin
val ready = Semaphore(0)

fun producer() {
    Thread.sleep(1000)
    ready.release()
}

fun consumer() {
    ready.acquire()
    println("Data siap")
}

Semaphore Penghitung (Counting Semaphore)

Semaphore penghitung dapat mengambil nilai non-negatif apa pun. Digunakan untuk mengelola kumpulan sumber daya homogen di mana beberapa instance tersedia. Misalnya, kumpulan 5 koneksi jaringan: setiap acquire mengambil satu koneksi, release mengembalikannya ke kumpulan.

Semaphore penghitung sangat diperlukan untuk membatasi kecepatan akses ke layanan eksternal dan mengimplementasikan kumpulan thread. Mereka memungkinkan kontrol yang tepat atas tingkat paralelisme tanpa manajemen thread manual.

ParameterSemaphore BinerSemaphore Penghitung
Rentang0 atau 1dari 0 hingga N
Thread simultan1hingga N
Penerapansinyalisasi, benderakumpulan sumber daya, rate limiting

Semaphore vs Mutex

Pengembang sering bingung antara semaphore dan mutex, meskipun ada perbedaan mendasar di antara keduanya. Memahami perbedaan ini sangat penting untuk memilih mekanisme sinkronisasi yang tepat dalam proyek.

Prinsip Kepemilikan

Perbedaan utama — konsep kepemilikan. Mutex selalu tahu thread mana yang mengambilnya, dan hanya thread itu yang dapat melepaskannya. Semaphore tidak memiliki pemilik: thread mana pun dapat memanggil release, bahkan tanpa memanggil acquire. Ini membuat mutex lebih aman untuk perlindungan data, dan semaphore lebih fleksibel untuk koordinasi.

Kinerja dan Skenario Penggunaan

Dalam praktiknya, mutex lebih cepat untuk penguncian timbal balik sederhana berkat optimalisasi untuk skenario tipikal. Semaphore memerlukan overhead tambahan untuk memelihara penghitung. Namun, untuk membatasi paralelisme atau mengimplementasikan pola “produsen-konsumen”, semaphore sangat diperlukan.

KarakteristikSemaphoreMutex
Kepemilikantanpa pemilikmemiliki pemilik
Pelepasanthread mana punhanya thread pemilik
Penghitungdari 0 hingga Nbiner
Use casemembatasi paralelisme dan sinyalisasiperlindungan seksi kritis
Rekursitidakya (reentrant)

Penerapan Semaphore dalam pengembangan mobile

Dalam pengembangan aplikasi mobile, Semaphore digunakan untuk mengelola akses ke sumber daya terbatas: koneksi jaringan, file, database, dan komponen perangkat keras. Platform modern menyediakan implementasi bawaan yang nyaman.

Kumpulan Koneksi ke Server

Salah satu kasus tipikal — kumpulan koneksi HTTP. Aplikasi dapat mengirim tidak lebih dari 4 permintaan secara bersamaan ke server, karena API penyedia membatasi paralelisme. Semaphore dengan nilai awal 4 menjamin bahwa pada beban apa pun jumlah permintaan simultan tidak akan melebihi batas, dan thread lainnya akan menunggu dalam antrian.

Tanpa semaphore, saat peningkatan aktivitas pengguna yang tiba-tiba, infrastruktur server dapat mengalami kelebihan beban mendadak, yang menyebabkan timeout dan kesalahan 429 Too Many Requests. Semaphore berfungsi seperti sekring, hanya melewatkan sejumlah panggilan simultan yang ditentukan secara ketat terlepas dari jumlah thread aktif.

Semaphore di Kotlin untuk Android

Android menyediakan kelas Semaphore dari paket java.util.concurrent. Mari kita lihat contoh membatasi permintaan jaringan simultan hingga dua thread untuk mencegah kelebihan beban server.

kotlin
class ApiClient {
    private val throttle = Semaphore(2)

    suspend fun fetch(url: String): Result {
        throttle.acquire()
        return try {
            httpGet(url)
        } finally {
            throttle.release()
        }
    }
}

DispatchSemaphore di Swift untuk iOS

Di iOS, DispatchSemaphore dari GCD menyelesaikan tugas yang sama. Pengembang menggunakannya untuk menyinkronkan akses ke sumber daya dalam kode asinkron tanpa memblokir thread utama.

swift
let semaphore = DispatchSemaphore(value: 3)

func processBatch(_ items: [UIImage]) {
    for img in items {
        semaphore.wait()
        DispatchQueue.global().async {
            applyFilter(to: img)
            semaphore.signal()
        }
    }
}

Kesalahan Umum saat Bekerja dengan Semaphore

Kesalahan paling umum — release yang terlupakan saat terjadi pengecualian. Jika thread berakhir dengan kesalahan sebelum memanggil release, semaphore akan tetap diblokir selamanya untuk thread lainnya. Gunakan try/finally atau defer untuk pelepasan yang terjamin. Masalah kedua adalah deadlock saat mengambil beberapa semaphore dalam urutan berbeda oleh thread yang berbeda.

Pola penggunaan semaphore

Semaphore tidak hanya digunakan untuk perlindungan data, tetapi juga untuk koordinasi thread dalam skenario multithread yang kompleks. Pengetahuan tentang pola umum mempercepat pengembangan dan mengurangi kemungkinan kesalahan sinkronisasi.

Ada beberapa pola yang terbukti dalam penerapan semaphore di proyek nyata. Mengetahuinya membantu menghindari kesalahan tipikal dan membangun sistem multithread yang andal.

Rate Limiter (pembatas kecepatan)

Semaphore dengan nilai awal N dan pelepasan periodik melalui timer mengimplementasikan pembatasan kecepatan permintaan ke API. Misalnya, layanan mengizinkan 10 permintaan per detik: semaphore mulai dengan 10, setiap permintaan mengurangi penghitung, dan TimerTask terpisah mengembalikan penghitung ke nilai awal sekali per detik. Ini melindungi aplikasi dan server dari kelebihan beban.

Produsen-Konsumen melalui semaphore

Dalam tugas klasik produsen-konsumen, dua semaphore mengelola buffer: empty (izin menulis) dan full (izin membaca). Produsen memanggil acquire pada empty dan release pada full, Konsumen — sebaliknya. Skema ini menjamin bahwa Konsumen tidak akan pernah membaca buffer kosong, dan Produsen tidak akan mengisinya berlebihan.

Skema yang sama mendasari buffer terbatas dalam sistem operasi — buffer melingkar dengan ukuran tetap. Dalam aplikasi mobile, pola ini digunakan untuk memproses antrian gambar, file video, dan peristiwa analitik.

Throttling permintaan jaringan

Semaphore berhasil digunakan untuk throttling panggilan jaringan di layanan latar belakang. Misalnya, aplikasi analitik mengirim paket peristiwa ke server. Tanpa membatasi thread konkuren, pada beban puncak (memulai aplikasi, sinkronisasi setelah offline) jumlah permintaan simultan dapat melebihi batas server. Semaphore dengan nilai awal 3 menjamin pengiriman yang lancar dan mencegah pemblokiran di sisi server.

Pertanyaan yang Sering Diajukan

Apa perbedaan antara Semaphore dan penghitung biasa?

Semaphore bukan sekadar penghitung, tetapi primitif sinkronisasi dengan operasi atomik dan antrian tunggu. Penghitung biasa tidak memblokir thread dan tidak menjamin atomisitas incrementasi saat akses konkuren oleh beberapa thread.

Bisakah semaphore menyebabkan deadlock?

Ya, deadlock mungkin terjadi saat mengambil beberapa semaphore dalam urutan berbeda oleh thread yang berbeda. Misalnya, thread A mengambil S1, lalu S2, dan thread B — S2, lalu S1. Tetapkan urutan pengambilan yang seragam untuk semua semaphore dalam proyek.

Apa yang terjadi saat acquire dengan penghitung nol?

Thread diblokir dan beralih ke status tunggu. Thread tidak menghabiskan waktu prosesor sampai thread lain memanggil release. Di Java ini adalah status BLOCKED, di Swift thread dihentikan oleh GCD.

Apa perbedaan fundamental Binary Semaphore dengan Mutex?

Perbedaan utama — kepemilikan. Mutex hanya dapat dilepaskan oleh thread pemilik. Binary Semaphore dapat dilepaskan oleh thread mana pun, yang nyaman untuk sinyalisasi antar thread, tetapi kurang aman untuk melindungi integritas data.

Nilai awal apa yang harus dipilih untuk penghitung?

Nilai awal tergantung pada skenario. Untuk melindungi satu sumber daya — 1. Untuk kumpulan N koneksi — N. Untuk sinyalisasi antar thread gunakan 0, sehingga thread konsumen menunggu sinyal dari produsen.

Ringkasan

  • Semaphore — primitif sinkronisasi berbasis penghitung, diusulkan oleh Dijkstra pada tahun 1965.
  • Semaphore biner mengambil nilai 0 dan 1, semaphore penghitung — nilai non-negatif apa pun.
  • Operasi acquire dan release bersifat atomik dan thread-safe.
  • Berbeda dengan mutex, semaphore tidak terikat pada thread pemilik.
  • Semaphore penghitung digunakan untuk mengelola kumpulan sumber daya dan membatasi paralelisme.
  • Release yang terlupakan — kesalahan paling umum yang menyebabkan pembekuan thread.

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga