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

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() 在这种情况下,同步工作很清楚,它释放锁

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程