在 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 提供了更加灵活和高效的锁机制,可以满足不同场景下的需求,而 synchronized 则是 Java 中的关键字,具有语言的内置性。
lock锁,lock锁和synchronized的对比 # Lock锁 JDK5.0后Java提供了一种更加强大的线程同步机制。一种显式定义同步锁对象来实现锁,提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问...
【Java面试题】lock与synchronized区别
### Lock接口与synchronized关键字详解 #### 一、概述 在Java并发编程中,Lock接口与synchronized关键字都是实现同步的重要工具。它们虽然都用于控制多线程对共享资源的访问,但在使用方式、功能特性及灵活性方面...
本文将深入探讨两种主要的锁机制:`synchronized`关键字和`Lock`接口,以及它们各自的特点、应用场景和使用方式。 一、Synchronized `synchronized`是Java中的一个内置关键字,用于提供线程安全。它的主要作用是...
了解synchronized和lock的区别 synchronized是Java语言...在Java中,synchronized和lock都是用于实现线程同步的机制,但它们有不同的实现机制和性能特点。在选择使用哪种机制时,需要考虑到具体的应用场景和性能要求。
synchronized 和 Lock 是 Java 编程中两种常用的同步机制,用于实现线程安全的访问。两者都可以实现同步访问,但是它们有着不同的设计理念和使用场景。 synchronized 的缺陷 synchronized 是 Java 语言内置的...
线程同步Synchronized,监视器monitor和锁lock的关系2---马克-to-win java视频
Java并发编程中,Lock接口和synchronized关键字是两种常见的互斥锁定机制,它们都用于确保多线程环境下的数据一致性。然而,它们在特性和使用上存在显著的区别。 首先,Lock是一个接口,它提供了比synchronized更细...
了解 JVM 锁机制中的 synchronized 和 Lock 实现原理 在 Java 中,锁机制是数据同步的关键,存在两种锁机制:synchronized 和 Lock。了解这两种锁机制的实现原理对于理解 Java 并发编程非常重要。 synchronized 锁...
Lock、Synchronized 和 ReentrantLock 的使用 Lock、Synchronized 和 ReentrantLock 是 Java 中三种常用的同步机制,每种机制都有其特点和使用场景。下面对这三种机制进行详细的分析和比较。 一、Synchronized ...
在 Java 中,synchronized 关键字可以作用于 instance 变量、object reference(对象引用)、static 函数和 class literals(类名称字面常量)身上。 Synchronized 关键字的作用是取得对象的锁,而不是把一段代码或...
在了解Lock和synchronized的区别之前,我们首先需要了解它们的基本概念。Lock是一个接口,它提供了一种机制来保护critical section,使得多个线程在访问共享资源时不会发生冲突。synchronized是Java语言提供的一种...
"Synchronized与Lock"这个主题探讨了两种主要的同步机制:synchronized关键字和Lock接口(包括其实现类如ReentrantLock)。这两种机制都用于实现线程间的互斥访问,但它们在功能、灵活性和性能上有所差异。 首先,...
总的来说,Synchronized和Lock都是Java中解决并发问题的重要工具,它们各有优势,适用于不同的场景。在实际开发中,根据应用的需求和性能考虑,选择合适的同步机制至关重要。理解它们的差异和使用方式,有助于编写出...
本文将深入探讨`synchronized`的几种使用示例,包括方法加锁、代码块加锁(针对`this`和对象)以及静态方法加锁。 1. **方法加锁** 当在实例方法前使用`synchronized`关键字时,它会锁定当前对象的监视器,只有...
除了锁住对象或类,`synchronized`还可以与`wait()`、`notify()`和`notifyAll()`方法结合使用,实现复杂的线程通信和同步。这些方法都是在`Object`类中定义的,只有在持有对象锁的情况下才能调用,否则会抛出`...
1. **使用方式**:`synchronized`是隐式同步,无需手动释放,而`Lock`是显式同步,需手动调用`lock()`和`unlock()`。 2. **控制能力**:`Lock`提供了更多控制选项,如可中断、可选择公平性、锁分离等。 3. **异常...
在深入探讨`synchronized`的关键知识点之前,我们先来明确`synchronized`在Java中的核心作用:它是一种用于实现线程同步的机制,确保了共享资源在多线程环境下的正确访问和修改,避免了数据不一致性和竞态条件等问题...