Java并发编程-重入锁

重入锁

    重入是是对synchronized的一个增强版本,因为synchronized是阻塞的。很容易导致死锁。重入锁在我们日常使用中比较灵活,能够很好的控制,性能比较synchronized好。

    重入锁使用java.util.concurrent.locks.ReentrantLock类来实现。其中里面的方法有如下:

lock():获取锁,如果无法获取锁,一直尝试获取锁
lockInterruptibly():获得锁,除非当前线程被中断,如果获取到锁,则立即返回,如果获取不到锁,则有两种情况发生:1.一直获取线程
                       的锁,2.其他线程中断当前的线程,避免死锁。
tryLock():尝试获得锁,如果成功,返回true,失败返回false。该方法不等待,立即返回。
tryLock(long time, TimeUnit unit):在给定时间内尝试获得锁。
unLock():释放锁。

重入锁的特点

    1.可重入 2.可中断 3.可限时 4.公平锁

--可重入

代码例子:

package com.currency;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author qianyi
 */
public class ReenterLockTest implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;
    @Override
    public void run() {
        for (int j = 0; j < 10000000; j++) {
            lock.lock();
            lock.lock();
            try {
                i++;
            } finally {
                lock.unlock();
                lock.unlock();//如果这里代码注释,将进入死锁。 程序不会退出
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReenterLockTest rl = new ReenterLockTest();
        Thread t1 = new Thread(rl);
        Thread t2 = new Thread(rl);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

上面代码中,对i进行操作操作,我们用ReenterLock进行加锁和解锁。我们可以手动操作什么时候加锁,什么时候解锁,比synchronized要灵活,对于上面代码需要注意一下两点。第一是ReenterLock加锁了几次,我们必须手动释放锁几次,如果我们不手动释放锁,就会导致死锁,程序将永不停止,第二ReenterLock的释放操作,最好在finally子句块中。

--可中断:lockInterruptibly

package com.currency;
import java.util.concurrent.locks.ReentrantLock;
/**
 * lockInterruptibly可以中断的锁
 * @author qianyi
 *
 */
public class InterruptiblyLock implements Runnable {
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();
    int lock;

    /**
     * 控制加锁顺序,方便构造死锁
     *
     * @param lock
     */
    public InterruptiblyLock(int lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            if (lock == 1) {
                lock1.lockInterruptibly();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
                lock2.lockInterruptibly();
            } else {
                lock2.lockInterruptibly();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
                lock1.lockInterruptibly();
            }
        } 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) throws InterruptedException {
        InterruptiblyLock il1 = new InterruptiblyLock(1);
        InterruptiblyLock il2 = new InterruptiblyLock(2);
        Thread t1 = new Thread(il1);
        Thread t2 = new Thread(il2);
        t1.start();
        t2.start();
        Thread.sleep(1000);
        // 中断其中一个线程.则finally中判断,谁持有这把锁,如果持有则进行解锁操操作
        t2.interrupt();
    }
}

上面代码中两个线程如果不执行t2.interrupt(),则将导致死锁。可以中断一个线程操作来唤醒这个线程,处理中断信息,然后进行锁的释放。

--限时特性:

package com.currency;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author qianyi
 * 锁申请等待延时
 * 指定一个锁的时间,给锁的申请释放掉
 */
public class TimeLock implements Runnable {
    public static ReentrantLock timeLock = new ReentrantLock();

    @Override
    public void run() {
        try {
        	//线程1进入之后获取锁,然后睡眠6S中。当线程2再次进入之后,
        	//尝试获取这把锁,发现一只获取不到,则进入finally进行释放锁
            if (timeLock.tryLock(5, TimeUnit.SECONDS)) {//
                Thread.sleep(6000);
            } else {
                System.out.println("get lock failed");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (timeLock.isHeldByCurrentThread()) {
                timeLock.unlock();
            }
        }

    }
    public static void main(String[] args) {
        TimeLock tl = new TimeLock();
        Thread t1 = new Thread(tl);
        Thread t2 = new Thread(tl);
        t1.start();
        t2.start();
    }
}

限时锁带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。

--公平锁

ReentrantLock构造函数中提供了两种锁:创建公平锁和非公平锁(默认)。代码如下:
public ReentrantLock() {
       sync = new NonfairSync();
}
 public ReentrantLock(boolean fair) {
          sync = fair ? new FairSync() : new NonfairSync();
    }

Condition条件

    Condition对象和Object中的wait以及notify方法大致相同,但是wait和notify方法是和synchronized关键字合作使用的,而Condition是与重入锁相关联的。通过Condition的newCondition()方法可以生成一个与当前重入锁绑定的Condition实例。利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时间得到通知,继续执行。

await() 方法会是当前线程等待,同时释放当前锁,当其他线程中使用signal() 或signalAll() 方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。这和obj.wait方法很相似。

awaitUninterruptibly() 方法与await() 方法基本相同,只不过它不会在等待过程中响应中断。

signal() 方法用于唤醒一个在等待中的线程,这和obj.notify方法很类似。

package com.currency;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author qianyi
 */
public class ReenterLockcondition implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();
    @Override
    public void run() {
        lock.lock();
        try {
            condition.await();//等待
            System.out.println("线程继续执行。。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReenterLockcondition rlc = new ReenterLockcondition();
        Thread t1 = new Thread(rlc);
        t1.start();//开启线程
        Thread.sleep(2000);
        lock.lock();
        condition.signal();   // 通知线程继续执行
        lock.unlock();
    }
}

    上面代码中,通过lock生成与之绑定的的Condition对象。condition.await()要求在线程上等待,condition.singal()通知线程继续运行.和Object对象中的wait()和notify方法一样,Condition.await()调用之后这个线程会释放这把锁。等待被唤醒,同理,在Condition.signal方法调用后,线程对尝试获取这把锁,

在signal方法调用后,系统会从当前Condition对象的等待队列中,唤醒一个线程。一旦线程被唤醒,它会重新尝试获得与之绑定的重入锁,一旦成功获取,就可以继续执行了。因此,在signal方法调用之后,一般需要释放相关的锁,谦让给被唤醒的线程,让他可以继续执行。比如,在本例中,lock.unlock()就释放了重入锁。


  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值