Java 锁 重入锁 读写锁

来源:互联网 发布:图像特征检测算法 编辑:程序博客网 时间:2024/05/04 10:05
ReentrantLock锁
java5.0之前,我们对于共享对象访问的机制只有synchronized关键字和volatile变量,现在提供了ReentrantLock
该锁比synchronized更多的灵活处置,比如,synchronized没法去解决死锁,不能中断正在等待获取锁的线程.
ReentrantLock锁
1.可重入
单线程可以重复进入,但必须重复退出。
2.可中断
可以中断正在等待的线程
3可限时
超时不能获得锁,就返回false,不会永久等待构成死锁
4.可轮训
tryLock() ,只有在获取到锁时,才返回true.
可轮训和可限时可以规避死锁
5.公平锁
public ReentrantLock(boolean fair)
public static ReentrantLock fairLock = new ReentrantLock(true);
设置为true时,按照先来先得的原则,后来的被放进等待队列中.

那么怎么选择synchronized和ReentrantLock了
从上面可以看出,ReentrantLock有很多的特性,那么如果想选择可中断,可限时,可轮询等就可以选择ReentrantLock.
锁的书写方式:
class X {   private final ReentrantLock lock = new ReentrantLock();   // ...   public void m() {      lock.lock();  // block until condition holds     try {       // ... method body     } finally {       lock.unlock()     }   } }

2. 可重入代码如下:
public class TestReentrantLock implements Runnable {private ReentrantLock lock = new ReentrantLock();private int a;@Overridepublic void run() {// TODO Auto-generated method stubfor (int i = 0; i < 1000; i++) {try {// 加锁lock.lock();a++;} catch (Exception e) {// TODO: handle exception} finally {// 释放锁lock.unlock();}}}public static void main(String[] args) throws InterruptedException {TestReentrantLock tr = new TestReentrantLock();Thread t1 = new Thread(tr);Thread t2 = new Thread(tr);t1.start();t2.start();t1.join();t2.join();System.out.println(tr.a);}}

3.可限时
如果在规定的时间内范围内,没有获取到该锁,等返回false,等待.
/** * 一段时间内,尝试获取锁 * @author Administrator * */public class TimeReentrantLock implements Runnable{private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {// TODO Auto-generated method stubtry {//设置在5秒内获取锁if(lock.tryLock(5, TimeUnit.SECONDS)){System.out.println(Thread.currentThread().getName()+"获取锁成功");Thread.sleep(6000);}else{System.out.println(Thread.currentThread().getName()+"获取锁失败");}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{if(lock.isHeldByCurrentThread()){lock.unlock();}}}public static void main(String[] args) {TimeReentrantLock tr = new TimeReentrantLock();Thread t1 = new Thread(tr,"t1");Thread t2 = new Thread(tr,"t2");t1.start();t2.start();}}

结果:
t1获取锁成功
t2获取锁失败
Exception in thread "t2" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
at java.util.concurrent.locks.ReentrantLock.unlock(Unknown Source)
at com.study.displaylock.TimeReentrantLock.run(TimeReentrantLock.java:25)
at java.lang.Thread.run(Unknown Source)

ReentrantLock源码分析
前面我们看到,ReentrantLock有两种锁,一种是公平锁,一种是非公平锁。
首先,来学习不公平锁
主要是下面三种技术来实现:
1.cas 2.等待队列 3.park

不公锁调用如下:

 final void lock() {            if (compareAndSetState(0, 1))//使用cas尝试设置值,如果设置成功,则表明现在没有现成得到锁,那么获取成功                setExclusiveOwnerThread(Thread.currentThread());            else                acquire(1);//再次尝试        } public final void acquire(int arg) {        if (!tryAcquire(arg) &&   //再次尝试获取锁,成功则不再做如下操作            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//否则添加到等待队列中            selfInterrupt();    }private Node addWaiter(Node mode) {        Node node = new Node(Thread.currentThread(), mode);        // Try the fast path of enq; backup to full enq on failure        Node pred = tail;        if (pred != null) {            node.prev = pred;            if (compareAndSetTail(pred, node)) {//cas成功,则直接放入队列                pred.next = node;                return node;            }        }        enq(node);.//不成功,强制放入队列        return node;    }final boolean acquireQueued(final Node node, int arg) {        boolean failed = true;        try {            boolean interrupted = false;            for (;;) {                final Node p = node.predecessor();                if (p == head && tryAcquire(arg)) {//再次尝试加锁                    setHead(node);                    p.next = null; // help GC                    failed = false;                    return interrupted;                }                if (shouldParkAfterFailedAcquire(p, node) &&//如果等待状态singal的就直接返回,singal表示这个线程可以unpack,如果是cancelled,则忽略这些线程                    parkAndCheckInterrupt())//否则,pack这些线程                    interrupted = true;            }        } finally {            if (failed)                cancelAcquire(node);        }    }

释放锁
public void unlock() {        sync.release(1);    }public final boolean release(int arg) {        if (tryRelease(arg)) {            Node h = head;            if (h != null && h.waitStatus != 0)                unparkSuccessor(h);//unpark 释放这些线程            return true;        }        return false;    }

读写锁:
ReentrantLock不管是读取还是写入都是互斥的,这对于大量的并发读取操作来说,性能就不会太高,ReentrantReadWriteLock读写锁,可以允许多个线程来读,只有一个线程来写。
它是读读不互斥,读写互斥,写写互斥。
/** * 读写锁 * 读写互斥   写写互斥 读读不互斥 * */public class TestReentrantRWLock {private  ReentrantReadWriteLock rw = new ReentrantReadWriteLock();private ReadLock rl = rw.readLock();private WriteLock wl = rw.writeLock();private int i=5;public void get(){try {rl.lock();Thread.sleep(2000);System.out.println("i的值是"+i);rl.unlock();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void add(){try {wl.lock();i++;System.out.println("i++后的值是:"+i);Thread.sleep(2000);wl.unlock();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void main(String[] args) {final TestReentrantRWLock trl = new TestReentrantRWLock();    Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtrl.get();}});  Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtrl.get();}});  Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtrl.add();}});  //t1.start();  t3.start();  t2.start();}}

如上所述:如果开启t1,t2线程,那么将不会阻塞,基本是同时运行的,如果开启t2,t3线程,就会发生阻塞。




原创粉丝点击