Java的可重入锁

1.可重入锁Lock

锁主要是为了保护临界区资源,防止由于并发执行而引起的数据异常。而synchronized关键字就是一种最简单的控制方法。经过jdk1.6对synchronized的优化,Lock和synchronized的性能相差并不多。 那么为什么还需要Lock,这当然有它的用处,
先看一个示例,锁的普通情况的使用:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo implements Runnable {
    static int i = 0; //声明为静态变量,否则无法直接在main方法中调用
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            lock.lock(); //进行同步控制
            try {
                ++i;
            } finally {
                lock.unlock(); //加锁部分写进try finally语句中,保证锁一定会被释放掉
            }
        }
    }

    public static void main(String[] args) {
        ReentrantLockDemo lockDemo = new ReentrantLockDemo();

        Thread t1 = new Thread(lockDemo);
        Thread t2 = new Thread(lockDemo);
        t1.start();
        t2.start();
        try {
            t1.join(); //等待t1线程和t2线程执行完毕
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i);
    }
}

Lock额外提供可几个以下的功能:
1)可重入
之所以把Lock称作可重入锁,是因为这把锁是可以反复进入的,当然这里反复进入仅仅局限于一个线程。上述代码的加锁部分,也可以加两把锁,如下:

lock.lock();
lock.lock();
try {
    ++i;
} finally {
    lock.unlock();
    lock.unlock();
}

注意:如果同一个线程多次获得锁,那么也必须释放相同次数的锁;如果释放次数多,那么会得到一个java.lang.IllegalMonitorStateException异常;如果释放次数少,那么其它线程将不能进入临界区。
2)中断响应
对于synchronized来说,如果一个线程在等待锁,那么结果只有两种情况,要么它获得锁继续执行,要么就等待。而使用重入锁,那么锁可以被中断,即在等待过程中,程序可以根据需要取消对锁的请求。

import java.util.concurrent.locks.ReentrantLock;

public class IntLock implements Runnable{
    ReentrantLock lock1 = new ReentrantLock();
    ReentrantLock lock2 = new ReentrantLock();
    int lock;
    public IntLock(int lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        //这里的if else主要是为了模拟死锁操作,这样可以看到通过调用中断方法,一个线程被中断,而另一个线程正常执行
        try {
            if (lock == 1){
                lock1.lockInterruptibly(); //调用这个方法加锁,同时可以响应中断
                Thread.sleep(500);
                lock2.lockInterruptibly();
                System.out.println(Thread.currentThread().getId() + ": 完成");
            }else {
                lock2.lockInterruptibly();
                Thread.sleep(500);
                lock1.lockInterruptibly();
                System.out.println(Thread.currentThread().getId() + ": 完成");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if(lock1.isHeldByCurrentThread()) //如果这把锁被当前线程持有,那么就释放这把锁
                lock1.unlock();
            if (lock2.isHeldByCurrentThread())
                lock2.unlock();
            System.out.println(Thread.currentThread().getId() + ": 线程退出!");
        }
    }

    public static void main(String[] args) {
        IntLock intLock1 = new IntLock(1);
        IntLock intLock2 = new IntLock(2);
        Thread t1 = new Thread(intLock1);
        Thread t2 = new Thread(intLock2);
        t1.start();
        t2.start();
        t1.interrupt(); //中断线程1
    }
}

3)锁申请等待限时

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TryLockDemo implements Runnable{
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        try {
            if(lock.tryLock(5, TimeUnit.SECONDS)){ //锁申请等待限时,等待5秒钟
                Thread.sleep(6000);
                System.out.println(Thread.currentThread().getId() + "complete!");
            }else {
                System.out.println(Thread.currentThread().getId() + "get lock failed");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(lock.isHeldByCurrentThread()) //如果当前线程持有该锁,则释放锁
                lock.unlock();
        }
    }

    public static void main(String[] args) {
        TryLockDemo tryLockDemo = new TryLockDemo();
        Thread t1 = new Thread(tryLockDemo);
        Thread t2 = new Thread(tryLockDemo);

        t1.start();
        t2.start();
    }
}

tryLock()也可以不带参数直接运行,在这种情况下,如果申请不成功,则直接返回false,不会等待。
4)总结
public void lock():获得锁,如果锁已经被占用,则等待;
public void lockInterruptibly():获得锁,但优先响应中断;
public boolean tryLock():尝试获得锁,如果成功返回true,继续执行;如果失败,返回false,不等待;
boolean tryLock(long timeout, TimeUnit unit):锁申请等待限时;
public void unlock():释放锁;

2.线程通信 (Condition)

Condition提供了一下几个方法:

void await() throws InterruptedException;
void awaitUninterruptibly();
boolean await(long time, TimeUnit unit) throws InterruptedException;
void signal();
void signalAll();

其和Object.wait()、Object.notify()方法作用类似;
await()方法使当前线程等待,同时释放锁;在其它线程中调用signal()方法或者signalAll()方法,线程会被唤醒,获得锁继续执行。当线程被中断时,也能跳出等待;
awaitUninterruptibly()作用和await()方法类似,但它不响应中断;
signal()方法用于唤醒线程。
示例如下:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo implements Runnable {

    private static Lock lock = new ReentrantLock(); //把变量声明为静态变量,这样可以直接在main方法中使用
    private static Condition condition =  lock.newCondition();

    @Override
    public void run() {
        try {
            lock.lock();
            condition.await();
            System.out.println("执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionDemo conditionDemo = new ConditionDemo();
        Thread t1 = new Thread(conditionDemo);
        t1.start();
        Thread.sleep(2000);
        //通知线程t1继续执行
        lock.lock(); //和Object的notify方法同理,这里也需要先获得重入锁,才能执行signal()方法
        condition.signal();
        lock.unlock(); //在唤醒线程之后,需要释放锁;如果省略这行代码,那么就算t1被唤醒,但由于它无法获得重入锁,
        //因而就无法继续执行。
    }
}

一旦线程被唤醒,它会重新尝试获得与其绑定的重入锁,如果成功获取就继续执行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值