常见的并发陷阱
volatile
volatile只能强调数据的可见性,并不能保证原子操作和线程安全,因此volatile不是万能的。参考指令重排序
volatile最常见于下面两种场景。
a. 循环检测机制
while( ! done ){
dosomething();
}
b. 单例模型 (http://www.blogjava.net/xylz/archive/2009/12/18/306622.html)
synchronized/Lock
看起来Lock有更好的性能以及更灵活的控制,是否完全可以替换synchronized?
在锁的一些其它问题中说过,synchronized的性能随着JDK版本的升级会越来越高,而Lock优化的空间受限于CPU的性能,很有限。另外JDK内部的工具(线程转储)对synchronized是有一些支持的(方便发现死锁等),而对Lock是没有任何支持的。
也就说简单的逻辑使用synchronized完全没有问题,随着机器的性能的提高,这点开销是可以忽略的。而且从代码结构上讲是更简单的。简单就是美。
对于复杂的逻辑,如果涉及到读写锁、条件变量、更高的吞吐量以及更灵活、动态的用法,那么就可以考虑使用Lock。当然这里尤其需要注意Lock的正确用法。
lock.lock();
try{
//do something
}finally{
lock.unlock();
}
一定要将Lock的释放放入finally块中,否则一旦发生异常或者逻辑跳转,很有可能会导致锁没有释放,从而发生死锁。而且这种死锁是难以排查的。
如果需要synchronized无法做到的尝试锁机制,或者说担心发生死锁无法自恢复,那么使用tryLock()是一个比较明智的选择的。
if(lock.tryLock()){
try{
//do something
}finally{
lock.unlock();
}
}
甚至可以使用获取锁一段时间内超时的机制Lock.tryLock(long,TimeUnit)。 锁的使用可以参考前面文章的描述和建议。
锁的边界
一个流行的错误是这样的。
if(!map.containsKey(key)){
map.put(key,value);
}
看起来很合理的,对于一个线程安全的Map实现,要存取一个不重复的结果,先检测是否存在然后加入。 其实我们知道两个原子操作和在一起的指令序列不代表就是线程安全的。 割裂的多个原子操作放在一起在多线程的情况下就有可能发生错误。
实际上ConcurrentMap提供了putIfAbsent(K, V)的“原子操作”机制,这等价于下面的逻辑:
return map.get(key);
}else{
return map.put(k,v);
}
除了putIfAbsent还有replace(K, V)以及replace(K, V, V)两种机制来完成组合的操作。
提到Map,这里有一篇谈HashMap读写并发的问题。
构造函数启动线程
下面的实例是在构造函数中启动一个线程。
int x,y;
Thread thread;
public Runner(){
this.x=1;
this.y=2;
this.thread=new MyThread();
this.thread.start();
}
}
这里可能存在的陷阱是如果此类被继承,那么启动的线程可能无法正确读取子类的初始化操作。
因此一个简单的原则是,禁止在构造函数中启动线程,可以考虑但是提供一个方法来启动线程。如果非要这么做,最好将类设置为final,禁止继承。
丢失通知的问题
这篇文章里面提到过notify丢失通知的问题。
对于wait/notify/notifyAll以及await/singal/singalAll,如果不确定到底是否能够正确的收到消息,担心丢失通知,简单一点就是总是通知所有。
如果担心只收到一次消息,使用循环一直监听是不错的选择。
非常主用性能的系统,可能就需要区分到底是通知单个还是通知所有的挂起者。
线程数
并不是线程数越多越好,在下一篇文章里面会具体了解下性能和可伸缩性。 简单的说,线程数多少没有一个固定的结论,受限于CPU的内核数,IO的性能以及依赖的服务等等。因此选择一个合适的线程数有助于提高吞吐量。
对于CPU密集型应用,线程数和CPU的内核数一致有助于提高吞吐量,所有CPU都很繁忙,效率就很高。 对于IO密集型应用,线程数受限于IO的性能,某些时候单线程可能比多线程效率更高。但通常情况下适当提高线程数,有利于提高网络IO的效率,因为我们总是认为网络IO的效率比较低。
对于线程池而言,选择合适的线程数以及任务队列是提高线程池效率的手段。
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
对于线程池来说,如果任务总是有积压,那么可以适当提高corePoolSize大小;如果机器负载较低,那么可以适当提高maximumPoolSize的大小;任务队列不长的情况下减小keepAliveTime的时间有助于降低负载;另外任务队列的长度以及任务队列的拒绝策略也会对任务的处理有一些影响.
相关推荐
10. **并发编程最佳实践**:提供实用的编程建议,避免常见并发陷阱,如死锁、活锁和饥饿状态,以及如何编写可读、可维护的并发代码。 这本书结合理论与实例,通过一系列的代码示例来展示如何在Java 7中高效、安全地...
### Java常见疑惑与陷阱 #### 一、Java基础的常见陷阱 **1.1 不一样的数字的宽类型和窄类型** 在Java中,当我们处理不同的数值类型时,可能会遇到一些意料之外的行为。例如,当一个`byte`类型的数据与一个`int`...
本文将深入探讨Java基础陷阱、Java客户端陷阱以及Java服务器陷阱,并提供一些常见的面试题,帮助你更好地理解和应对这些问题。 一、Java基础陷阱 1. 内存管理:Java使用垃圾回收机制管理内存,但过度依赖可能导致...
9. **并发编程最佳实践**:最后,书中会提供一些并发编程的最佳实践,帮助读者避免常见的并发陷阱,写出更健壮、更高效的代码。 通过阅读《Java并发编程的艺术》这本书,开发者不仅可以掌握Java并发编程的基础知识...
为了帮助开发者避免常见的并发陷阱,《Java并发编程实战》还提供了一系列的最佳实践建议,比如: - **正确使用同步机制**:强调了在使用`synchronized`关键字或`ReentrantLock`时需要注意的问题。 - **异常处理**:...
通过阅读这些书籍,开发者可以深入理解Java并发编程的底层机制,掌握有效的并发编程技巧,提高程序的运行效率,同时避免并发编程中常见的陷阱和问题。无论是初学者还是经验丰富的开发者,都能从中受益匪浅。
本篇文章将深入探讨一些常见的C语言陷阱和漏洞,并提供避免它们的策略。 1. **类型转换陷阱**:C语言对类型转换的处理比较宽松,不自动进行类型提升可能导致数据丢失或溢出。例如,将大整数赋值给小整数类型时,高...
- **最佳实践**:结合具体案例分享Java并发编程的最佳实践,涵盖设计模式的应用、常见陷阱规避等方面。 ### 实战案例 - **多线程下载**:利用多线程技术实现文件的分块下载与合并,展示并发技术在提高下载速度方面...
5. 《Java多线程编程实战指南 设计模式篇》:此书特别强调设计模式在并发编程中的应用,如单例模式、工厂模式在多线程环境下的实现,以及如何使用模式来解决并发场景下的常见问题。 通过阅读这些书籍,开发者可以...
通过阅读这本书,你将学习如何避免常见的并发陷阱,如死锁、活锁和饥饿,以及如何进行有效的并发调试和性能优化。此外,书中还可能涉及Java内存模型、线程通信(如wait/notify机制)以及并发设计模式等高级主题。总...
高并发编程第三阶段21讲 Exchanger工具的使用以及常见问题分析-上_.mp4 高并发编程第三阶段22讲 Exchanger工具的使用以及常见问题分析-下_.mp4 高并发编程第三阶段23讲 Semaphore工具的介绍以及借助于...
在使用的过程中,开发者可能会遇到一些误区和常见陷阱。例如,过度依赖同步或者错误使用锁可能会导致性能问题或者死锁。J.U.C的文档和源代码中也包含了许多设计和使用上的原则,这些原则旨在帮助开发者避免常见问题...
通过分析常见的并发问题和解决方案,读者可以学习到如何在实际项目中应用并发编程技巧,避免常见的陷阱和错误。 总之,《Java并发编程实践》是一本全面、深入的指南,旨在帮助Java开发者掌握并发编程的核心概念和...
7. **并发编程最佳实践**:书中会讨论如何避免并发编程中的常见陷阱,比如避免过度同步、正确处理中断、避免死锁和饥饿等问题。此外,还会讲解如何进行并发性能调优,包括使用JVM的监控和调试工具。 8. **原子操作...
本教程将深入探讨Java并发编程的核心概念、最佳实践以及常见陷阱。 首先,我们要了解Java中的线程。线程是操作系统分配CPU时间的基本单元,Java通过Thread类来抽象线程。创建线程有两种方式:继承Thread类并重写run...
《JAVA并发编程实践》是一本深入探讨Java多线程与并发控制的权威著作。这本书针对Java程序员,旨在帮助他们理解和掌握如何在并发...在实际工作中,这本书的知识将帮助我们避免常见的并发陷阱,提升系统的稳定性和性能。
《Java并发编程:设计原则与模式(第二版)》是一本深入探讨Java平台上的多线程和并发编程的著作。...书中的实例和实战经验将帮助读者在实际开发中避免常见的并发陷阱,编写出更加稳定、高效的并发程序。
Java并发工具包(J.U.C)是Java编程语言中用于...这需要开发者具备一定的并发理论知识,并结合实践经验来避免常见的并发陷阱。对于J.U.C的深入研究和实践,不仅能够提升Java编程能力,也会对编程思想有更深层次的理解。
本书详细介绍了如何在Java环境中有效地设计和实现并发程序,帮助开发者避免常见的陷阱和错误。 首先,书中详细讲解了Java并发的基础概念,包括线程的创建、生命周期管理以及线程间通信。Java提供了多种方式来创建...
这两本书结合阅读,可以帮助Java开发者从理论到实践全面掌握并发编程,避免常见的陷阱,提升多线程应用程序的效率和可靠性。无论是对并发编程有初步了解的新手,还是希望深化理解的老手,都能从中受益匪浅。通过阅读...