一、使用ReentrantLock 类
在Java多线程中,可以使用synchronized 关键字来实现线程之间的同步互斥,但在JDK1.5 中新增了 ReentrantLock 类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加的灵活。
1.使用ReentrantLock 实现同步
同步的方法所在类:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public void methodA(){
try {
lock.lock();
System.out.println("methodA begin ThreadName = "+ Thread.currentThread().getName()+" begin time "+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("methodA end ThreadName = "+ Thread.currentThread().getName()+" end time "+System.currentTimeMillis());
} catch (Exception e) {
}finally{
lock.unlock();
}
}
public void methodB(){
try {
lock.lock();
System.out.println("methodB begin ThreadName = "+ Thread.currentThread().getName()+" begin time "+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("methodB end ThreadName = "+ Thread.currentThread().getName()+" end time "+System.currentTimeMillis());
} catch (Exception e) {
}finally{
lock.unlock();
}
}
}
两个线程类ThreadA 与 ThreadB:
public class ThreadA extends Thread{
private MyService service;
public ThreadA(MyService service){
this.service = service;
}
@Override
public void run() {
service.methodA();
}
}
public class ThreadB extends Thread{
private MyService service;
public ThreadB(MyService service){
this.service = service;
}
@Override
public void run() {
service.methodB();
}
}
测试入口:
public class Run {
public static void main(String[] args){
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
}
运行结果:
从运行结果上看,当前线程打印完,将锁进行释放,其他线程才可以继续打印。调用lock.lock()代码的线程就持有了“对象监视器”其他线程只有等待锁被释放时再次争抢。效果和使用synchronized关键字一样,县城建史顺序执行的。
2.使用Condition 实现等待/通知
关键字synchronized 与wait()和notify()/notifyAll()方法结合可以实现等待/通知模式。借助Condition对象,类ReentrantLock 也可以实现同样的功能。Condition 类是 JDK5 中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock 对象里面可以创建多个Condition实例,线程对象可以注册在指定的Condition 中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。
代码如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await(){
try {
lock.lock();
System.out.println("进入await的时间为"+System.currentTimeMillis());
condition.await();
} catch (Exception e) {
}finally{
lock.unlock();
}
}
public void signal(){
try {
lock.lock();
System.out.println("进入signal的时间为"+System.currentTimeMillis());
condition.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
}
public class ThreadA extends Thread{
private MyService service;
ThreadA(MyService service){
this.service = service;
}
@Override
public void run() {
service.await();
}
}
public class Run {
public static void main(String[] args){
try {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(2000);
service.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
注意:在调用condition.await()方法之前需要调用lock.lock() 代码获得同步监视器。不然会报监视器出错的异常 java.lang.illegalMonitorStateException。
3.使用Condition 实现通知 特定的线程
代码如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA(){
try {
lock.lock();
System.out.println("begin awaitA时间为 " + System.currentTimeMillis() + " ThreadName= " +Thread.currentThread().getName());
conditionA.await();
System.out.println("end awaitA时间为 " + System.currentTimeMillis() + " ThreadName= " +Thread.currentThread().getName());
} catch (Exception e) {
}finally{
lock.unlock();
}
}
public void awaitB(){
try {
lock.lock();
System.out.println("begin awaitB时间为 " + System.currentTimeMillis() + " ThreadName= " +Thread.currentThread().getName());
conditionB.await();
System.out.println("end awaitB时间为 " + System.currentTimeMillis() + " ThreadName= " +Thread.currentThread().getName());
} catch (Exception e) {
}finally{
lock.unlock();
}
}
public void signalAll_A(){
try {
lock.lock();
System.out.println("signal_A时间为 " + System.currentTimeMillis() + " ThreadName= " +Thread.currentThread().getName());
conditionA.signalAll();
} catch (Exception e) {
}finally{
lock.unlock();
}
}
public void signalAll_B(){
try {
lock.lock();
System.out.println("signal_B时间为 " + System.currentTimeMillis() + " ThreadName= " +Thread.currentThread().getName());
conditionB.signalAll();
} catch (Exception e) {
}finally{
lock.unlock();
}
}
}
public class ThreadA extends Thread{
private MyService service ;
public ThreadA(MyService service){
this.service = service;
}
@Override
public void run() {
service.awaitA();
}
}
public class ThreadB extends Thread{
private MyService service ;
public ThreadB(MyService service){
this.service = service;
}
@Override
public void run() {
service.awaitB();
}
}
public class Run {
public static void main(String[] args){
try {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
通过运行结果 只有 A 线程被唤醒了,因此可以得知 使用 ReentrantLock 对象可以唤醒指定种类的线程,这是控制部分线程行为的方便方式。
4.公平锁与非公平锁
公平锁与非公平锁:锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取所得顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。
5.常用的一些方法
1)getHoldCount()
方法 int getHoleCount()方法的作用是 查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
public class GetHoldCount {
private ReentrantLock lock = new ReentrantLock();
public void methodOne(){
lock.lock();
lock.lock();
lock.lock();
lock.lock();
System.out.println("One 中 持锁数为"+lock.getHoldCount());
}
}
public class GetHoldCountRun {
public static void main(String[] args){
GetHoldCount ghc = new GetHoldCount();
ghc.methodOne();
}
}
运行结果:
2)getQueueLength()
方法int getQueueLength()的作用是返回正等待获取此锁定的线程估计数,比如有5个线程,1个线程首先执行 await()方法,那么在调用getQueueLength()方法后返回值是4,说明4个线程同时等待lock的释放。
public class GetQueueLength {
public ReentrantLock lock = new ReentrantLock();
public void method(){
try {
lock.lock();
System.out.println("ThreadName= "+Thread.currentThread().getName());
Thread.sleep(Integer.MAX_VALUE);
} catch (Exception e) {
}finally {
lock.unlock();
}
}
}
public class GetQueueLengthRun {
public static void main(String[] args){
final GetQueueLength gql = new GetQueueLength();
Runnable runnable = new Runnable(){
@Override
public void run() {
gql.method();
}
};
Thread[] threadArr = new Thread[10];
for(int i = 0; i< 10; i++){
threadArr[i] = new Thread(runnable);
}
for(int i = 0;i<10;i++){
threadArr[i].start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("还有"+ gql.lock.getQueueLength());
}
}
运行结果:
3)getWaitQueueLength(Condition condition)
方法 int getWaitQueueLength(Condition condition) 的作用是返回等待与此锁定相关的给定条件 Condition 的线程估计数,比如有5个线程,每个线程都执行了同一个condition对象的await()方法,则调用getWaitQueueLength(Condition condition)方法时返回的int 值是5。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class GetWaitQueueLength {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod(){
try {
lock.lock();
condition.await();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
public void notityMethod(){
try {
lock.lock();
System.out.println("有"+lock.getWaitQueueLength(condition));
condition.signalAll();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
}
public class GetWaitQueueLengthRun {
public static void main(String[] args){
final GetWaitQueueLength gwql = new GetWaitQueueLength();
Runnable runnable = new Runnable(){
@Override
public void run(){
gwql.waitMethod();
}
};
Thread[] threadArr = new Thread[10];
for(int i = 0;i<10;i++){
threadArr[i] = new Thread(runnable);
}
for(int i=0;i<10;i++){
threadArr[i].start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
gwql.notityMethod();
}
}
运行结果:
4)hasQueuedThread(Thread thread)与hasQueuedThreads()
hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待获取此锁定。
hasQueuedThreads()的作用 是查询是否有线程正在等待获取此锁定。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class HasQueuedThread {
public ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void waitMethod(){
try {
lock.lock();
Thread.sleep(Integer.MAX_VALUE);
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
}
public class HasQueuedThreadRun {
public static void main(String[] args) throws InterruptedException{
final HasQueuedThread hqt = new HasQueuedThread();
Runnable runnable = new Runnable(){
@Override
public void run(){
hqt.waitMethod();
}
};
Thread a = new Thread(runnable);
a.start();
Thread.sleep(500);
Thread b = new Thread(runnable);
b.start();
Thread.sleep(500);
System.out.println(hqt.lock.hasQueuedThread(a));
System.out.println(hqt.lock.hasQueuedThread(b));
System.out.println(hqt.lock.hasQueuedThreads());
}
}
5) hasWaiters(Condition condition)
方法 boolean hasWaiters(Condition condition)的作用是 查询是否有线程正在等待与此锁定有关的condition条件。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class HasWaiters {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod(){
try {
lock.lock();
condition.await();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
public void notityMethod(){
try {
lock.lock();
System.out.println("有没有线程正在等待 condition? "+lock.hasWaiters(condition)+"线程数是多少? "+lock.getWaitQueueLength(condition));
condition.signalAll();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
}
public class HasWaiterRun {
public static void main(String[] args){
final HasWaiters hw = new HasWaiters();
Runnable runnable = new Runnable(){
@Override
public void run(){
hw.waitMethod();
}
};
Thread[] threadArr = new Thread[10];
for(int i = 0;i<10;i++){
threadArr[i] = new Thread(runnable);
}
for(int i=0;i<10;i++){
threadArr[i].start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
hw.notityMethod();
}
}
运行结果: