Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the problems encountered in the use of Java polling lock

2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly introduces the relevant knowledge of how to solve the problems encountered in the use of Java polling lock, the content is detailed and easy to understand, the operation is simple and fast, and has a certain reference value. I believe that after reading this article, how to solve the problems encountered in the use of Java polling lock will be rewarded. Let's take a look at it.

Problem demonstration

Before we use polling locks, there may be problems like this:

Import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class DeadLockByReentrantLock {public static void main (String [] args) {Lock lockA = new ReentrantLock (); / / create lock A Lock lockB = new ReentrantLock () / / create lock B / / create thread 1 Thread T1 = new Thread (new Runnable () {@ Override public void run () {lockA.lock (); / / lock System.out.println ("Thread 1: get lock A!"); try {Thread.sleep (1000) System.out.println ("Thread 1: waiting for acquisition of B..."); lockB.lock (); / / Lock try {System.out.println ("Thread 1: get Lock B!");} finally {lockA.unlock () / / release lock}} catch (InterruptedException e) {e.printStackTrace ();} finally {lockA.unlock (); / / release lock}); t1.start () / / running thread / / creating thread 2 Thread T2 = new Thread (new Runnable () {@ Override public void run () {lockB.lock (); / / locking System.out.println ("Thread 2: get lock B!"); try {Thread.sleep (1000) System.out.println ("Thread 2: waiting to acquire A..."); lockA.lock (); / / Lock try {System.out.println ("Thread 2: get Lock A!");} finally {lockA.unlock () / / release lock}} catch (InterruptedException e) {e.printStackTrace ();} finally {lockB.unlock (); / / release lock}); t2.start (); / / running thread}}

The execution result of the above code is as follows:

As can be seen from the above results, there are threads waiting for each other in the program and trying to obtain each other's (lock) resources, which is a typical deadlock problem.

Simple version polling lock

When there is a deadlock problem, we can use polling locks to solve it. Its implementation idea is to acquire multiple locks by polling. If any lock acquisition fails in the middle, the fallback operation will be performed. Release all locks owned by the current thread and wait for the next re-execution, which can prevent multiple threads from owning and dominating the lock resources at the same time, thus directly solving the deadlock problem. The simple version of the polling lock is implemented as follows:

Import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SolveDeadLockExample2 {public static void main (String [] args) {Lock lockA = new ReentrantLock (); / / create lock A Lock lockB = new ReentrantLock () / / create lock B / / create thread 1 (using polling locks) Thread T1 = new Thread (new Runnable () {@ Override public void run () {/ / call polling lock pollingLock (lockA, lockB);}}); t1.start () / / running thread / / creating thread 2 Thread T2 = new Thread (new Runnable () {@ Override public void run () {lockB.lock (); / / locking System.out.println ("Thread 2: get lock B!"); try {Thread.sleep (1000) System.out.println ("Thread 2: waiting to acquire A..."); lockA.lock (); / / Lock try {System.out.println ("Thread 2: get Lock A!");} finally {lockA.unlock () / / release lock}} catch (InterruptedException e) {e.printStackTrace ();} finally {lockB.unlock (); / / release lock}); t2.start () / / running thread} / * polling lock * / private static void pollingLock (Lock lockA, Lock lockB) {/ / polling lock while (true) {if (lockA.tryLock ()) {/ / attempt to acquire lock System.out.println ("Thread 1: get lock A!") Try {Thread.sleep (1000); System.out.println ("Thread 1: waiting to get B...") If (lockB.tryLock ()) {/ / attempt to acquire lock try {System.out.println ("Thread 1: get lock B!");} finally {lockB.unlock () / / release lock System.out.println ("Thread 1: release lock B."); break;} catch (InterruptedException e) {e.printStackTrace () } finally {lockA.unlock (); / / release lock System.out.println ("Thread 1: release lock A.");}} / / wait one second before continuing with try {Thread.sleep (1000) } catch (InterruptedException e) {e.printStackTrace ();}

The execution result of the above code is as follows:

From the above results, we can see that when we use the polling lock in the program, there will be no deadlock problem, but the above polling lock is not perfect, let's take a look at the problem of this polling lock.

Problem 1: endless cycle

In the above simple version of the polling lock, if a thread has been occupying the lock resource or for a long time, it will cause the polling lock to enter an endless loop, and it will try to acquire the lock resource all the time, which will cause new problems and unnecessary performance overhead. The specific examples are as follows.

Counterexample import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SolveDeadLockExample {public static void main (String [] args) {Lock lockA = new ReentrantLock (); / / create lock A Lock lockB = new ReentrantLock () / / create lock B / / create thread 1 (using polling locks) Thread T1 = new Thread (new Runnable () {@ Override public void run () {/ / call polling lock pollingLock (lockA, lockB);}}); t1.start () / / running thread / / creating thread 2 Thread T2 = new Thread (new Runnable () {@ Override public void run () {lockB.lock (); / / locking System.out.println ("Thread 2: get lock B!"); try {Thread.sleep (1000) System.out.println ("Thread 2: waiting to acquire A..."); lockA.lock (); / / Lock try {System.out.println ("Thread 2: get Lock A!");} finally {lockA.unlock () / / release lock}} catch (InterruptedException e) {e.printStackTrace ();} finally {/ / if the code here is not executed, thread 2 has not released lock resources / / lockB.unlock () }); t2.start () / / running thread} / * polling lock * / public static void pollingLock (Lock lockA, Lock lockB) {while (true) {if (lockA.tryLock ()) {/ / attempt to acquire lock System.out.println ("Thread 1: get lock A!") Try {Thread.sleep (1000); System.out.println ("Thread 1: waiting to get B...") If (lockB.tryLock ()) {/ / attempt to acquire lock try {System.out.println ("Thread 1: get lock B!");} finally {lockB.unlock () / / release lock System.out.println ("Thread 1: release lock B."); break;} catch (InterruptedException e) {e.printStackTrace () } finally {lockA.unlock (); / / release lock System.out.println ("Thread 1: release lock A.");}} / / wait one second before continuing with try {Thread.sleep (1000) } catch (InterruptedException e) {e.printStackTrace ();}

The execution result of the above code is as follows:

As can be seen from the above results, thread 1 polled the lock into an endless loop.

Optimized version

In view of the above endless cycle, there are two ideas that we can improve:

Add a maximum number of times limit: if the lock has not been acquired after n attempts, the lock acquisition is considered to have failed, and the polling is terminated after executing the failure policy (the failure policy can be logging or other operations)

Add maximum time limit: if the lock has not been acquired after an n-second attempt to acquire the lock, the lock acquisition is considered to have failed, and polling is terminated after executing the failed policy.

Any one of the above strategies can solve the problem of endless loop. for the sake of implementation cost, we can use the maximum number of polls to improve the polling lock.

The specific implementation code is as follows:

Import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SolveDeadLockExample {public static void main (String [] args) {Lock lockA = new ReentrantLock (); / / create lock A Lock lockB = new ReentrantLock () / / create lock B / / create thread 1 (using polling locks) Thread T1 = new Thread (new Runnable () {@ Override public void run () {/ / call polling lock pollingLock (lockA, lockB, 3);}}); t1.start () / / running thread / / creating thread 2 Thread T2 = new Thread (new Runnable () {@ Override public void run () {lockB.lock (); / / locking System.out.println ("Thread 2: get lock B!"); try {Thread.sleep (1000) System.out.println ("Thread 2: waiting to acquire A..."); lockA.lock (); / / Lock try {System.out.println ("Thread 2: get Lock A!");} finally {lockA.unlock () / / release lock}} catch (InterruptedException e) {e.printStackTrace ();} finally {/ / Thread 2 forgot to release lock resource / / lockB.unlock (); / / release lock}) T2.start (); / / running thread} / * polling lock * * maxCount: maximum polling times * / public static void pollingLock (Lock lockA, Lock lockB, int maxCount) {/ / polling times counter int count = 0 While (true) {if (lockA.tryLock ()) {/ / attempt to acquire lock System.out.println ("Thread 1: get Lock A!"); try {Thread.sleep (1000); System.out.println ("Thread 1: waiting to acquire B...") If (lockB.tryLock ()) {/ / attempt to acquire lock try {System.out.println ("Thread 1: get lock B!");} finally {lockB.unlock () / / release lock System.out.println ("Thread 1: release lock B."); break;} catch (InterruptedException e) {e.printStackTrace () } finally {lockA.unlock (); / / release lock System.out.println ("Thread 1: release lock A.") }} / / determine whether the maximum number of times has been exceeded if (count++ > maxCount) {/ / terminate the loop System.out.println ("polling lock acquisition failed, log or execute other failure policies"); return } / / wait one second before continuing to attempt to acquire lock try {Thread.sleep (1000);} catch (InterruptedException e) {e.printStackTrace ();}

The execution result of the above code is as follows:

As can be seen from the above results, when we improve, the polling lock will not have the problem of endless loop, it will try a certain number of times and then terminate the execution.

Problem 2: thread starves to death

The polling wait time for our above polling lock is a fixed time, as shown in the following code:

/ wait 1 s before attempting to acquire (poll) lock try {Thread.sleep (1000);} catch (InterruptedException e) {e.printStackTrace ();}

This will cause the thread to starve to death in special cases, that is, the polling lock has been unable to obtain the lock, such as the following example.

Counterexample import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SolveDeadLockExample {public static void main (String [] args) {Lock lockA = new ReentrantLock (); / / create lock A Lock lockB = new ReentrantLock () / / create lock B / / create thread 1 (using polling locks) Thread T1 = new Thread (new Runnable () {@ Override public void run () {/ / call polling lock pollingLock (lockA, lockB, 3);}}); t1.start () / / running thread / / creating thread 2 Thread T2 = new Thread (new Runnable () {@ Override public void run () {while (true) {lockB.lock (); / / locking System.out.println ("Thread 2: get lock B!") Try {System.out.println ("Thread 2: waiting to acquire A..."); lockA.lock (); / / Lock try {System.out.println ("Thread 2: get Lock A!") } finally {lockA.unlock (); / / release the lock}} finally {lockB.unlock () / / release lock} / / wait one second and then continue to execute try {Thread.sleep (1000);} catch (InterruptedException e) {e.printStackTrace () }); t2.start (); / / running thread} / * * polling lock * / public static void pollingLock (Lock lockA, Lock lockB, int maxCount) {/ / cycle count int count = 0 While (true) {if (lockA.tryLock ()) {/ / attempt to acquire lock System.out.println ("Thread 1: get lock A!"); try {Thread.sleep / / wait 0.1s (time required to acquire lock) System.out.println ("Thread 1: wait for acquisition B..."); if (lockB.tryLock ()) {/ / attempt to acquire lock try {System.out.println ("Thread 1: acquire lock B!") } finally {lockB.unlock (); / / release lock System.out.println ("Thread 1: release lock B."); break } catch (InterruptedException e) {e.printStackTrace ();} finally {lockA.unlock (); / / release lock System.out.println ("Thread 1: release lock A.") }} / / determine whether the maximum number of times has been exceeded if (count++ > maxCount) {/ / terminate the loop System.out.println ("polling lock acquisition failed, log or execute other failure policies"); return } / / wait one second before continuing to attempt to acquire lock try {Thread.sleep (1000);} catch (InterruptedException e) {e.printStackTrace ();}

The execution result of the above code is as follows:

As can be seen from the above results, thread 1 (polling lock) has not successfully acquired the lock. The reason for this result is that thread 1 has a fixed waiting time of 1s for each poll, while thread 2 has the same frequency. As a result, thread 2 will first successfully acquire the lock, while thread 1 will always be "starved to death". The execution process is shown in the following figure:

Optimized version

Next, we can improve the fixed waiting time of the polling lock to a fixed time + random time, so as to avoid the problem of "starving to death" caused by the same frequency of acquiring the lock. The specific implementation code is as follows:

Import java.util.Random;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SolveDeadLockExample {private static Random rdm = new Random (); public static void main (String [] args) {Lock lockA = new ReentrantLock (); / / create lock A Lock lockB = new ReentrantLock () / / create lock B / / create thread 1 (using polling locks) Thread T1 = new Thread (new Runnable () {@ Override public void run () {/ / call polling lock pollingLock (lockA, lockB, 3);}}); t1.start () / / running thread / / creating thread 2 Thread T2 = new Thread (new Runnable () {@ Override public void run () {while (true) {lockB.lock (); / / locking System.out.println ("Thread 2: get lock B!") Try {System.out.println ("Thread 2: waiting to acquire A..."); lockA.lock (); / / Lock try {System.out.println ("Thread 2: get Lock A!") } finally {lockA.unlock (); / / release the lock}} finally {lockB.unlock () / / release lock} / / wait one second and then continue to execute try {Thread.sleep (1000);} catch (InterruptedException e) {e.printStackTrace () }); t2.start (); / / running thread} / * * polling lock * / public static void pollingLock (Lock lockA, Lock lockB, int maxCount) {/ / cycle count int count = 0 While (true) {if (lockA.tryLock ()) {/ / attempt to acquire lock System.out.println ("Thread 1: get lock A!"); try {Thread.sleep / / wait 0.1s (time required to acquire lock) System.out.println ("Thread 1: wait for acquisition B..."); if (lockB.tryLock ()) {/ / attempt to acquire lock try {System.out.println ("Thread 1: acquire lock B!") } finally {lockB.unlock (); / / release lock System.out.println ("Thread 1: release lock B."); break } catch (InterruptedException e) {e.printStackTrace ();} finally {lockA.unlock (); / / release lock System.out.println ("Thread 1: release lock A.") }} / / determine whether the maximum number of times has been exceeded if (count++ > maxCount) {/ / terminate the loop System.out.println ("polling lock acquisition failed, log or execute other failure policies"); return } / / wait for a certain time (fixed time + random time) before continuing to attempt to acquire lock try {Thread.sleep (300 + rdm.nextInt (8) * 100); / / fixed time + random time} catch (InterruptedException e) {e.printStackTrace () }

The execution result of the above code is as follows:

As can be seen from the above results, thread 1 (polling lock) will not starve to death after adding random waiting time.

This is the end of the article on "how to solve the problems encountered in the use of Java polling locks". Thank you for reading! I believe you all have a certain understanding of the knowledge of "how to solve the problems encountered in the use of Java polling locks". If you want to learn more, you are welcome to follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report