ReentrantLock中的方法

http://www.cnblogs.com/xrq730/p/4855538.html

公平锁与非公平锁

ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平 锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,先来的未必就一定能先得到锁,从这 个角度讲,synchronized其实就是一种非公平锁。非公平锁的方式可能造成某些线程一直拿不到锁,自然是非公平的了。看一下例子,new ReentrantLock的时候有一个单一参数的构造函数表示构造的是一个公平锁还是非公平锁,传入true就可以了:

public class ThreadDomain42
{
    private Lock lock = new ReentrantLock(true);

    public void testMethod()
    {
        try
        {
            lock.lock();
            System.out.println("ThreadName" + Thread.currentThread().getName() + "获得锁");
        }
        finally
        {
            lock.unlock();
        }
    }
}
public static void main(String[] args) throws Exception
{
    final ThreadDomain42 td = new ThreadDomain42();
    Runnable runnable = new Runnable()
    {
        public void run()
        {
            System.out.println("◆线程" + Thread.currentThread().getName() + "运行了");
            td.testMethod();
        }
    };
    Thread[] threads = new Thread[5];
    for (int i = 0; i < 5; i++)
        threads[i] = new Thread(runnable);
    for (int i = 0; i < 5; i++)
        threads[i].start();
}

看一下运行结果:

◆线程Thread-0运行了
◆线程Thread-3运行了
ThreadNameThread-0获得锁
◆线程Thread-2运行了
◆线程Thread-1运行了
ThreadNameThread-3获得锁
◆线程Thread-4运行了
ThreadNameThread-2获得锁
ThreadNameThread-1获得锁
ThreadNameThread-4获得锁

我们的代码很简单,一执行run()方法的第一步就是尝试获得锁。看到结果里面获得锁的顺序和线程启动顺序是一致的,这就是公平锁。对比一下,如果是非公平锁运行结果是怎么样的,在new ReentrantLock的时候传入false:

◆线程Thread-1运行了
◆线程Thread-2运行了
◆线程Thread-0运行了
ThreadNameThread-1获得锁
ThreadNameThread-2获得锁
◆线程Thread-3运行了
◆线程Thread-4运行了
ThreadNameThread-3获得锁
ThreadNameThread-0获得锁
ThreadNameThread-4获得锁

线程启动顺序是1 2 0 3 4,获得锁的顺序却是1 2 3 0 4,这就是非公平锁,它不保证先排队尝试去获取锁的线程一定能先拿到锁

getHoldCount()

getHoldCount()方法返回的是当前线程调用lock()的次数,看一下例子:

public class ThreadDomain43
{
    private ReentrantLock lock = new ReentrantLock();

    public void testMethod1()
    {
        try
        {
            lock.lock();
            System.out.println("testMethod1 getHoldCount = " + lock.getHoldCount());
            testMethod2();
        }
        finally
        {
            lock.unlock();
        }
    }

    public void testMethod2()
    {
        try
        {
            lock.lock();
            System.out.println("testMethod2 getHoldCount = " + lock.getHoldCount());
        }
        finally
        {
            lock.unlock();
        }
    }
}
public static void main(String[] args)
{
    ThreadDomain43 td = new ThreadDomain43();
    td.testMethod1();
}

看一下运行结果:

testMethod1 getHoldCount = 1
testMethod2 getHoldCount = 2

ReentrantLock和synchronized一样,锁都是可重入的,同一线程的同一个ReentrantLock的lock()方法被调用了多少次,getHoldCount()方法就返回多少

getQueueLength()和isFair()

getQueueLength()方法用于获取正等待获取此锁定的线程估计数。注意"估计"两个字,因为此方法遍历内部数据结构的同时,线程的数据可能动态变化

isFair()用来获取此锁是否公平锁

看一下例子:

public class ThreadDomain44
{
    public ReentrantLock lock = new ReentrantLock();

    public void testMethod()
    {
        try
        {
            lock.lock();
            System.out.println("ThreadName = " + Thread.currentThread().getName() + "进入方法!");
            System.out.println("是否公平锁?" + lock.isFair());
            Thread.sleep(Integer.MAX_VALUE);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            lock.unlock();
        }
    }
}
public static void main(String[] args) throws InterruptedException
{
    final ThreadDomain44 td = new ThreadDomain44();
    Runnable runnable = new Runnable()
    {
        public void run()
        {
            td.testMethod();
        }
    };
    Thread[] threads = new Thread[10];
    for (int i = 0; i < 10; i++)
        threads[i] = new Thread(runnable);
    for (int i = 0; i < 10; i++)
        threads[i].start();
    Thread.sleep(2000);
    System.out.println("有" + td.lock.getQueueLength() + "个线程正在等待!");
}

看一下运行结果:

ThreadName = Thread-0进入方法!
是否公平锁?false
有9个线程正在等待!

ReentrantLock默认的是非公平锁,因此是否公平锁打印的是false。启动了10个线程,只有1个线程lock()了,其余9个等待,都符合预期。

hasQueuedThread()和hasQueuedThreads()

