Java中的锁框架与线程同步的区别

线程同步机制可以使用 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的资源,它有两个方法和两个不同的锁。 有两个名为DisplayJobReadJob的作业。 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

(0)
上一篇 2022年6月7日
下一篇 2022年6月7日

相关推荐

发表回复

登录后才能评论