在 Java 中线程的状态可以分为:新建(New),运行状态(Runnable)、阻塞状态(Blocked)、等待状态(Waiting)、结束状态(Terminated)。运行状态可以转为 阻塞状态或等待状态。
在接收完基本概念后,我们看看显示锁(Lock)和内部锁(synchronized)有什么不同。
- Lock 支持更细粒度的同步控制
- Lock是无阻塞锁,synchronized 是阻塞锁。当线程A持有锁S,如果线程B也期望获取锁S,如果是显示锁则线程B进入等待状态,如果是内部锁则线程B进入阻塞状态
- Lock可以实现公平锁(默认是非公平锁,即使对公平锁而言,可轮询的tryLock仍然会插队),synchronized 只能是非公平锁
- Lock 是代码级的,synchronized 是JVM级的。
ReentrantLock实现了Lock接口,并提供了与Synchronized相同的互斥性和可见性。在获取ReentrantLock时,有着与进入同步代码块相同的内存语义,在释放ReentrantLock时同样有着与退出同步代码块相同的含义。此外还提供了一些其他的功能,包括定时的锁等待,可中断的锁等待等。
与显示锁相比,内置锁容然具有很大的优势(性能在JAVA 6.0 后与显示锁旗鼓相当)。内置锁为大家所熟悉,代码简洁紧凑。所以在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列以及非块锁。否则,还是优先考虑使用内置锁。因为 synchronized 是JVM的内置属性,在未来优化的可能性更高。
import java.util.Calendar; import java.util.concurrent.TimeUnit; /** * * @author zhangwei_david * @version $Id: Task.java, v 0.1 2014年10月23日 下午10:26:05 zhangwei_david Exp $ */ public class Task { public void doSomething() { try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { } StringBuffer strBuffer = new StringBuffer(); strBuffer.append("线程名称:" + Thread.currentThread().getName()); strBuffer.append(", 执行时间 :" + Calendar.getInstance().get(13) + "s"); System.out.println(strBuffer.toString()); } } import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * * @author zhangwei_david * @version $Id: TaskWithLock.java, v 0.1 2014年10月23日 下午10:29:47 zhangwei_david Exp $ */ public class TaskWithLock extends Task implements Runnable { Lock lock = new ReentrantLock(); /** * @see java.lang.Runnable#run() */ public void run() { try { lock.lock(); doSomething(); } finally { lock.unlock(); } } } /** * * @author zhangwei_david * @version $Id: TaskWithSync.java, v 0.1 2014年10月23日 下午10:31:33 zhangwei_david Exp $ */ public class TaskWithSync extends Task implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { synchronized ("A") { doSomething(); } } } import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * * @author zhangwei_david * @version $Id: TaskTest.java, v 0.1 2014年10月23日 下午10:32:07 zhangwei_david Exp $ */ public class TaskTest { public static void runTask(Class<? extends Runnable> clz) throws InterruptedException, InstantiationException, IllegalAccessException { ExecutorService es = Executors.newCachedThreadPool(); System.out.println("*** 开始执行 " + clz.getSimpleName() + " 任务****"); for (int i = 0; i < 3; i++) { es.submit(clz.newInstance()); } TimeUnit.SECONDS.sleep(10); System.out.println("---------" + clz.getSimpleName() + "-----------任务执行完毕!\n\n"); es.shutdown(); } /** * * @param args * @throws Exception * @throws * @throws */ public static void main(String[] args) throws Exception { runTask(TaskWithLock.class); runTask(TaskWithSync.class); } }
运行的结果是:
*** 开始执行 TaskWithLock 任务**** 线程名称:pool-1-thread-3, 执行时间 :24s 线程名称:pool-1-thread-2, 执行时间 :24s 线程名称:pool-1-thread-1, 执行时间 :24s ---------TaskWithLock-----------任务执行完毕! *** 开始执行 TaskWithSync 任务**** 线程名称:pool-2-thread-1, 执行时间 :34s 线程名称:pool-2-thread-3, 执行时间 :36s 线程名称:pool-2-thread-2, 执行时间 :38s ---------TaskWithSync-----------任务执行完毕!
从结果中可以发现Lock 没有生效,到底是什么呢?因为Lock 是代码级的锁,属于一个对象,定义一个私有的锁是不会起到同步的作用的。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * * @author zhangwei_david * @version $Id: TaskWithLock.java, v 0.1 2014年10月23日 下午10:29:47 zhangwei_david Exp $ */ public class TaskWithLock extends Task implements Runnable { private static final TaskWithLock instance = new TaskWithLock(); /** * Getter method for property <tt>instance</tt>. * * @return property value of instance */ public static TaskWithLock getInstance() { return instance; } Lock lock = new ReentrantLock(); /** * @see java.lang.Runnable#run() */ public void run() { try { lock.lock(); doSomething(); } finally { lock.unlock(); } } } /** * * @author zhangwei_david * @version $Id: TaskWithSync.java, v 0.1 2014年10月23日 下午10:31:33 zhangwei_david Exp $ */ public class TaskWithSync extends Task implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { synchronized ("A") { doSomething(); } } } import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * * @author zhangwei_david * @version $Id: TaskTest.java, v 0.1 2014年10月23日 下午10:32:07 zhangwei_david Exp $ */ public class TaskTest { public static void runTask(Runnable task) throws InterruptedException, InstantiationException, IllegalAccessException { ExecutorService es = Executors.newCachedThreadPool(); System.out.println("*** 开始执行 " + task.getClass().getSimpleName() + " 任务****"); for (int i = 0; i < 3; i++) { es.submit(task); } TimeUnit.SECONDS.sleep(10); System.out .println("---------" + task.getClass().getSimpleName() + "-----------任务执行完毕!\n\n"); es.shutdown(); } /** * * @param args * @throws Exception * @throws * @throws */ public static void main(String[] args) throws Exception { runTask(TaskWithLock.getInstance()); runTask(new TaskWithSync()); } }
结果是:
*** 开始执行 TaskWithLock 任务**** 线程名称:pool-1-thread-2, 执行时间 :41s 线程名称:pool-1-thread-1, 执行时间 :43s 线程名称:pool-1-thread-3, 执行时间 :45s ---------TaskWithLock-----------任务执行完毕! *** 开始执行 TaskWithSync 任务**** 线程名称:pool-2-thread-2, 执行时间 :51s 线程名称:pool-2-thread-3, 执行时间 :53s 线程名称:pool-2-thread-1, 执行时间 :55s ---------TaskWithSync-----------任务执行完毕!
可以发现只有锁本身是共享的时候才可以进行代码的同步控制。
如果将Lock改成这样:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * * @author zhangwei_david * @version $Id: TaskWithLock.java, v 0.1 2014年10月23日 下午10:29:47 zhangwei_david Exp $ */ public class TaskWithLock extends Task implements Runnable { private static final TaskWithLock instance = new TaskWithLock(); /** * Getter method for property <tt>instance</tt>. * * @return property value of instance */ public static TaskWithLock getInstance() { return instance; } Lock lock = new ReentrantLock(true); /** * @see java.lang.Runnable#run() */ public void run() { try { lock.lock(); doSomething(); } finally { lock.unlock(); } } }
运行的结果是:
*** 开始执行 TaskWithLock 任务**** 线程名称:pool-1-thread-1, 执行时间 :34s 线程名称:pool-1-thread-2, 执行时间 :36s 线程名称:pool-1-thread-3, 执行时间 :38s ---------TaskWithLock-----------任务执行完毕! *** 开始执行 TaskWithSync 任务**** 线程名称:pool-2-thread-1, 执行时间 :44s 线程名称:pool-2-thread-3, 执行时间 :46s 线程名称:pool-2-thread-2, 执行时间 :48s ---------TaskWithSync-----------任务执行完毕!
我们可以发现Lock的锁是公平的,而synchronized 是非公平的。(公平是指JVM优先选择等待时间最长的线程持有锁,非公平是指随机选择)
在激烈竞争的情况下,非公平锁的性能远远高于公平锁,原因是:恢复一个被挂起的线程与该线程真正开始运行之间存在着验证的延迟。 如: 线程B请求线程A持有的锁,由于锁已经被A持有B被挂起,A释放锁,C请求锁;如果是非公平的锁,此时C可以持有锁,线程B被唤醒,B唤醒时C已经释放锁,B正好可以持有锁。公平锁,就必须等待B释放,C 才可以获取锁。
Synchronized 重入(Reentrant)
当某个线程请求一个由其他线程持有的锁时,发出的请求的线程就会阻塞。然而,由于内置锁(Intrinsic Lock)是可重入的,因此如果该线程试图获取一个已经由他持有锁,那么这个请求就会成功。重入意味着获取锁的操作粒度是一个线程而不是调用。
/** * * @author zhangwei_david * @version $Id: Parent.java, v 0.1 2015年2月4日 下午5:28:43 zhangwei_david Exp $ */ public class Parent { public synchronized void doParent() { System.out.println("父类获取锁,进入同步代码块"); } }
/** * * @author zhangwei_david * @version $Id: Child.java, v 0.1 2015年2月4日 下午5:30:11 zhangwei_david Exp $ */ public class Child extends Parent { public synchronized void doChild() { System.out.println("子类获取锁,进入同步代码块"); super.doParent(); } }
测试类:
/** * * @author zhangwei_david * @version $Id: Client.java, v 0.1 2015年2月4日 下午5:31:31 zhangwei_david Exp $ */ public class Client { public static void main(String[] args) { Child child = new Child(); child.doChild(); } }
结果是:
子类获取锁,进入同步代码块 父类获取锁,进入同步代码块
相关推荐
基础理论知识,描述lock和synchronized的区别和基础的理论,其中还有死锁等基础概念。
lock锁,lock锁和synchronized的对比 # Lock锁 JDK5.0后Java提供了一种更加强大的线程同步机制。一种显式定义同步锁对象来实现锁,提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问...
【Java面试题】lock与synchronized区别
并发编程中,锁是经常需要用到的,今天我们一起来看下Java中的锁机制:synchronized和lock。 Synchronized 和 Lock的概念 Synchronized 是Java 并发编程中很重要的关键字,另外一个很重要的是 volatile。Syncronized...
而Lock接口则是一个更底层的同步机制,它提供了更丰富的功能,但需要显式地获取和释放锁,通常通过实现类如ReentrantLock来使用。 其次,从功能特性上来看,synchronized具有可重入性,即同一个线程可以多次获取同...
目录synchronized的缺陷Lock和ReentrantLock常用方法ReadWriteLock和ReentrantReadWriteLockLock和synchronized区别synchronized锁升级公平锁和非公平锁 synchronized的缺陷 众所周知,synchronized锁是JAVA的关键字...
线程同步Synchronized,监视器monitor和锁lock的关系2---马克-to-win java视频
下面小编就为大家带来一篇详谈Lock与synchronized 的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
java lock synchronized
主要介绍了简单了解synchronized和lock的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
synchronized和LOCK的实现原理深入JVM锁机制比较好.docx
线程同步Synchronized,监视器monitor和锁lock的关系2---马克-to-win java视频
互联网信息泛滥环境下少有的良心之作!如果您想对Java编程synchronized与lock的区别有所了解,这篇文章绝对值得!分享给大家,供需要的朋友参考。不说了,我先学习去了。
本篇文章是对Synchronized和java.util.concurrent.locks.Lock的区别进行了详细的分析介绍,需要的朋友参考下
在jdk1.5以后,JAVA提供了Lock类来实现和synchronized一样的功能,并且还提供了Condition来显示线程间通信。 Lock类是Java类来提供的功能,丰富的api使得Lock类的同步功能比synchronized的同步更强大。本文章的所有...
如果 再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。 无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而...
线程同步Synchronized,监视器monitor和锁lock的关系1---马克-to-win java视频
主要介绍了Java synchronized关键字和Lock接口实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
主要介绍了使用synchronized实现一个Lock代码详解,具有一定借鉴价值,需要的朋友可以参考下。
同样是锁,先说说synchronized和lock的区别: 1.synchronized是java关键字,是用c++实现的;而lock是用java类,用java可以实现 2.synchronized可以锁住代码块,对象和类,但是线程从开始获取锁之后开发者不能进行...