Skip to content
geeksforgeeks
  • Tutorials
    • Python
    • Java
    • Data Structures & Algorithms
    • ML & Data Science
    • Interview Corner
    • Programming Languages
    • Web Development
    • CS Subjects
    • DevOps And Linux
    • School Learning
    • Practice Coding Problems
  • Courses
    • DSA to Development
    • Get IBM Certification
    • Newly Launched!
      • Master Django Framework
      • Become AWS Certified
    • For Working Professionals
      • Interview 101: DSA & System Design
      • Data Science Training Program
      • JAVA Backend Development (Live)
      • DevOps Engineering (LIVE)
      • Data Structures & Algorithms in Python
    • For Students
      • Placement Preparation Course
      • Data Science (Live)
      • Data Structure & Algorithm-Self Paced (C++/JAVA)
      • Master Competitive Programming (Live)
      • Full Stack Development with React & Node JS (Live)
    • Full Stack Development
    • Data Science Program
    • All Courses
  • DSA
  • Algorithms
  • Analysis of Algorithms
  • Sorting
  • Searching
  • Greedy
  • Recursion
  • Backtracking
  • Dynamic Programming
  • Divide and Conquer
  • Geometric Algorithms
  • Mathematical Algorithms
  • Pattern Searching
  • Bitwise Algorithms
  • Branch & Bound
  • Randomized Algorithms
Open In App
Next Article:
Bakery Algorithm in Process Synchronization
Next article icon

Bakery Algorithm in Process Synchronization

Last Updated : 01 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Prerequisite - Critical Section, Process Synchronization, Inter Process Communication The Bakery algorithm is one of the simplest known solutions to the mutual exclusion problem for the general case of N process. Bakery Algorithm is a critical section solution for N processes. The algorithm preserves the first come first serve property.

How does the Bakery Algorithm work?
In the Bakery Algorithm, each process is assigned a number (a ticket) in a lexicographical order. Before entering the critical section, a process receives a ticket number, and the process with the smallest ticket number enters the critical section. If two processes receive the same ticket number, the process with the lower process ID is given priority.

How does the Bakery Algorithm ensure fairness?
The Bakery Algorithm ensures fairness by assigning a unique ticket number to each process based on a lexicographical order. This ensures that processes are served in the order they arrive, which guarantees that all processes will eventually enter the critical section.

  • Before entering its critical section, the process receives a number. Holder of the smallest number enters the critical section.
  • If processes Pi and Pj receive the same number,
if i < j 
Pi is served first;
else
Pj is served first.

  • The numbering scheme always generates numbers in increasing order of enumeration; i.e., 1, 2, 3, 3, 3, 3, 4, 5, ...

