`
blueyanghualong
  • 浏览: 227292 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java多线程应用

 
阅读更多

大家使用多线程无非是为了提高性能,但如果多线程使用不当,不但性能提升不明显,而且会使得资源消耗更大。下面列举一下可能会造成多线程性能问题的点:

  • 死锁
  • 过多串行化
  • 过多锁竞争
  • 切换上下文
  • 内存同步

下面分别解析以上性能隐患

死锁

关于死锁,我们在学习操作系统的时候就知道它产生的原因和危害,这里就不从原理上去累述了,可以从下面的代码和图示重温一下死锁产生的原因:

 

  1. public class LeftRightDeadlock {  
  2.     private final Object left = new Object();  
  3.     private final Object right = new Object();  
  4.     public void leftRight() {  
  5.         synchronized (left) {  
  6.             synchronized (right) {  
  7.                 doSomething();  
  8.             }  
  9.         }  
  10.     }  
  11.     public void rightLeft() {  
  12.         synchronized (right) {  
  13.             synchronized (left) {  
  14.                 doSomethingElse();  
  15.             }  
  16.         }  
  17.     }  
  18. }  

 

预防和处理死锁的方法:

1)尽量不要在释放锁之前竞争其他锁

一般可以通过细化同步方法来实现,只在真正需要保护共享资源的地方去拿锁,并尽快释放锁,这样可以有效降低在同步方法里调用其他同步方法的情况

2)顺序索取锁资源

如果实在无法避免嵌套索取锁资源,则需要制定一个索取锁资源的策略,先规划好有哪些锁,然后各个线程按照一个顺序去索取,不要出现上面那个例子中不同顺序,这样就会有潜在的死锁问题

3)尝试定时锁

Java 5提供了更灵活的锁工具,可以显式地索取和释放锁。那么在索取锁的时候可以设定一个超时时间,如果超过这个时间还没索取到锁,则不会继续堵塞而是放弃此次任务,示例代码如下:

 

  1. public boolean trySendOnSharedLine(String message,  
  2.                                    long timeout, TimeUnit unit)  
  3.                                    throws InterruptedException {  
  4.     long nanosToLock = unit.toNanos(timeout)  
  5.                      - estimatedNanosToSend(message);  
  6.     if (!lock.tryLock(nanosToLock, NANOSECONDS))  
  7.         return false;  
  8.     try {  
  9.         return sendOnSharedLine(message);  
  10.     } finally {  
  11.         lock.unlock();  
  12.     }  
  13. }  

 

这样可以有效打破死锁条件。

4)检查死锁

JVM采用thread dump的方式来识别死锁的方式,可以通过操作系统的命令来向JVM发送thread dump的信号,这样可以查询哪些线程死锁。

过多串行化

用多线程实际上就是想并行地做事情,但这些事情由于某些依赖性必须串行工作,导致很多环节得串行化,这实际上很局限系统的可扩展性,就算加CPU加线程,但性能却没有线性增长。有个Amdahl定理可以说明这个问题:

其中,F是串行化比例,N是处理器数量,由上可知,只有尽可能减少串行化,才能最大化地提高可扩展能力。降低串行化的关键就是降低锁竞争,当很多并行任务挂在锁的获取上,就是串行化的表现

过多锁竞争

过多锁竞争的危害是不言而喻的,那么看看有哪些办法来降低锁竞争

1)缩小锁的范围

前面也谈到这一点,尽量缩小锁保护的范围,快进快出,因此尽量不要直接在方法上使用synchronized关键字,而只是在真正需要线程安全保护的地方使用

2)减小锁的粒度

Java 5提供了显式锁后,可以更为灵活的来保护共享变量。synchronized关键字(用在方法上)是默认把整个对象作为锁,实际上很多时候没有必要用这么大一个锁,这会导致这个类所有synchronized都得串行执行。可以根据真正需要保护的共享变量作为锁,也可以使用更为精细的策略,目的就是要在真正需要串行的时候串行,举一个例子:

 

  1. public class StripedMap {  
  2.     // Synchronization policy: buckets[n] guarded by locks[n%N_LOCKS]  
  3.     private static final int N_LOCKS = 16;  
  4.     private final Node[] buckets;  
  5.     private final Object[] locks;  
  6.     private static class Node { ... }  
  7.     public StripedMap(int numBuckets) {  
  8.         buckets = new Node[numBuckets];  
  9.         locks = new Object[N_LOCKS];  
  10.         for (int i = 0; i < N_LOCKS; i++)  
  11.             locks[i] = new Object();  
  12.     }  
  13.     private final int hash(Object key) {  
  14.         return Math.abs(key.hashCode() % buckets.length);  
  15.     }  
  16.     public Object get(Object key) {  
  17.         int hash = hash(key);  
  18.         synchronized (locks[hash % N_LOCKS]) {  
  19.             for (Node m = buckets[hash]; m != null; m = m.next)  
  20.                 if (m.key.equals(key))  
  21.                     return m.value;  
  22.         }  
  23.         return null;  
  24.     }  
  25.     public void clear() {  
  26.         for (int i = 0; i < buckets.length; i++) {  
  27.             synchronized (locks[i % N_LOCKS]) {  
  28.                 buckets[i] = null;  
  29.             }  
  30.         }  
  31.     }  
  32.     ...  
  33. }  

 

上面这个例子是通过hash算法来把存取的值所对应的hash值来作为锁,这样就只需要对hash值相同的对象存取串行化,而不是像HashTable那样对任何对象任何操作都串行化。

3)减少共享资源的依赖

共享资源是竞争锁的源头,在多线程开发中尽量减少对共享资源的依赖,比如对象池的技术应该慎重考虑,新的JVM对新建对象以做了足够的优化,性能非常好,如果用对象池不但不能提高多少性能,反而会因为锁竞争导致降低线程的可并发性。

4)使用读写分离锁来替换独占锁

Java 5提供了一个读写分离锁(ReadWriteLock)来实现读-读并发,读-写串行,写-写串行的特性。这种方式更进一步提高了可并发性,因为有些场景大部分是读操作,因此没必要串行工作。关于ReadWriteLock的具体使用可以参加一下示例:

 

  1. public class ReadWriteMap<K,V> {  
  2.     private final Map<K,V> map;  
  3.     private final ReadWriteLock lock = new ReentrantReadWriteLock();  
  4.     private final Lock r = lock.readLock();  
  5.     private final Lock w = lock.writeLock();  
  6.     public ReadWriteMap(Map<K,V> map) {  
  7.         this.map = map;  
  8.     }  
  9.     public V put(K key, V value) {  
  10.         w.lock();  
  11.         try {  
  12.             return map.put(key, value);  
  13.         } finally {  
  14.             w.unlock();  
  15.         }  
  16.     }  
  17.     // Do the same for remove(), putAll(), clear()  
  18.     public V get(Object key) {  
  19.         r.lock();  
  20.         try {  
  21.             return map.get(key);  
  22.         } finally {  
  23.             r.unlock();  
  24.         }  
  25.     }  
  26.     // Do the same for other read-only Map methods  
  27. }  

 

切换上下文

线程比较多的时候,操作系统切换线程上下文的性能消耗是不能忽略的,在构建高性能web之路------web服务器长连接 可以看出在进程切换上的代价,当然线程会更轻量一些,不过道理是类似的

内存同步

当使用到synchronized、volatile或Lock的时候,都会为了保证可见性导致更多的内存同步,这就无法享受到JMM结构带来了性能优化。

 

分享到:
评论

相关推荐

    Java多线程应用

    Java多线程应用是Java编程中的重要组成部分,它允许程序同时执行多个任务,提升系统效率。在现代软件开发中,特别是在服务器端应用和高并发场景下,熟练掌握Java多线程技术至关重要。以下是对给定文件中涉及的知识点...

    java多线程应用实现示例

    总结,这个"java多线程应用实现示例"通过小球运动的模拟,深入浅出地展示了Java多线程的运用,包括线程创建、同步、通信和线程池管理等关键概念。学习这个示例有助于理解和掌握Java多线程编程,为实际项目开发中的...

    基于线程池的Java多线程应用技术.pdf

    基于线程池的Java多线程应用技术 概述: Java多线程机制是Java编程语言中的一种重要机制,它通过多线程技术提高了任务的执行效率。线程池技术是Java多线程机制中的一个重要组成部分,它能够有效地满足同类的、独立...

    java多线程应用实现方法

    Java多线程应用实现主要涉及两种方式:继承Thread类和实现Runnable接口。这两种方法都是为了在Java程序中创建并管理多个并发执行的任务,从而提高程序的执行效率。 1. 继承Thread类: 当一个类直接继承Thread类时,...

    java 多线程应用

    使用多线程时遇到的问题,并提出那些常见陷阱的解决方案。

    Java多线程应用练习源代码及相关说明资料

    这个压缩包包含的"Java多线程应用练习源代码及相关说明资料"是一份宝贵的资源,旨在帮助学习者深入理解并实践Java的并发编程。 1. **Java多线程基础** - **线程的概念**:线程是程序执行的最小单位,一个进程可以...

    java多线程经典案例

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,极大地提升了程序的效率和性能。在Java中,实现多线程有两种主要方式:通过实现Runnable接口或者继承Thread类。本案例将深入探讨Java多线程中的关键...

    第2章-Java多线程应用ppt课件(全).ppt

    Java多线程应用ppt课件 本课件主要讲解了Java多线程应用的基本概念、线程的生命周期、多线程同步机制等重要知识点。 首先,课件讲解了线程的基本概念。线程是计算机正在执行的程序中的一个控制流程。线程本身不是...

    你的第一个java多线程应用

    使用java多线程编写的一个java类,从这个类中懂得贡献资源。

    java多线程应用实战

    多线程在实际开发中有着举足轻重的作用,但在教科书中并没有学到,本文以mysql(支持任何数据库)插入数据和查询数据为例子,讲述了如何在几秒钟内疯狂插入几十万数据,在一个方法中如何同时运行多条SQL语句(效率比...

    JAVA多线程应用——时钟

    很好的JAVA多线程实例,方便初学都学习。

    详细剖析JAVA多线程案例教学

    #### 三、Java多线程应用案例 ##### 1. 实现生产者消费者模式 生产者消费者模式是一种经典的多线程编程模型,用于解决多线程之间的数据传递问题。在这个模式中,生产者负责生成数据,消费者负责消费数据。Java中可以...

    java 多线程操作数据库

    ### Java多线程操作数据库:深入解析与应用 在当今高度并发的应用环境中,Java多线程技术被广泛应用于处理数据库操作,以提升系统的响应速度和处理能力。本文将基于一个具体的Java多线程操作数据库的应用程序,深入...

    Java多线程与并发库高级应用

    并发库高级应用\多线程\Java

    论文研究-Java多线程测试策略及测试方法探讨.pdf

    在分析Java 多线程特性的基础上, 探讨了Java 多线程的测试策略及测试方法, 提出Java 多线程测试由类测试、集成模块测试和系统测试三个层次组成, 并讨论了多线程的继承测试、同步测试以及效率测试。

    Java多线程知识点总结

    Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...

    Java多线程的简单应用

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应性。在Java中,实现多线程有两种主要方式:通过...通过分析和实践提供的Applet示例,我们可以深入理解和掌握Java多线程的应用。

    java 多线程小例子 很方便 很实用 适合初学者

    在给定的代码示例中,我们首先看到的是一个简单的Java多线程应用实例。这个例子展示了如何创建并启动一个新的线程。在`ThreadDemo`类的`main`方法中,我们创建了一个名为`TestThread`的新线程,并调用了它的`start()...

    java多线程进度条

    本主题将深入探讨如何在Java多线程环境下实现进度条功能。 首先,理解Java多线程的基本概念至关重要。Java通过Thread类和Runnable接口来支持多线程。创建一个新线程通常有两种方式:继承Thread类并重写run()方法,...

Global site tag (gtag.js) - Google Analytics