公平锁与非公平锁

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只是尽力的估计值

Java多线程12:ReentrantLock中的方法的更多相关文章

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

    公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得 ...

  2. java 多线程12 : 无锁 实现CAS原子性操作----原子类

    由于java 多线程11:volatile关键字该文讲道可以使用不带锁的情况也就是无锁使变量变成可见,这里就理解下如何在无锁的情况对线程变量进行CAS原子性及可见性操作 我们知道,在并发的环境下,要实 ...

  3. java 多线程阻塞队列 与 阻塞方法与和非阻塞方法

    Queue是什么 队列,是一种数据结构.除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的.无论使用哪种排序方式,队列的头都是调用remove()或poll()移 ...

  4. java多线程有几种实现方法?线程之间如何同步

    java中多线程的实现方法有两种:1.直接继承thread类:2.实现runnable接口: 同步的实现方法有五种:1.同步方法:2.同步代码块:3.使用特殊域变量(volatile)实现线程同步:4 ...

  5. Java 多线程启动为什么调用 start() 方法而不是 run() 方法?

    多线程在工作中多多少少会用到,我们知道启动多线程调用的是 start() 方法,而不是 run() 方法,你知道原因吗? 在探讨这个问题之前,我们先来了解一些多线程的基础知识~ 线程的状态 Java ...

  6. JAVA多线程实现的三种方法

    JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...

  7. Java多线程【三种实现方法】

    java多线程 并发与并行 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行 并行:一组程 ...

  8. java多线程12设计模式

    1.Single Threaded Execution Pattern(单线程运行模式) 2.Immutable Pattern(一成不变的模式) 3.Guarded Suspension Patte ...

  9. java多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?

    多线程有两种实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify 先看一下java线程运行时各个阶段的运行状态 j ...

随机推荐

  1. ExtJs 获取Dom对象

    对象指页面上的某一部分,如:Input等.我觉得在EXT JS中会有三类基本对象,htmlelement , EXT.Element和CompositeElement .分别解释一下: htmlele ...

  2. IOS 数据库

    系统自带可以储存字段的字典: NSUserDefaults *user = [[NSUserDefaults alloc] init]; 存 : [user setObject:@"YES& ...

  3. 读书笔记——body and html

    在看<常见标签的默认属性值及相互作用——关于CSS reset的思考>的时候,其中说body默认的margin是8px.但是,将body的backgound-color:red:后,看到的 ...

  4. RTABMAP-ROS RGB-D的建图原理

    CoreNode.cpp: new CoreWrapper -- CoreWrapper.cpp: process() -- mapsManager_.updateMapCaches MapsMana ...

  5. 第十三章:降维:主成分分析PCA

  6. UI进阶

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  7. VPN帐号(每1小时自动断开及更新密码)

    免费的vpn帐号 http://freevpn.wwdhz.com/

  8. Mac下升级Nodejs

    突然发现系统中的nodejs版本比较旧,想升级一下但又不想下载安装包一步一步安装, 发现还是可以很简单用命令行升级的. 首先得清理npm的缓存 sudo npm cache clean -f 安装 n ...

  9. 用sql语句清除日志

    DUMP TRANSACTION [数据库] WITH NO_LOGBACKUP LOG [数据库] WITH NO_LOGDBCC SHRINKDATABASE([数据库])

  10. C# GetHashCode与Equals在HashTable表查找时的关系

    using System; using System.Collections.Generic; using System.Text; using Microsoft.Win32; using Syst ...