Notation - lexicographical order (ticket #, process id #) - Firstly the ticket number is compared. If same then the process ID is compared next, i.e.-

– (a, b) < (c, d) if a < c or if a = c and b < d
– max(a [0], . . ., a [n-1]) is a number, k, such that k >= a[i] for i = 0, . . ., n - 1

Shared data - choosing is an array [0..n - 1] of boolean values; & number is an array [0..n - 1] of integer values. Both are initialized to False & Zero respectively. Algorithm Pseudocode -

repeat
choosing[i] := true;
number[i] := max(number[0], number[1], ..., number[n - 1])+1;
choosing[i] := false;
for j := 0 to n - 1
do begin
while choosing[j] do no-op;
while number[j] != 0
and (number[j], j) < (number[i], i) do no-op;
end;
critical section

number[i] := 0;

remainder section
until false;

Explanation - Firstly the process sets its "choosing" variable to be TRUE indicating its intent to enter critical section. Then it gets assigned the highest ticket number corresponding to other processes. Then the "choosing" variable is set to FALSE indicating that it now has a new ticket number. This is in-fact the most important and confusing part of the algorithm. It is actually a small critical section in itself ! The very purpose of the first three lines is that if a process is modifying its TICKET value then at that time some other process should not be allowed to check its old ticket value which is now obsolete. This is why inside the for loop before checking ticket value we first make sure that all other processes have the "choosing" variable as FALSE. After that we proceed to check the ticket values of processes where process with least ticket number/process id gets inside the critical section. The exit section just resets the ticket value to zero.

Code - Here's the code implementation of the Bakery Algorithm. Run the following in a UNIX environment - 

C++
#include <chrono> #include <iostream> #include <mutex> #include <thread> #include <vector>  #define THREAD_COUNT 8  std::vector<int> tickets(THREAD_COUNT); std::vector<int> choosing(THREAD_COUNT); volatile int resource = 0;  std::mutex mtx; // Mutex for resource access  void lock(int thread) {     choosing[thread] = 1;      std::atomic_thread_fence(std::memory_order_seq_cst);      int max_ticket = 0;      for (int i = 0; i < THREAD_COUNT; ++i) {         int ticket = tickets[i];         max_ticket             = ticket > max_ticket ? ticket : max_ticket;     }      tickets[thread] = max_ticket + 1;      std::atomic_thread_fence(std::memory_order_seq_cst);     choosing[thread] = 0;      for (int other = 0; other < THREAD_COUNT; ++other) {         while (choosing[other]) {         }          std::atomic_thread_fence(std::memory_order_seq_cst);          while (tickets[other] != 0                && (tickets[other] < tickets[thread]                    || (tickets[other] == tickets[thread]                        && other < thread))) {         }     } }  void unlock(int thread) {     std::atomic_thread_fence(std::memory_order_seq_cst);     tickets[thread] = 0; }  void use_resource(int thread) {     std::lock_guard<std::mutex> lock(mtx);      if (resource != 0) {         std::cout << "Resource was acquired by " << thread                   << ", but is still in-use by " << resource                   << "!\n";     }      resource = thread;     std::cout << thread << " using resource...\n";      std::atomic_thread_fence(std::memory_order_seq_cst);     std::this_thread::sleep_for(std::chrono::seconds(2));     resource = 0; }  void thread_body(int thread) {     lock(thread);     use_resource(thread);     unlock(thread); }  int main() {     std::fill(tickets.begin(), tickets.end(), 0);     std::fill(choosing.begin(), choosing.end(), 0);     resource = 0;      std::vector<std::thread> threads;      for (int i = 0; i < THREAD_COUNT; ++i) {         threads.emplace_back(thread_body, i);     }      for (auto& thread : threads) {         thread.join();     }      return 0; } // Compile this code using the following command to link // against the pthread library: g++ -std=c++11 -pthread // Solution.cpp -o Solution  // Note: Ensure that you have the '-pthread' option to // properly link against the pthread library. 
C
// Importing the thread library #include "pthread.h"  #include "stdio.h"  // Importing POSIX Operating System API library #include "unistd.h"  #include "string.h"  // This is a memory barrier instruction. // Causes compiler to enforce an ordering // constraint on memory operations. // This means that operations issued prior // to the barrier will be performed // before operations issued after the barrier. #define MEMBAR __sync_synchronize() #define THREAD_COUNT 8  volatile int tickets[THREAD_COUNT]; volatile int choosing[THREAD_COUNT];  // VOLATILE used to prevent the compiler // from applying any optimizations. volatile int resource;  void lock(int thread) {      // Before getting the ticket number     //"choosing" variable is set to be true     choosing[thread] = 1;      MEMBAR;     // Memory barrier applied      int max_ticket = 0;      // Finding Maximum ticket value among current threads     for (int i = 0; i < THREAD_COUNT; ++i) {          int ticket = tickets[i];         max_ticket             = ticket > max_ticket ? ticket : max_ticket;     }      // Allotting a new ticket value as MAXIMUM + 1     tickets[thread] = max_ticket + 1;      MEMBAR;     choosing[thread] = 0;     MEMBAR;      // The ENTRY Section starts from here     for (int other = 0; other < THREAD_COUNT; ++other) {          // Applying the bakery algorithm conditions         while (choosing[other]) {         }          MEMBAR;          while (tickets[other] != 0                && (tickets[other] < tickets[thread]                    || (tickets[other] == tickets[thread]                        && other < thread))) {         }     } }  // EXIT Section void unlock(int thread) {      MEMBAR;     tickets[thread] = 0; }  // The CRITICAL Section void use_resource(int thread) {      if (resource != 0) {         printf("Resource was acquired by %d, but is still "                "in-use by %d!\n",                thread, resource);     }      resource = thread;     printf("%d using resource...\n", thread);      MEMBAR;     sleep(2);     resource = 0; }  // A simplified function to show the implementation void* thread_body(void* arg) {      long thread = (long)arg;     lock(thread);     use_resource(thread);     unlock(thread);     return NULL; }  int main(int argc, char** argv) {      memset((void*)tickets, 0, sizeof(tickets));     memset((void*)choosing, 0, sizeof(choosing));     resource = 0;      // Declaring the thread variables     pthread_t threads[THREAD_COUNT];      for (int i = 0; i < THREAD_COUNT; ++i) {          // Creating a new thread with the function         //"thread_body" as its thread routine         pthread_create(&threads[i], NULL, &thread_body,                        (void*)((long)i));     }      for (int i = 0; i < THREAD_COUNT; ++i) {          // Reaping the resources used by         // all threads once their task is completed !         pthread_join(threads[i], NULL);     }      return 0; } 
Java
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;  public class Main {     // Define the number of threads     private static final int THREAD_COUNT = 8;      // Define tickets for each thread     private static AtomicInteger[] tickets         = new AtomicInteger[THREAD_COUNT];      // Define choosing array to indicate if a thread is     // trying to enter the critical section     private static AtomicInteger[] choosing         = new AtomicInteger[THREAD_COUNT];      // Define the shared resource     private static AtomicInteger resource         = new AtomicInteger(0);      // Mutex for resource access     private static Lock mtx = new ReentrantLock();      public static void main(String[] args)     {         // Initialize tickets and choosing arrays         for (int i = 0; i < THREAD_COUNT; i++) {             tickets[i] = new AtomicInteger(0);             choosing[i] = new AtomicInteger(0);         }          // Initialize the shared resource         resource.set(0);          // Create threads         Thread[] threads = new Thread[THREAD_COUNT];          for (int i = 0; i < THREAD_COUNT; i++) {             final int thread = i;             threads[i] = new Thread(() -> {                 lock(thread); // Acquire the lock                 useResource(                     thread); // Use the shared resource                 unlock(thread); // Release the lock             });             threads[i].start(); // Start the thread         }          // Wait for all threads to finish         for (Thread thread : threads) {             try {                 thread.join();             }             catch (InterruptedException e) {                 e.printStackTrace();             }         }     }      // Method to acquire the lock     private static void lock(int thread)     {         choosing[thread].set(             1); // Indicate that the thread is trying to                 // enter the critical section          // Find the maximum ticket number and assign the         // next number to the current thread         int maxTicket = 0;         for (int i = 0; i < THREAD_COUNT; i++) {             int ticket = tickets[i].get();             maxTicket = Math.max(ticket, maxTicket);         }          tickets[thread].set(             maxTicket + 1); // Assign the next ticket number                             // to the current thread         choosing[thread].set(0); // Indicate that the thread                                  // has got its ticket          // Wait until all other threads have got their         // tickets and it's the current thread's turn         for (int other = 0; other < THREAD_COUNT; other++) {             while (choosing[other].get() != 0) {             }              while (tickets[other].get() != 0                    && (tickets[other].get()                            < tickets[thread].get()                        || (tickets[other].get()                                == tickets[thread].get()                            && other < thread))) {             }         }     }      // Method to release the lock     private static void unlock(int thread)     {         tickets[thread].set(             0); // Reset the ticket of the current thread     }      // Method to use the shared resource     private static void useResource(int thread)     {         mtx.lock(); // Acquire the mutex lock         try {             // Check if the resource is already in use             if (resource.get() != 0) {                 System.out.println(                     "Resource was acquired by " + thread                     + ", but is still in-use by "                     + resource.get() + "!");             }              // Use the resource             resource.set(thread);             System.out.println(thread                                + " using resource...");              // Simulate the usage of the resource             try {                 Thread.sleep(2000);             }             catch (InterruptedException e) {                 e.printStackTrace();             }              // Release the resource             resource.set(0);         }         finally {             mtx.unlock(); // Release the mutex lock         }     } } 
C#
using System; using System.Threading;  public class Solution {     private const int THREAD_COUNT = 8;     private static int[] tickets         = new int[THREAD_COUNT]; // Ticket array for each                                  // thread     private static int[] choosing         = new int[THREAD_COUNT]; // Array to indicate if a                                  // thread is choosing     private static volatile int resource         = 0; // Volatile resource variable     private static object lockObj         = new object(); // Lock object for synchronization      // Memory barrier instruction.     private static void Membar() { Thread.MemoryBarrier(); }      // Function to acquire the lock     private static void Lock(int thread)     {         choosing[thread]             = 1; // Indicate that this thread is choosing         Membar(); // Memory barrier          int maxTicket = 0;          // Find the maximum ticket value         for (int i = 0; i < THREAD_COUNT; i++) {             int ticket = tickets[i];             maxTicket = Math.Max(ticket, maxTicket);         }          // Assign ticket to this thread         tickets[thread] = maxTicket + 1;          Membar(); // Memory barrier         choosing[thread] = 0; // Done choosing         Membar(); // Memory barrier          // The ENTRY Section starts from here         for (int other = 0; other < THREAD_COUNT; ++other) {             // Applying the bakery algorithm conditions             while (choosing[other] != 0) {             }              Membar();              while (tickets[other] != 0                    && (tickets[other] < tickets[thread]                        || (tickets[other] == tickets[thread]                            && other < thread))) {             }         }     }      // EXIT Section     private static void Unlock(int thread)     {         Membar();         tickets[thread] = 0;     }      // The CRITICAL Section     private static void UseResource(int thread)     {         lock(lockObj) // Lock to ensure thread-safe access                       // to resource         {             // Check if resource is already in use             if (resource != 0) {                 Console.WriteLine(                     $                     "Resource was acquired by {thread}, but is still in-use by {resource}!");             }              // Acquire resource             resource = thread;             Console.WriteLine($                               "{thread} using resource...");         }          // Simulate resource usage         Thread.Sleep(TimeSpan.FromSeconds(2));          // Release resource         lock(lockObj) { resource = 0; }     }      // A simplified function to show the implementation     private static void ThreadBody(object arg)     {         long thread = (long)arg;         Lock((int)thread); // Acquire lock         UseResource((int)thread); // Use resource         Unlock((int)thread); // Release lock     }      public static void Main(string[] args)     {         Array.Clear(             tickets, 0,             THREAD_COUNT); // Initialize ticket array         Array.Clear(             choosing, 0,             THREAD_COUNT); // Initialize choosing array         resource = 0; // Initialize resource          Thread[] threads = new Thread[THREAD_COUNT];          for (int i = 0; i < THREAD_COUNT; ++i) {             threads[i] = new Thread(ThreadBody);             threads[i].Start(i);         }          for (int i = 0; i < THREAD_COUNT; ++i) {             threads[i].Join();         }     } } 
JavaScript
const THREAD_COUNT = 8; const tickets = new Array(THREAD_COUNT).fill(0); const choosing = new Array(THREAD_COUNT).fill(0); let resource = 0; const lockObj = {};  // Memory barrier instruction. function membar() {   // JavaScript doesn't have explicit memory barrier instructions.  // In most cases, JavaScript's single-threaded   //nature makes explicit memory barriers unnecessary.   // If working with Web Workers or other asynchronous operations,    // additional synchronization may be required. }  // Function to acquire the lock function lock(thread) {   choosing[thread] = 1;   membar();    let maxTicket = 0;    // Find the maximum ticket value   for (let i = 0; i < THREAD_COUNT; i++) {     const ticket = tickets[i];     maxTicket = Math.max(ticket, maxTicket);   }    // Assign ticket to this thread   tickets[thread] = maxTicket + 1;    membar();   choosing[thread] = 0;   membar();    // The ENTRY Section starts from here   for (let other = 0; other < THREAD_COUNT; ++other) {     while (choosing[other] !== 0) {}      membar();      while (tickets[other] !== 0 && (tickets[other] <      tickets[thread] || (tickets[other] === tickets[thread] &&      other < thread))) {}   } }  // EXIT Section function unlock(thread) {   membar();   tickets[thread] = 0; }  // The CRITICAL Section function useResource(thread) {   lockObj.lock = true;    // Check if resource is already in use   if (resource !== 0) {     console.log(`Resource was acquired by ${thread},      but is still in-use by ${resource}!`);   }    // Acquire resource   resource = thread;   console.log(`${thread} using resource...`);    // Simulate resource usage   setTimeout(() => {     // Release resource     resource = 0;     lockObj.lock = false;   }, 2000); }  // A simplified function to show the implementation function threadBody(thread) {   lock(thread);   useResource(thread);   unlock(thread); }  const threads = [];  for (let i = 0; i < THREAD_COUNT; ++i) {   threads[i] = setTimeout(threadBody, 0, i); } //This code is contributed by Aman.  // Note: This code uses setTimeout to simulate threads,  // but for actual multithreading in JavaScript, // you would need to use a library or environment that supports it,  // such as Node.js with Worker Threads // or a web browser environment with Web Workers. // use this node multithreading.js 

Output: Output

Advantages of Bakery Algorithm:

  • Fairness: The Bakery Algorithm provides fairness, as it ensures that all processes get a fair chance to access the critical section, and no process will be left waiting indefinitely.
  • Easy to Implement: The algorithm is easy to understand and implement, as it uses simple concepts such as turn numbers and flags to ensure mutual exclusion.
  • No Deadlock: The Bakery Algorithm ensures that there is no deadlock situation in the system.
  • No starvation: The algorithm also ensures that there is no starvation of any process, as every process gets a fair chance to enter the critical section.


Disadvantages Bakery Algorithm:

  • Not Scalable: The Bakery Algorithm is not scalable, as the overhead of the algorithm increases with the number of processes in the system.
  • High Time Complexity: The algorithm has a high time complexity, which increases as the number of processes in the system increases. This can result in performance issues in systems with a large number of processes.
  • Busy Waiting: The algorithm requires busy waiting, which can lead to wastage of CPU cycles and increased power consumption.
  • Memory Overhead: The algorithm requires extra memory to store the turn number and flag values, which can lead to increased memory overhead in systems with a large number of processes.

Next Article
Bakery Algorithm in Process Synchronization

S

Siddhant-Bajaj
Improve
Article Tags :
  • Algorithms
  • Operating Systems
  • GATE CS
  • Intellipaat
  • Functions
  • C++-Misc C++
  • QA - Placement Quizzes-Data Interpretation
  • TCS-coding-questions
  • Volkswagen IT Services
Practice Tags :
  • Algorithms
  • Functions

Similar Reads

    Dekker's algorithm in Process Synchronization
    Prerequisite - Process Synchronization, Inter Process Communication To obtain such a mutual exclusion, bounded waiting, and progress there have been several algorithms implemented, one of which is Dekker's Algorithm. To understand the algorithm let's understand the solution to the critical section p
    15+ min read
    Peterson's Algorithm in Process Synchronization
    Peterson's Algorithm is a classic solution to the critical section problem in process synchronization. It ensures mutual exclusion meaning only one process can access the critical section at a time and avoids race conditions. The algorithm uses two shared variables to manage the turn-taking mechanis
    15+ min read
    FIFO Barbershop in Process synchronization
    Overview :In process synchronization, there exists a Sleeping Barber problem, as discussed here. But in the above solution, there is no guarantee that the customers are served in the order they arrive in the barbershop, i.e. there is no guarantee that the customers enter a FIFO manner(First-in First
    5 min read
    Monitors in Process Synchronization
    Monitors are a higher-level synchronization construct that simplifies process synchronization by providing a high-level abstraction for data access and synchronization. Monitors are implemented as programming language constructs, typically in object-oriented languages, and provide mutual exclusion,
    3 min read
    Introduction of Process Synchronization
    Process Synchronization is used in a computer system to ensure that multiple processes or threads can run concurrently without interfering with each other.The main objective of process synchronization is to ensure that multiple processes access shared resources without interfering with each other an
    10 min read
geeksforgeeks-footer-logo
Corporate & Communications Address:
A-143, 7th Floor, Sovereign Corporate Tower, Sector- 136, Noida, Uttar Pradesh (201305)
Registered Address:
K 061, Tower K, Gulshan Vivante Apartment, Sector 137, Noida, Gautam Buddh Nagar, Uttar Pradesh, 201305
GFG App on Play Store GFG App on App Store
Advertise with us
  • Company
  • About Us
  • Legal
  • Privacy Policy
  • In Media
  • Contact Us
  • Advertise with us
  • GFG Corporate Solution
  • Placement Training Program
  • Languages
  • Python
  • Java
  • C++
  • PHP
  • GoLang
  • SQL
  • R Language
  • Android Tutorial
  • Tutorials Archive
  • DSA
  • Data Structures
  • Algorithms
  • DSA for Beginners
  • Basic DSA Problems
  • DSA Roadmap
  • Top 100 DSA Interview Problems
  • DSA Roadmap by Sandeep Jain
  • All Cheat Sheets
  • Data Science & ML
  • Data Science With Python
  • Data Science For Beginner
  • Machine Learning
  • ML Maths
  • Data Visualisation
  • Pandas
  • NumPy
  • NLP
  • Deep Learning
  • Web Technologies
  • HTML
  • CSS
  • JavaScript
  • TypeScript
  • ReactJS
  • NextJS
  • Bootstrap
  • Web Design
  • Python Tutorial
  • Python Programming Examples
  • Python Projects
  • Python Tkinter
  • Python Web Scraping
  • OpenCV Tutorial
  • Python Interview Question
  • Django
  • Computer Science
  • Operating Systems
  • Computer Network
  • Database Management System
  • Software Engineering
  • Digital Logic Design
  • Engineering Maths
  • Software Development
  • Software Testing
  • DevOps
  • Git
  • Linux
  • AWS
  • Docker
  • Kubernetes
  • Azure
  • GCP
  • DevOps Roadmap
  • System Design
  • High Level Design
  • Low Level Design
  • UML Diagrams
  • Interview Guide
  • Design Patterns
  • OOAD
  • System Design Bootcamp
  • Interview Questions
  • Inteview Preparation
  • Competitive Programming
  • Top DS or Algo for CP
  • Company-Wise Recruitment Process
  • Company-Wise Preparation
  • Aptitude Preparation
  • Puzzles
  • School Subjects
  • Mathematics
  • Physics
  • Chemistry
  • Biology
  • Social Science
  • English Grammar
  • Commerce
  • World GK
  • GeeksforGeeks Videos
  • DSA
  • Python
  • Java
  • C++
  • Web Development
  • Data Science
  • CS Subjects
@GeeksforGeeks, Sanchhaya Education Private Limited, All rights reserved
We use cookies to ensure you have the best browsing experience on our website. By using our site, you acknowledge that you have read and understood our Cookie Policy & Privacy Policy
Lightbox
Improvement
Suggest Changes
Help us improve. Share your suggestions to enhance the article. Contribute your expertise and make a difference in the GeeksforGeeks portal.
geeksforgeeks-suggest-icon
Create Improvement
Enhance the article with your expertise. Contribute to the GeeksforGeeks community and help create better learning resources for all.
geeksforgeeks-improvement-icon
Suggest Changes
min 4 words, max Words Limit:1000

Thank You!

Your suggestions are valuable to us.

What kind of Experience do you want to share?

Interview Experiences
Admission Experiences
Career Journeys
Work Experiences
Campus Experiences
Competitive Exam Experiences