线程并发五:线程安全之重入锁

来源:互联网 发布:红家 蓝家 知乎 编辑:程序博客网 时间:2024/06/05 10:18
  1. 重入锁简单介绍

    之前介绍的synchronized关键字是一种最简单的控制方法。下面说一说线程安全的另一种实现方式——–重入锁
    重入锁使用java.util.concurrent.loks.ReentrantLock 类来实现
    a. lock() //获得锁,如果锁已被占用,则等待
    b. lockInterruptibly() //获得锁,但优先响应中断
    c. tryLock() //尝试获得锁,如果成功返回true.失败返回false.
    d. tryLock(long time, TimeUnit nuit) //在指定时间内尝试获取锁
    e. unlock() //释放锁
    f. isHeldByCurrentThread() //检测当前线程是否持有锁。

import java.util.Date;import java.util.concurrent.locks.ReentrantLock;public class ReenterLockSimpleTest implements Runnable {    public static ReentrantLock lock = new ReentrantLock();    public void sayHello(){        lock.lock();        lock.lock();        try {            System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": say hello!!");            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        } finally {            lock.unlock();            lock.unlock();        }    }    public void run(){        while(true){            sayHello();        }    }    public static void main(String[] args){        Thread t1 = new Thread(new ReenterLockSimpleTest(),"test1");        Thread t2 = new Thread(new ReenterLockSimpleTest(),"test2");        t1.start();        t2.start();    }}

从打印可以看出,1秒只打印一条,两个线程相互交替。重入锁有着明显的操作痕迹,何时加锁,何时释放锁。也正是这样,重入锁对逻辑控制的灵活性要远远高于synchronized,重入锁之所以叫重入锁,那是因为这种锁可以反复进入的,当然这里的反复进入仅仅局限于同一个线程。并且,释放所得次数要是获取锁的次数一样。

2.中断响应
对于synchronized来说,如果一个线程在等待锁,只有两种情况,要么得到锁,要么继续等待,而重入锁提供另一种可能,那就是线程中断。

import java.util.concurrent.locks.ReentrantLock;public class InterruptedReenterLock implements Runnable {    public static ReentrantLock lock1 = new ReentrantLock();    public static ReentrantLock lock2 = new ReentrantLock();    @Override    public void run() {        // TODO Auto-generated method stub        try {            if(!Thread.currentThread().isInterrupted()){                if("test1".equals(Thread.currentThread().getName())){                    lock1.lockInterruptibly();                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        // TODO: handle exception                    }                    lock2.lockInterruptibly();                }else {                    lock2.lockInterruptibly();                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        // TODO: handle exception                    }                    lock1.lockInterruptibly();                }            }        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            if(lock1.isHeldByCurrentThread()){                lock1.unlock();            }            if(lock2.isHeldByCurrentThread()){                lock2.unlock();            }            System.out.println(Thread.currentThread().getName() + " 线程退出!");        }    }    public static void main(String[] args) throws InterruptedException{        Thread t1 = new Thread(new InterruptedReenterLock(),"test1");        Thread t2 = new Thread(new InterruptedReenterLock(),"test2");        t1.start();        t2.start();        Thread.sleep(2000);        t2.interrupt();    }}

如上代码会出现两个线程相互等待的情况,当线程2发生中断后,释放所有的锁,线程1在线程2释放锁之后获得锁,两个线程先后结束。

3.限时申请锁
tryLock() 方法限时等待,有两种方式, 一种为限时申请锁,需要设定时间, 一种为尝试获取锁,无论成功还是失败直接返回,不等待。

import java.util.Date;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;public class TryLockTiem implements Runnable {    public static ReentrantLock lock = new ReentrantLock();    public void sayHello(){        try {            if(lock.tryLock(4, TimeUnit.SECONDS)){                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get lock!!");                Thread.sleep(6000);            }else{                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get nothing!!");            }        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            if(lock.isHeldByCurrentThread()){                lock.unlock();            }        }    }    public void run(){        while(true){            sayHello();        }    }    public static void main(String[] args){        Thread t1 = new Thread(new TryLockTiem(),"test1");        Thread t2 = new Thread(new TryLockTiem(),"test2");        t1.start();        t2.start();    }}
import java.util.Date;import java.util.concurrent.locks.ReentrantLock;public class TryLockTiem implements Runnable {    public static ReentrantLock lock = new ReentrantLock();    public void sayHello(){        try {            if(lock.tryLock()){                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get lock!!");                Thread.sleep(2000);            }else{                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get nothing!!");                Thread.sleep(1000);            }        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            if(lock.isHeldByCurrentThread()){                System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": release lock!!");                lock.unlock();            }        }    }    public void run(){        while(true){            sayHello();        }    }    public static void main(String[] args){        Thread t1 = new Thread(new TryLockTiem(),"test1");        Thread t2 = new Thread(new TryLockTiem(),"test2");        t1.start();        t2.start();    }}