hasQueuedThread(Thread thread)用来查询指定的线程是否正在等待获取指定的对象监视器

hasQueuedThreads()用于查询是否有线程正在等待获取指定的对象监视器

看一下例子,换一个写法,ReentrantLock既然是一个类,就有类的特性,所以这次用继承ReentrantLock的写法,这也是很常见的:

public class ThreadDomain45 extends ReentrantLock
{
    public void waitMethod()
    {
        try
        {
            lock();
            Thread.sleep(Integer.MAX_VALUE);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            unlock();
        }
    }
}
public static void main(String[] args) throws InterruptedException
{
    final ThreadDomain45 td = new ThreadDomain45();
    Runnable runnable = new Runnable()
    {
        public void run()
        {
            td.waitMethod();
        }
    };
    Thread t0 = new Thread(runnable);
    t0.start();
    Thread.sleep(500);
    Thread t1 = new Thread(runnable);
    t1.start();
    Thread.sleep(500);
    Thread t2 = new Thread(runnable);
    t2.start();
    Thread.sleep(500);
    System.out.println("t0 is waiting?" + td.hasQueuedThread(t0));
    System.out.println("t1 is waiting?" + td.hasQueuedThread(t1));
    System.out.println("t2 is waiting?" + td.hasQueuedThread(t2));
    System.out.println("is any thread waiting?" + td.hasQueuedThreads());
}

这里加了几个Thread.sleep(500)保证线程按顺序启动(其实不按顺序启动也关系不大),看一下运行结果:

t0 is waiting?false
t1 is waiting?true
t2 is waiting?true
is any thread waiting?true

由于t0先启动获得了锁,因此不等待,返回false,另外两个线程则要等待获取t0的锁,因此返回的是true,而此ReentrantLock中有线程在等待,所以hasQueuedThreads()返回的是true

isHeldByCurrentThread()和isLocked()

isHeldByCurrentThread()表示此对象监视器是否由当前线程保持

isLocked()表示此对象监视器是否由任意线程保持

看一下例子:

public class ThreadDomain46 extends ReentrantLock
{
    public void testMethod()
    {
        try
        {
            lock();
            System.out.println(Thread.currentThread().getName() + "线程持有了锁!");
            System.out.println(Thread.currentThread().getName() + "线程是否持有锁?" +
                    isHeldByCurrentThread());
            System.out.println("是否任意线程持有了锁?" + isLocked());
            Thread.sleep(Integer.MAX_VALUE);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            unlock();
        }
    }

    public void testHoldLock()
    {
        System.out.println(Thread.currentThread().getName() + "线程是否持有锁?" +
                isHeldByCurrentThread());
        System.out.println("是否任意线程持有了锁?" + isLocked());
    }
}
public static void main(String[] args)
{
    final ThreadDomain46 td = new ThreadDomain46();
    Runnable runnable0 = new Runnable()
    {
        public void run()
        {
            td.testMethod();
        }
    };
    Runnable runnable1 = new Runnable()
    {
        public void run()
        {
            td.testHoldLock();
        }
    };
    Thread t0 = new Thread(runnable0);
    Thread t1 = new Thread(runnable1);
    t0.start();
    t1.start();
}

看一下运行结果:

Thread-0线程持有了锁!
Thread-1线程是否持有锁?false
Thread-0线程是否持有锁?true
是否任意线程持有了锁?true
是否任意线程持有了锁?true

这个应该很好理解,当前持有锁的是Thread-0线程,所以对于Thread-1来说自然不持有锁。

tryLock()和tryLock(long timeout, TimeUnit unit)

tryLock()方法的作用是,在调用try()方法的时候,如果锁没有被另外一个线程持有,那么就返回true,否则返回false

tryLock(long timeout, TimeUnit unit)是tryLock()另一个重要的重载方法,表示如果在指定等待时间内获得了锁,则返回true,否则返回false

注意一下,tryLock()只探测锁是否,并没有lock()的功能,要获取锁,还得调用lock()方法,看一下tryLock()的例子:

public class ThreadDomain47 extends ReentrantLock
{
    public void waitMethod()
    {
        if (tryLock())
            System.out.println(Thread.currentThread().getName() + "获得了锁");
        else
            System.out.println(Thread.currentThread().getName() + "没有获得锁");
    }
}
public static void main(String[] args)
{
    final ThreadDomain47 td = new ThreadDomain47();
    Runnable runnable = new Runnable()
    {
        public void run()
        {
            td.waitMethod();
        }
    };
    Thread t0 = new Thread(runnable);
    Thread t1 = new Thread(runnable);
    t0.start();
    t1.start();
}

看一下运行结果:

Thread-0获得了锁
Thread-1没有获得锁

第一个线程获得了锁返回true,第二个线程自然返回的false。由于有了tryLock()这种机制,如果一个线程长时间在synchronzied代码/synchronized代码块之中,别的线程不得不长时间无限等待的情况将可以被避免。

ReentrantLock中的其他方法

篇幅原因,ReentrantLock中还有很多没有被列举到的方法就不写了,看一下它们的作用:

1、getWaitQueueLength(Condition condition)

类似getQueueLength(),不过此方法的前提是condition。比如5个线程,每个线程都执行了同一个await()的await()方法,那么方法调用的返回值是5,因为5个线程都在等待获得锁

2、hasWaiters(Condition condition)

查询是否有线程正在等待与此锁有关的condition条件。比如5个线程,每个线程都执行了同一个condition的await()方法,那么方法调用的返回值是true,因为它们都在等待condition

3、lockInterruptibly()

如果当前线程未被中断,则获取锁

4、getWaitingThreads(Condition condition)

返回一个collection,它包含可能正在等待与此锁相关给定条件的那些线程,因为构造结果的时候实际线程可能动态变化,因此返回的collection只是尽力的估计值

时间: 2024-10-12 17:46:13

ReentrantLock中的方法的相关文章

Java多线程12:ReentrantLock中的方法

公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,先来的未必就一定能先得到锁,从这个角度讲,synchronized其实就是一种非公平锁.非公平锁的方式可能造成某些线程一直拿不到锁,自然是非公平的了.看一下例子,new ReentrantLock的时候有一个单一参数的构造函数表示构造的是一个公平锁还是非公平锁,传入true就可以了: publ

java多线程20 : ReentrantLock中的方法 ,公平锁和非公平锁

公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,先来的未必就一定能先得到锁,从这个角度讲,synchronized其实就是一种非公平锁.非公平锁的方式可能造成某些线程一直拿不到锁,自然是非公平的了.看一下例子,new ReentrantLock的时候有一个单一参数的构造函数表示构造的是一个公平锁还是非公平锁,传入true就可以了: publ

由AbstractQueuedSynchronizer和ReentrantLock来看模版方法模式

在学完volatile和CAS之后,近几天在撸AbstractQueuedSynchronizer(AQS)的源代码,很多并发工具都是基于AQS来实现的,这也是并发专家Doug Lea的初衷,通过写一个这样的基础工具来提高j.u.c的灵活性.具体可以看这篇论文的一段原文,我摘录一下: As is well-known (see e.g., [2]) nearly any synchronizer can be used to implement nearly any other. For exa

SSH配置环境都正常,但是每次执行到Action中的方法时就中断了,而且不报任何错误

SSH配置环境都正常,但是每次执行到Action中的方法时就不执行课,而且不报任何错误.Action中的方法封装的是业务层,业务层封装DAO层,检查了一天才发现错误在哪. 在applicationContext.xml中报了一个异常,如: Multiple annotations found at this line: - Exception 'com/dao/StudentDAO : Unsupported major.minor version 51.0' - Exception 'com/

Java中的方法重写 与 final关键字

Java中的方法重写 与 final关键字 以下程序是否存在错误? class Base { private final void method() { System.out.println("In Base..."); } } class Sub extends Base { public void method() { System.out.println("In Sub..."); } } 答案 正确的. 使用final关键字可以声明类.成员变量和成员方法,一

jsp 中声明方法的使用

1.在"<%!"和"%>"之间声明方法,该方法在整个JSP页面有效,但是该方法内定义的变量只在该方法内有效.这些方法将在Java程序片中被调用,当方法被调用时,方法内定义的变量被分配内存,调用完毕即可释放内存.当多个客户同时请求一个JSP页面时,他们可能使用方法操作成员变量,对这种情况应给予重视. 解决办法:通过synchronized方法操作一个成员变量来实现一个计数器 <%@ page contentType="text/html;c

JQuery中$.ajax()方法参数详解

url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如put和 delete也可以使用,但仅部分浏览器支持. timeout: 要求为Number类型的参数,设置请求超时时间(毫秒).此设置将覆盖$.ajaxSetup()方法的全局设 置. async:要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求. 如果需要发送同步请求,请将此选项

三个案例带你看懂LayoutInflater中inflate方法两个参数和三个参数的区别

版权声明:本文为sang原创文章,转载请注明出处. 目录(?)[+] 关于inflate参数问题,我想很多人多多少少都了解一点,网上也有很多关于这方面介绍的文章,但是枯燥的理论或者翻译让很多小伙伴看完之后还是一脸懵逼,so,我今天想通过三个案例来让小伙伴彻底的搞清楚这个东东.本篇博客我们不讲源码,只看使用.源码的解读会在下一篇博文中带来. inflate方法从大范围来看,分两种,三个参数的构造方法和两个参数的构造方法.在这两类中又有细分,OK,那我们就把各种情况都来演示一遍. 1.三个参数的in

ThinkPHP中create()方法自动验证

自动验证是ThinkPHP模型层提供的一种数据验证方法,可以在使用create创建数据对象的时候自动进行数据验证. 原理: create()方法收集表单($_POST)信息并返回,同时触发表单自动验证,过滤非法字段, 在控制器中使用create()方法,(返回值为true/false),会自动触发模型类中的$_validate属性(为父类Model中的方法,在子类Model中重写),在$_validate中自定义验证规则(验证规则下面会详细说明),当create()方法没有数据即返回值为fals