首先需要强调的一点是:所有锁(包括内置锁和高级锁)都是有性能消耗的,也就是说在高并发的情况下,由于锁机制带来的上下文切换、资源同步等消耗是非常可观的。在某些极端情况下,线程在锁上的消耗可能比线程本身的消耗还要多。所以如果可能的话,在任何情况下都尽量少用锁,如果不可避免那么采用非阻塞算法是一个不错的解决方案,但是却也不是绝对的。
内部锁
Java语言通过synchronized关键字来保证原子性。这是因为每一个Object都有一个隐含的锁,这个也称作监视器对象。在进入synchronized之前自动获取此内部锁,而一旦离开此方式(不管通过和中方式离开此方法)都会自动释放锁。显然这是一个独占锁,每个锁请求之间是互斥的。相对于前面介绍的众多高级锁(Lock/ReadWriteLock等),synchronized的代价都比后者要高。但是synchronized的语法比较简单,而且也比较容易使用和理解,不容易写法上的错误。而我们知道Lock一旦调用了lock()方法获取到锁而未正确释放的话很有可能就死锁了。所以Lock的释放操作总是跟在finally代码块里面,这在代码结构上也是一次调整和冗余。另外前面介绍中说过Lock的实现已经将硬件资源用到了极致,所以未来可优化的空间不大,除非硬件有了更高的性能。但是synchronized只是规范的一种实现,这在不同的平台不同的硬件还有很高的提升空间,未来Java在锁上的优化也会主要在这上面。
性能
由于锁总是带了性能影响,所以是否使用锁和使用锁的场合就变得尤为重要。如果在一个高并发的Web请求中使用了强制的独占锁,那么就可以发现Web的吞吐量将急剧下降。
为了利用并发来提高性能,出发点就是:更有效的利用现有的资源,同时让程序尽可能的开拓更多可用的资源。这意味着机器尽可能的处于忙碌的状态,通常意义是说CPU忙于计算,而不是等待。当然CPU要做有用的事情,而不是进行无谓的循环。当然在实践中通常会预留一些资源出来以便应急特殊情况,这在以后的线程池并发中可以看到很多例子。
线程阻塞
锁机制的实现通常需要操作系统提供支持,显然这会增加开销。当锁竞争的时候,失败的线程必然会发生阻塞。JVM既能自旋等待(不断尝试,知道成功,很多CAS就是这样实现的),也能够在操作系统中挂起阻塞的线程,直到超时或者被唤醒。通常情况下这取决于上下文切换的开销以及与获取锁需要等待的时间二者之间的关系。自旋等待适合于比较短的等待,而挂起线程比较适合那些比较耗时的等待。
挂起一个线程可能是因为无法获取到锁,或者需要某个特定的条件,或者耗时的I/O操作。挂起一个线程需要两次额外的上下文切换以及操作系统、缓存等多资源的配合:如果线程被提前换出,那么一旦拿到锁或者条件满足,那么又需要将线程换回执行队列,这对线程而言,两次上下文切换可能比较耗时。
锁竞争
影响锁竞争性的条件有两个:锁被请求的频率和每次持有锁的时间。显然当而这二者都很小的时候,锁竞争不会成为主要的瓶颈。但是如果锁使用不当,导致二者都比较大,那么很有可能CPU不能有效的处理任务,任务被大量堆积。
所以减少锁竞争的方式有下面三种:
- 减少锁持有的时间
- 减少锁请求的频率
- 采用共享锁取代独占锁
死锁
如果一个线程永远不释放另外一个线程需要的资源那么就会导致死锁。这有两种情况:一种情况是线程A永远不释放锁,结果B一直拿不到锁,所以线程B就“死掉”了;第二种情况下,线程A拥有线程B需要的锁Y,同时线程B拥有线程A需要的锁X,那么这时候线程A/B互相依赖对方释放锁,于是二者都“死掉”了。
还有一种情况为发生死锁,如果一个线程总是不能被调度,那么等待此线程结果的线程可能就死锁了。这种情况叫做线程饥饿死锁。比如说在前面介绍的非公平锁中,如果某些线程非常活跃,在高并发情况下这类线程可能总是拿到锁,那么那些活跃度低的线程可能就一直拿不到锁,这样就发生了“饥饿死”。
避免死锁的解决方案是:尽可能的按照锁的使用规范请求锁,另外锁的请求粒度要小(不要在不需要锁的地方占用锁,锁不用了尽快释放);在高级锁里面总是使用tryLock或者定时机制(这个以后会讲,就是指定获取锁超时的时间,如果时间到了还没有获取到锁那么就放弃)。高级锁(Lock)里面的这两种方式可以有效的避免死锁。
活锁
活锁描述的是线程总是尝试某项操作却总是失败的情况。这种情况下尽管线程没有被阻塞,但是人物却总是不能被执行。比如在一个死循环里面总是尝试做某件事,结果却总是失败,现在线程将永远不能跳出这个循环。另外一种情况是在一个队列中每次从队列头取出一个任务来执行,每次都失败,然后将任务放入队列头,接下来再一次从队列头取出任务执行,仍然失败。
还有一种活锁方式发生在“碰撞协让”情况下:两个人过独木桥,如果在半路相撞,双方礼貌退出去然后再试一次。如果总是失败,那么这两个任务将一直无法得到执行。
总之解决锁问题的关键就是:从简单的开始,先保证正确,然后再开始优化。
相关推荐
在Java编程中,多线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而提高了系统的效率和响应性。然而,在某些场景下,我们可能需要控制线程的执行顺序,确保它们按照特定的顺序交替运行,这在并发编程...
Java 多线程主题1- Java 多线程启动线程2- Java 多线程Volatile – 基本线程通信3- Java 多线程同步4- Java 多线程锁对象5- Java 多线程线程池6- Java 多线程倒计时闩锁7- Java 多线程生产者-消费者8- Java 多线程...
Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 Java线程:线程的调度-让步 Java线程:线程的调度-合并 Java线程:线程的调度-守护线程 Java线程:线程的...
本资料"JAVA-JavaThreadProgramming-Sams.zip"提供了一套详细的教程,旨在深入讲解Java中的多线程设计和实现。 Java多线程允许程序同时执行多个任务,这极大地提高了程序的效率和响应性。在Java中,线程可以分为两...
Java-JUC-多线程进阶resources是 Java 并发编程的高级课程,涵盖了 Java 中的并发编程概念、线程安全、锁机制、集合类、线程池、函数式接口、Stream流式计算等多个方面。 什么是JUC JUC(Java Utilities for ...
Java多线程编程是Java开发中的重要组成部分,它允许程序同时执行多个任务,从而提高了CPU资源的利用率。本文档详细介绍了Java多线程的相关知识,包括概念、原理、创建、同步、调度以及新特性。 首先,理解多线程的...
Java 多线程之并发锁 Java 中的多线程编程是指在一个程序中同时运行多个线程,以提高程序的执行效率和响应速度。在多线程编程中,线程间的同步是非常重要的,因为不同的线程可能会同时访问同一个共享资源,导致数据...
二、Java锁机制 1. **内置锁(显式锁)** - ReentrantLock(可重入锁):提供与synchronized相似的功能,但更灵活,支持公平锁、非公平锁,以及可中断和定时尝试获取锁。 - ReadWriteLock(读写锁):...
《狂神说Java-多线程课程全部代码》是一个涵盖了Java多线程和并发编程的实战教程资源。这个压缩包包含了一系列的示例代码(如demo01),旨在帮助开发者深入理解和掌握Java中的多线程技术及其在并发环境中的应用。 ...
总之,Java线程是并发编程的核心,理解并熟练掌握线程的创建、同步、通信、异常处理和线程池等概念,对于编写高效、稳定的多线程程序至关重要。在实际开发中,应根据需求选择合适的方式和策略,确保线程安全和程序...
计算机后端-Java-Java核心基础-第20章 多线程 13. Lock锁方式解决线程安全问题.avi
### JAVA编程高级-多线程编程 #### 一、多线程简介 多线程编程是一种软件技术,它允许在单个程序内并发执行多个控制流。这种技术极大地提高了程序的执行效率和响应能力,特别是在现代多核处理器环境中。本文档主要...
### JAVA-基础多线程 #### 一、线程概念 1. **线程定义**: - 线程是程序执行时的一条路径,是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。 - 一个线程指的是进程中一个...
Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,提高了系统的效率和响应性。在Java中,实现多线程有两种主要方式:继承`Thread`类和实现`Runnable`接口。本资料“java-Thread-study-summary.zip”...
在多线程环境下,可能会出现数据竞争问题,为了解决这个问题,Java提供了多种同步机制,如synchronized关键字、wait/notify机制、Lock锁(ReentrantLock)等。synchronized用于控制对共享资源的访问,而wait/notify...
在进行Java多线程编程时,要遵循良好的编程实践,如避免长时间持有锁、合理设置线程优先级、使用并发集合(如ConcurrentHashMap、CopyOnWriteArrayList等)以减少同步开销,以及及时关闭不再使用的线程池。...
Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,极大地提升了程序的效率和性能。在Java中,实现多线程有两种主要方式:通过实现Runnable接口或者继承Thread类。本案例将深入探讨Java多线程中的关键...
《Java多线程编程实战指南-核心篇》是一本深入探讨Java并发编程的书籍,旨在帮助读者掌握在Java环境中创建、管理和同步线程的核心技术。Java的多线程能力是其强大之处,使得开发者能够在同一时间执行多个任务,提高...
《Java-jdk10-最新最全多线程编程实战指南-核心篇》是一本深入探讨Java多线程编程的专著,针对Java 10版本进行了全面的更新和优化。这本书聚焦于Java多线程的核心概念和技术,旨在帮助开发者理解和掌握如何在并发...
在Java多线程编程中,线程间的通信是非常重要的概念,用于协调多个并发执行的任务。线程的状态转换是理解线程通信的基础,主要包括四个状态:新(New)、可执行(Runnable)、死亡(Dead)和停滞(Blocked)。新状态...