理解java的ReetrantLock

来源:互联网 发布:电脑拍照的软件 编辑:程序博客网 时间:2024/05/07 18:35

关于java多线程并发控制,除了内置关键字synchronized外,还有lock,lock的一个实现类就是ReetrantLock,Lock可以实现更灵活的多线程并发控制。

如何使用,举个栗子

public class ReetrantLockDemo {    static final Lock lock = new ReentrantLock();    static Runnable runnable1 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable1 running " + i);                }            } finally {                lock.unlock();            }        }    };    static Runnable runnable2 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable2 running " + i);                }            } finally {                lock.unlock();            }        }    };    public static void main(String[] args) {        new Thread(runnable1).start();        new Thread(runnable2).start();    }}

这就是lock的最基本使用,使用lock和unlock方法对代码片进行加锁了解锁,效果和synchronized一样,两个线程串行化执行了,控制台输出如下:
runnable1 running 0
runnable1 running 1
runnable1 running 2
runnable1 running 3
runnable1 running 4
runnable2 running 0
runnable2 running 1
runnable2 running 2
runnable2 running 3
runnable2 running 4

也就是说synchronized能做的lock都能做,那么看看lock还能做什么。它还提供了trylock方法,这个方法就是说我会尝试获取锁,但获取锁如果失败,不会导致当前线程阻塞,直接跳过,把上面的栗子稍微改造一下。

public class ReetrantLockDemo {    static final Lock lock = new ReentrantLock();    static Runnable runnable1 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable1 running " + i);                }            } finally {                lock.unlock();            }        }    };    static Runnable runnable2 = new Runnable() {        @Override        public void run() {            if (lock.tryLock()) {                try {                    for (int i = 0; i < 5; i++) {                        System.out.println("runnable2 running " + i);                    }                } finally {                    lock.unlock();                }            } else {                System.out.println("Can not get the lock, skip running");            }        }    };    public static void main(String[] args) {        new Thread(runnable1).start();        new Thread(runnable2).start();    }}

在线程2中尝试获取锁,如果获取不到,就直接跳过,控制台输出如下:
runnable1 running 0
Can not get the lock, skip running
runnable1 running 1
runnable1 running 2
runnable1 running 3
runnable1 running 4

另外trylock方法还可以设置等待时间,等待多少时间后获取不了锁就跳过。在构造ReetrantLock时可以设置一个boolean值,就是 new ReetrantLock(true/false),选择是否构造一个“公平锁”,所谓公平锁就是让所有等待线程按他们的等待顺序来得到当前锁,防止一些线程很久都不被执行,不过需要注意实现公平锁需要一些额外计算开销,慎用。

lock还有一个功能就是condition,简单的说就是条件设置。举个栗子,比如上面2个线程,需求是第一个线程循环到3的时候第二个线程开始,这时候第一个线程停止,等到第二个线程执行到3的时候在启动。那么这里就有2个条件:
1. 第二个线程需要在第一个线程执行到3的时候才能启动,用condition1表示
2. 第一个线程需要在第二个线程执行到3的时候重启。用condition2表示

public class ReetrantLockDemo {    static final Lock lock = new ReentrantLock();    static boolean thread1Arrive3 = false;    static boolean thread2Arrive3 = false;    static final Condition condition1 = lock.newCondition();    static final Condition condition2 = lock.newCondition();    static Runnable runnable1 = new Runnable() {        @Override        public void run() {            lock.lock();            try {                for (int i = 0; i < 5; i++) {                    System.out.println("runnable1 running " + i);                    if(i==3){                        thread1Arrive3=true;                        condition1.signal();                        if(!thread2Arrive3){                            condition2.await();                        }                                   }                }            } catch (Exception e) {                e.printStackTrace();            } finally {                lock.unlock();            }        }    };    static Runnable runnable2 = new Runnable() {        @Override        public void run() {                lock.lock();                try {                    if(!thread1Arrive3){                        condition1.await();                    }                    for (int i = 0; i < 5; i++) {                        System.out.println("runnable2 running " + i);                        if(i==3){                            thread2Arrive3=true;                            condition2.signal();                        }                    }                } catch (Exception e) {                    e.printStackTrace();                } finally {                    lock.unlock();                }        }    };    public static void main(String[] args) {        new Thread(runnable1).start();        new Thread(runnable2).start();    }}

运行结果:
runnable1 running 0
runnable1 running 1
runnable1 running 2
runnable1 running 3
runnable2 running 0
runnable2 running 1
runnable2 running 2
runnable2 running 3
runnable2 running 4
runnable1 running 4

condition的await方法可以让当前线程释放锁并且在指定的condition上面进行等待,而signal方法则通知在condition上等待的线程获取锁并继续执行。condition其实维护了一个队列的数据结构,await相当于往队列中put,而signal则是从队列中取出。所以需要注意如果在执行signal时,没有线程在这个condition上去await,singal方法也不会阻塞。所以需要注意使用condition的时候一定是在满足条件的时候才去进行await和signal(示例代码中使用2个boolean变量作为判定条件),如果signal先执行,await后执行就直接死锁了。

0 0
原创粉丝点击