线程同步机制可以使用 java.util.concurrent
包中的 Lock
框架来实现。锁框架像同步块一样工作,除了锁可以比 Java 的同步块更复杂。锁允许更灵活的同步代码结构。 Java 5 中引入了这种新方法来解决下面提到的同步问题。
下面来看看一个 Vector
类,它有许多同步方法。当一个类中有 100
个同步方法时,在任何给定时间点,这 100 个方法中只能执行一个线程。在任何给定时间点,使用同步块只允许一个线程访问一个方法。这是一个非常昂贵的操作。锁通过允许为不同目的配置各种锁来避免这种情况。一个人可以在一个锁下同步几个方法,在另一个锁下同步其他方法。这允许更多的并发性并提高整体性能。
示例
Lock lock = new ReentrantLock(); lock.lock(); // Critical section lock.unlock();
锁通过 lock()
方法获取并通过 unlock()
方法释放。 在没有 lock()
的情况下调用 unlock()
将引发异常。 如前所述,Lock 接口存在于 java.util.concurrent.locks
包中,而 ReentrantLock
实现了 Lock 接口。
注意:
lock()
调用的次数应始终等于unlock()
调用的次数。
在下面的代码中,用户创建了一个名为TestResource
的资源,它有两个方法和两个不同的锁。 有两个名为DisplayJob
和ReadJob
的作业。 LockTest
类创建 5
个线程来完成DisplayJob
和 5 个线程来完成ReadJob
。 所有 10 个线程共享一个资源TestResource
。
import java.util.Date; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; // Test class to test the lock example // 5 threads are created with DisplayJob // and 5 thread are created with ReadJob. // Both these jobs use single TestResource named "test". public class LockTest { public static void main(String[] args) { TestResource test = new TestResource(); Thread thread[] = new Thread[10]; for (int i = 0; i < 5; i++) { thread[i] = new Thread(new DisplayJob(test), "Thread " + i); } for (int i = 5; i < 10; i++) { thread[i] = new Thread(new ReadJob(test), "Thread " + i); } for (int i = 0; i < 10; i++) { thread[i].start(); } } } // DisplayJob class implementing Runnable interface. // This uses TestResource object passed in the constructor. // run method invokes displayRecord method on TestResource. class DisplayJob implements Runnable { private TestResource test; DisplayJob(TestResource tr) { test = tr; } @Override public void run() { System.out.println("display job"); test.displayRecord(new Object()); } } // ReadJob class implementing Runnable interface. // which uses TestResource object passed in the constructor. // run method invokes readRecord method on TestResource. class ReadJob implements Runnable { private TestResource test; ReadJob(TestResource tr) { test = tr; } @Override public void run() { System.out.println("read job"); test.readRecord(new Object()); } } // Class which has two locks and two methods. class TestResource { // displayQueueLock is created to make // displayQueueLock thread safe. // When T1 acquires lock on testresource(o1) // object displayRecord method // T2 has to wait for lock to be released // by T1 on testresource(o1) object // displayRecord method. But T3, can execute // readRecord method with out waiting for lock // to be released by t1 as // readRecord method uses readQueueLock not // displayQueueLock. private final Lock displayQueueLock = new ReentrantLock(); private final Lock readQueueLock = new ReentrantLock(); // displayRecord uses displayQueueLock to // achieve thread safety. public void displayRecord(Object document) { final Lock displayLock = this.displayQueueLock; displayLock.lock(); try { Long duration = (long) (Math.random() * 10000); System.out.println(Thread.currentThread(). getName() + ": TestResource: display a Job"+ " during " + (duration / 1000) + " seconds ::"+ " Time - " + new Date()); Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.printf("%s: The document has been"+ " dispalyedn", Thread.currentThread().getName()); displayLock.unlock(); } } // readRecord uses readQueueLock to achieve thread safety. public void readRecord(Object document) { final Lock readQueueLock = this.readQueueLock; readQueueLock.lock(); try { Long duration = (long) (Math.random() * 10000); System.out.println (Thread.currentThread().getName() + ": TestResource: reading a Job during " + (duration / 1000) + " seconds :: Time - " + new Date()); Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.printf("%s: The document has"+ " been readn", Thread.currentThread().getName()); readQueueLock.unlock(); } } }
运行结果如下:
display job display job display job display job display job read job read job read job read job read job Thread 5: TestResource: reading a Job during 4 seconds :: Time – Wed Feb 27 15:49:53 UTC 2022 Thread 0: TestResource: display a Job during 6 seconds :: Time – Wed Feb 27 15:49:53 UTC 2022 Thread 5: The document has been read Thread 6: TestResource: reading a Job during 4 seconds :: Time – Wed Feb 27 15:49:58 UTC 2022
在上面的示例中,DisplayJob
不需要等待 ReadJob
线程完成任务,因为 ReadJob 和 Display 作业使用两个不同的锁。synchronized
无法做到这一点。
区别如下:
参数 | 锁框架 | 同步 |
---|---|---|
跨方法 | 是的,可以跨方法实现锁,可以在方法1中调用lock() ,在方法2中调用unlock() 。 |
不可能 |
尝试获取锁 | 是的,锁框架支持trylock(timeout) 方法,如果资源可用,它将获取资源的锁,否则返回false ,线程不会被阻塞。 |
同步时不可能 |
公平锁管理 | 是的,在锁框架的情况下可以使用公平锁管理。 它将锁交给长时间等待的线程。 即使在将公平模式设置为 true 的情况下,如果对 trylock 进行了编码,也会首先提供它。 |
同步时不可能 |
等待线程列表 | 是的,使用 Lock 框架可以看到等待线程列表 |
同步时不可能。 |
释放异常中的锁定 | Lock.lock(); myMethod();Lock.unlock(); 如果 myMethod() 抛出任何异常,则无法在此代码中执行 unlock() 。 |
在这种情况下,同步工作很清楚,它释放锁 。 |
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/264264.html