How it works...

The Java Concurrency API provides a class that can be used to implement synchronization mechanisms with features of locks or semaphores. It's called AbstractQueuedSynchronizer, and as the name suggests, it's an abstract class. It provides operations to control access to a critical section and manage a queue of threads that are blocked and are awaiting access to the section. The operations are based on two abstract methods:

  • tryAcquire(): This method is called to try and get access to a critical section. If the thread that calls it can access the critical section, the method returns the true value. Otherwise, it returns the false value.
  • tryRelease(): This method is called to try and release access to a critical section. If the thread that calls it can release access, the method returns the true value. Else, it returns the false value.

In these methods, you have to implement the mechanism you use to control access to a critical section. In this case, you implemented the MyAbstractQueuedSynchonizer class that extends the AbstractQueuedSyncrhonizer class and implements the abstract methods using an AtomicInteger variable to control access to the critical section. This variable will store the value 0 if the lock is free, so a thread can have access to the critical section, and the value 1 if the lock is blocked, so a thread 'doesn't have access to the critical section.

You used the compareAndSet() method provided by the AtomicInteger class that tries to change the value you specify as the first parameter with the value you specify as the second parameter. To implement the tryAcquire() method, you try to change the value of the atomic variable from zero to one. Similarly, to implement the tryRelease() method, you try to change the value of the atomic variable from one to zero.

You have to implement AtomicInteger class because other implementations of the AbstractQueuedSynchronizer class (for example, the one used by ReentrantLock) are implemented as private classes internally. This is carried out in the class that uses it, so you don't have access to it.

Then, you implemented the MyLock class. This class implements the Lock interface and has a MyQueuedSynchronizer object as an attribute. To implement all the methods of the Lock interface, you used methods of the MyQueuedSynchronizer object.

Finally, you implemented the Task class that implements the Runnable interface and uses a MyLock object to get access to the critical section. This critical section puts the thread to sleep for 2 seconds. The main class creates a MyLock object and runs 10 Task objects that share the lock. The main class also tries to get access to the lock using the tryLock() method.

When you execute the example, you can see how only one thread has access to the critical section, and when that thread finishes, another one gets access to it.

You can use your own Lock interface to write log messages about its utilization, control the time that it's locked, or implement advanced synchronization mechanisms to control, for example, access to a resource so that it's only available at certain times.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.161.155