Java多线程锁管理

Java中,多线程编程是一个复杂但必不可少的话题。线程同步是确保线程安全的关键手段之一。在多线程环境中,一个线程可能需要同时持有多个锁,以确保对共享资源的访问是安全的。本文将通过一个示例程序来展示Java线程如何管理多个锁,并讨论这种做法的优缺点。

示例程序

以下是一个Java程序,演示了线程如何同时获取多个锁。

public class Processor implements Runnable { Object objLock; String name; public Processor(String name, Object objLock) { this.objLock = objLock; this.name = name; } public void run() { display(); } public void display() { synchronized(this) { synchronized(objLock) { for (int i = 0; i <= 5; i++) { System.out.println("i = " + i + " In thread " + Thread.currentThread() + " acquired lock on this i.e. " + name + " and objLock"); } } } } } public class Locks { public static void main(String[] args) { Object objLock = new Object(); Processor p1 = new Processor("p1", objLock); Processor p2 = new Processor("p2", objLock); Thread t1 = new Thread(p1, "t1"); Thread t2 = new Thread(p1, "t2"); Thread t3 = new Thread(p2, "t3"); Thread t4 = new Thread(p2, "t4"); t1.start(); t2.start(); t3.start(); t4.start(); } }

程序输出

程序运行后,输出如下:

i = 0 In threadThread[t1, 5, main]acquired lock on this i.e. p1 and objLock i = 1 In threadThread[t1, 5, main]acquired lock on this i.e. p1 and objLock i = 2 In threadThread[t1, 5, main]acquired lock on this i.e. p1 and objLock i = 3 In threadThread[t1, 5, main]acquired lock on this i.e. p1 and objLock i = 4 In threadThread[t1, 5, main]acquired lock on this i.e. p1 and objLock i = 5 In threadThread[t1, 5, main]acquired lock on this i.e. p1 and objLock i = 0 In threadThread[t3, 5, main]acquired lock on this i.e. p2 and objLock i = 1 In threadThread[t3, 5, main]acquired lock on this i.e. p2 and objLock i = 2 In threadThread[t3, 5, main]acquired lock on this i.e. p2 and objLock i = 3 In threadThread[t3, 5, main]acquired lock on this i.e. p2 and objLock i = 4 In threadThread[t3, 5, main]acquired lock on this i.e. p2 and objLock i = 5 In threadThread[t3, 5, main]acquired lock on this i.e. p2 and objLock

分析

从输出中可以看到,每个线程在执行时都成功地获取了两个锁:一个是当前对象的锁(即p1或p2),另一个是所有线程共享的objLock。

在主函数main()中,创建了两个Processor对象p1和p2,并将它们分别传递给两个线程t1和t2,以及t3和t4。这意味着objLock可以被所有线程看到。

在run()方法中,display()函数包含一个for循环,该循环在"this"和"objLock"上同步。因此,任何线程必须先获取自身的锁,然后才能获取objLock。

当所有线程启动时,t1和t2会竞争获取p1的锁,而t3和t4会竞争获取p2的锁。假设t1获取了p1的锁,t3获取了p2的锁,那么t1和t3将竞争objLock。如果t1获得了objLock,它将执行for循环,同时持有两个锁。完成后,t1将释放两个锁,t3将有机会执行for循环。t2和t4之间会发生类似的竞争条件,得到了一个清晰的有序输出。

讨论

这个示例清楚地展示了一个线程可以获取并持有多个锁。但这是否是一个好的做法呢?将在后面讨论。

获取多个可以确保线程在执行关键代码段时,对共享资源的访问是安全的。然而,这种做法也可能导致死锁,因为线程可能在等待获取一个锁的同时持有另一个锁。为了避免这种情况,通常建议使用锁排序策略,即总是以相同的顺序获取锁。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485