Use case 1
One use case would be to completely avoid running the thread. Like in the example below, for example a very strict internet hotspot where you can only access one webpage at once, and other requests are cancelled.
With synchronized
you cannot cancel it, since it waits until it can obtain the lock. So tryLock
just gives you flexibility to cancel something or to run other behavior instead.
package Concurrency;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LimitedHotspotDemo
{
private static Lock webAccessLock = new ReentrantLock();
private static class AccessUrl implements Runnable
{
private String url;
public AccessUrl(String url)
{
this.url = url;
}
@Override
public void run()
{
if(webAccessLock.tryLock()) {
System.out.println("Begin request for url " + url);
try {
Thread.sleep(1500);
System.out.println("Request completed for " + url);
} catch (InterruptedException e) {
webAccessLock.unlock();
return;
} finally {
webAccessLock.unlock();
}
} else {
System.out.println("Cancelled request " + url + "; already one request running");
}
}
}
public static void main(String[] args)
{
for(String url : Arrays.asList(
"https://www.google.com/",
"https://www.microsoft.com/",
"https://www.apple.com/"
)) {
new Thread(new AccessUrl(url)).start();
}
}
}
Output:
Begin request for url https://www.microsoft.com/
Cancelled request https://www.google.com/; already one request running
Cancelled request https://www.apple.com/; already one request running
Request completed for https://www.microsoft.com/
Use case 2
Another use case would be a light sensor which keeps the light on when there is movement in the room (with Sensor
thread). There is another thread (TurnOffLights
) running to switch the light off when there is no more movement in the room for a few seconds.
The TurnOffLights
thread uses tryLock
to obtain a lock. If no lock can be obtained, the process is delayed for 500ms. The last Sensor
thread is blocking the lock for 5 seconds, after which the TurnOffLights
thread can obtain the lock and turn off the lights.
So in this case the TurnOffLights
thread is only allowed to turn off the lights when there are no more signals to the Sensor
for 5 seconds. The TurnOffLights
thread is using tryLock
to obtain the lock.
package Concurrency;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LightSensorDemo
{
private static volatile Lock lock = new ReentrantLock();
private static volatile Thread lastSignal = null;
private static Sensor sensor = new Sensor();
private static class Sensor implements Runnable
{
private static Boolean preparing = false;
public static Boolean isPreparing()
{
return preparing;
}
@Override
public void run()
{
System.out.println("Signal send " + Thread.currentThread().getName());
try {
invalidatePreviousSignalsAndSetUpCurrent();
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
//System.out.println("Signal interrupted " + Thread.currentThread().getName());
return;
} finally {
lock.unlock();
}
}
private static synchronized void invalidatePreviousSignalsAndSetUpCurrent() throws InterruptedException
{
preparing = true;
if(lastSignal != null) {
lastSignal.interrupt();
}
lastSignal = Thread.currentThread();
lock.lockInterruptibly();
preparing = false;
}
}
private static class TurnOffLights implements Runnable
{
@Override
public void run()
{
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Interrupted" + this.getClass().getName());
return;
}
if (!Sensor.isPreparing()) {
if(lock.tryLock()) {
try {
System.out.println("Turn off lights");
break;
} finally {
lock.unlock();
}
} else {
System.out.println("Cannot turn off lights yet");
}
} else {
System.out.println("Cannot turn off lights yet");
}
}
}
}
public static void main(String[] args) throws InterruptedException
{
Thread turnOffLights = new Thread(new TurnOffLights());
turnOffLights.start();
//Send 40 signals to the light sensor to keep the light on
for(int x = 0; x < 10; x++) {
new Thread(sensor).start(); //some active movements
new Thread(sensor).start(); //some active movements
new Thread(sensor).start(); //some active movements
new Thread(sensor).start(); //some active movements
Thread.sleep(250);
}
turnOffLights.join();
}
}
Notice also that I use lock.lockInterruptibly();
to interrupt previous signals. So the 5 second countdown always starts from the last signal.
Output is something like:
...
Cannot turn off lights yet
Cannot turn off lights yet
Signal send Thread-19
Signal send Thread-20
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Turn off lights
Process finished with exit code 0