java中的lock
2016-01-27 23:34
375 查看
lock就像同步块一样是一种线程同步机制,除此之外locks要比java的同步块更加复杂。locks(和其他的更加先进的同步机制)是使用同步块创建的,所以我们完全摆脱synchronize关键字是不可能的。从java5开始,java.util.concurrent.locks包含了几种lock的实现,所以你或许不必实现自己的locks,但是,你依然要知道怎么样使用它们,知道他们背后的实现原理也是非常有帮助的,要想得到更多的细节,可以看一下java.util.concureent.locks.lock接口
注意inc()方法中的synchronized(this)块,这个块确保在某一时刻只能有一个线程执行return ++count这个操作。在同步块中的代码可以更好,但是这个++count操作足够了解了。
这个Counter类可以写成如下的方式,使用lock替代同步块lock()方法锁住了Lock实例以至于所有调用lock方法的线程都会阻塞直到unlock方法执行。
下边这是一个简单的lock的实现
注意这个while(isLocked)循环,也叫作“旋转锁”。旋转锁和方法wait以及notify在线程信号这篇文章中多次讲到了。当isLocked为true的时候,线程调用lock方法在wait方法调用中等待。在这种情况相下没有收到notify调用的时候线程从wait调用中返回的是不确定的,线程要重新检查isLocked条件来看看继续是否是安全的,而不仅仅是假设条件安全的。如果isLocked是假的,线程会退出循环并且设置isLocked为true,为其他线程调用lock方法锁定Lock实例。当线程在关键区完成工作的时候(在lock和unlock之间的代码),线程调用unlock方法,执行unlock方法把isLocked设置回假,叫醒在wait方法中的等待的线程。
注意outer方法和inner方法都声明为同步的,在java中和synchronized(this)是等价的。如果线程调用了outer方法毫无疑问它会调用内部的inner方法。因为两个方法或者是块在同一个监视对象(this)上进行的同步,如果一个线程已经拥有了这个监视对象的锁,它可以访问同一个监视对象上的所有的同步代码块,这就叫做重入,线程可以重新进入已经拥有锁的任何的代码块。
早前看到的实现并不是重入,如果我们像下边这样重写Reentrant类,线程调用outer会在inner方法中的lock.lock中阻塞。
一个线程调用outer方法会首先锁上Lock实例,然后调用inner方法。在inner方法中线程再一次的想锁住锁实例,这会失败(意思是线程将会被阻塞),因为锁实例已经在outer方法中被锁住了。线程将会被第二次阻塞的原因是它调用了lock方法而没有调用unlock犯法,我们看看这个lock方法的实现之后就很明显了
注意while循环现在把锁实例也考虑进去了,或者锁被解锁调用线程是锁实例。在while循环中的条件决定是否线程要退出,当前情况下isLocked必须是false才可以,而不管线程是否已经锁住它了。为了让锁类重入,我们需要做一些小小的改变
注意while循环现在把锁实例也考虑进去了,或者锁被解锁调用线程是锁实例
翻译的不好,还有一些没有翻译出来,想看原文的同学可以看下边的链接
原文地址:http://tutorials.jenkov.com/java-concurrency/locks.html
一个简单的lock
我们以一个java的同步块代码开始public class Counter{ private int count = 0; public int inc(){ synchronized(this){ return ++count; } } }
注意inc()方法中的synchronized(this)块,这个块确保在某一时刻只能有一个线程执行return ++count这个操作。在同步块中的代码可以更好,但是这个++count操作足够了解了。
public class Counter{ private Lock lock = new Lock(); private int count = 0; public int inc(){ lock.lock(); int newCount = ++count; lock.unlock(); return newCount; } }
这个Counter类可以写成如下的方式,使用lock替代同步块lock()方法锁住了Lock实例以至于所有调用lock方法的线程都会阻塞直到unlock方法执行。
下边这是一个简单的lock的实现
public class Lock{ private boolean isLocked = false; public synchronized void lock() throws InterruptedException{ while(isLocked){ wait(); } isLocked = true; } public synchronized void unlock(){ isLocked = false; notify(); } }
注意这个while(isLocked)循环,也叫作“旋转锁”。旋转锁和方法wait以及notify在线程信号这篇文章中多次讲到了。当isLocked为true的时候,线程调用lock方法在wait方法调用中等待。在这种情况相下没有收到notify调用的时候线程从wait调用中返回的是不确定的,线程要重新检查isLocked条件来看看继续是否是安全的,而不仅仅是假设条件安全的。如果isLocked是假的,线程会退出循环并且设置isLocked为true,为其他线程调用lock方法锁定Lock实例。当线程在关键区完成工作的时候(在lock和unlock之间的代码),线程调用unlock方法,执行unlock方法把isLocked设置回假,叫醒在wait方法中的等待的线程。
Lock Reentrance
java中的同步块是再入的。也就是说,如果一个java线程进入了一个同步代码块,在监控对象上拿到了同步锁并进入同步状态,线程可以其他的java代码块,在相同的监视对象上进行同步。下边这是一个例子public class Reentrant{ public synchronized outer(){ inner(); } public synchronized inner(){ //do something } }
注意outer方法和inner方法都声明为同步的,在java中和synchronized(this)是等价的。如果线程调用了outer方法毫无疑问它会调用内部的inner方法。因为两个方法或者是块在同一个监视对象(this)上进行的同步,如果一个线程已经拥有了这个监视对象的锁,它可以访问同一个监视对象上的所有的同步代码块,这就叫做重入,线程可以重新进入已经拥有锁的任何的代码块。
早前看到的实现并不是重入,如果我们像下边这样重写Reentrant类,线程调用outer会在inner方法中的lock.lock中阻塞。
public class Reentrant2{ Lock lock = new Lock(); public outer(){ lock.lock(); inner(); lock.unlock(); } public synchronized inner(){ lock.lock(); //do something lock.unlock(); } }
一个线程调用outer方法会首先锁上Lock实例,然后调用inner方法。在inner方法中线程再一次的想锁住锁实例,这会失败(意思是线程将会被阻塞),因为锁实例已经在outer方法中被锁住了。线程将会被第二次阻塞的原因是它调用了lock方法而没有调用unlock犯法,我们看看这个lock方法的实现之后就很明显了
public class Lock{ boolean isLocked = false; public synchronized void lock() throws InterruptedException{ while(isLocked){ wait(); } isLocked = true; } ... }
注意while循环现在把锁实例也考虑进去了,或者锁被解锁调用线程是锁实例。在while循环中的条件决定是否线程要退出,当前情况下isLocked必须是false才可以,而不管线程是否已经锁住它了。为了让锁类重入,我们需要做一些小小的改变
public class Lock{ boolean isLocked = false; Thread lockedBy = null; int lockedCount = 0; public synchronized void lock() throws InterruptedException{ Thread callingThread = Thread.currentThread(); while(isLocked && lockedBy != callingThread){ wait(); } isLocked = true; lockedCount++; lockedBy = callingThread; } public synchronized void unlock(){ if(Thread.curentThread() == this.lockedBy){ lockedCount--; if(lockedCount == 0){ isLocked = false; notify(); } } } ... }
注意while循环现在把锁实例也考虑进去了,或者锁被解锁调用线程是锁实例
翻译的不好,还有一些没有翻译出来,想看原文的同学可以看下边的链接
原文地址:http://tutorials.jenkov.com/java-concurrency/locks.html
相关文章推荐
- javaweb学习总结(四十七)——监听器(Listener)在开发中的应用
- javaweb学习总结(四十六)——Filter(过滤器)常见应用
- Java 日期处理 Date 、Calendar 和TimeZone类
- javaweb学习总结(四十三)——Filter高级开发
- spring mvc 的jpa JpaRepository数据层 访问方式汇总
- spring AOP
- Java程序在命令行下编译运行打Jar包
- spring4.2.0集成restful和jackson
- javaweb学习总结(三十七)——获得MySQL数据库自动生成的主键
- javaweb学习总结(三十六)——使用JDBC进行批处理
- [Java视频笔记]day06
- javaweb学习总结(三十三)——使用JDBC对数据库进行CRUD
- Java 入门 之 集合 Collection
- Spring中配置和读取多个Properties文件
- Spring/AOP框架, 以及使用注解
- Spring4 mvc+maven 框架搭建(1)
- Eclipse使用快捷键
- Ubuntu下Eclipse中文乱码解决
- Eclipse使用hibernate插件
- 在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法