java并发程序设计建议

java并发程序设计建议

尽可能使用不可变对象


在并发应用程序环境中,不可变对象 有以下优点:

(1)不可变对象一旦创建,任何线程程序对其不可修改,所以不需要使用同步机制来保护它们的属性。

(2)不可变对象没有数据一致性问题。

但唯一的缺点就是:不能修改现有对象,而是创建新对象。

其实jdk也提供了一些不可变对象,如string、BigDecimal、BigInteger及LocalDateTime等等。

要实现一个不可变类需要遵守以下规则:

(1)类是final的。

(2)类中的属性是private final的。

(3)类中的属性必须由构造函数提供初始化。而且不能提供set方法。

(4)类中的可变属性的get方法,必须通过创建新的对象返回,来提供保护性copy。


通过锁排序避免死锁


我们看一段由于锁顺序不当引起的死锁:

package com.doctor.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author sdcuike
 * @date 2018/2/4
 * @since 2018/2/4
 */
public class DeadLock {
    private Lock lockA = new ReentrantLock();
    private Lock lockB = new ReentrantLock();
    
    public void operation1() {
        lockA.lock();
        lockB.lock();
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lockB.unlock();
            lockA.unlock();
        }
    }
    
    public void operation2() {
        lockB.lock();
        lockA.lock();
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lockA.unlock();
            lockB.unlock();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        DeadLock deadLock = new DeadLock();
        
        new Thread(()->{
            deadLock.operation1();
        }).start();
        
        new Thread(()->{
            deadLock.operation2();
        }).start();
        
        TimeUnit.SECONDS.sleep(5);
    
    }
}


运行程序,有时候会看到进程始终没退出,控制台无任何输入,我们查看一下此时jvm的进程:


beaver:~ beaver$ jps
8208 Launcher
8209 DeadLock
8307 Jps
94498 
77482 
3342 Launcher
3343 SpringApplicationBoot

进程id为8209,我们看一下线程信息:

beaver:~ beaver$ jstack 8209
2018-02-04 14:10:20
Full thread dump Java HotSpot(TM) 64-Bit Server VM (9.0.1+11 mixed mode):

"Attach Listener" #16 daemon prio=9 os_prio=31 tid=0x00007fb2c7834000 nid=0x6307 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #15 prio=5 os_prio=31 tid=0x00007fb2c6008800 nid=0x1c03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #14 prio=5 os_prio=31 tid=0x00007fb2c5855800 nid=0x7c03 waiting on condition [0x0000700010e7d000]
   java.lang.Thread.State: WAITING (parking)
	at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
	- parking to wait for  <0x00000006cfdf5470> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park([email protected]/LockSupport.java:194)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt([email protected]/AbstractQueuedSynchronizer.java:871)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued([email protected]/AbstractQueuedSynchronizer.java:903)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire([email protected]/AbstractQueuedSynchronizer.java:1226)
	at java.util.concurrent.locks.ReentrantLock.lock([email protected]/ReentrantLock.java:267)
	at com.doctor.thread.DeadLock.operation2(DeadLock.java:32)
	at com.doctor.thread.DeadLock.lambda$main$1(DeadLock.java:52)
	at com.doctor.thread.DeadLock$$Lambda$2/2114694065.run(Unknown Source)
	at java.lang.Thread.run([email protected]/Thread.java:844)

"Thread-0" #13 prio=5 os_prio=31 tid=0x00007fb2c584f800 nid=0x7a03 waiting on condition [0x0000700010d7a000]
   java.lang.Thread.State: WAITING (parking)
	at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
	- parking to wait for  <0x00000006cfdf54a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park([email protected]/LockSupport.java:194)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt([email protected]/AbstractQueuedSynchronizer.java:871)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued([email protected]/AbstractQueuedSynchronizer.java:903)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire([email protected]/AbstractQueuedSynchronizer.java:1226)
	at java.util.concurrent.locks.ReentrantLock.lock([email protected]/ReentrantLock.java:267)
	at com.doctor.thread.DeadLock.operation1(DeadLock.java:18)
	at com.doctor.thread.DeadLock.lambda$main$0(DeadLock.java:48)
	at com.doctor.thread.DeadLock$$Lambda$1/1170794006.run(Unknown Source)
	at java.lang.Thread.run([email protected]/Thread.java:844)

"Service Thread" #12 daemon prio=9 os_prio=31 tid=0x00007fb2c8006800 nid=0x7603 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #11 daemon prio=5 os_prio=31 tid=0x00007fb2c7824000 nid=0x7403 runnable [0x0000700010a71000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0([email protected]/Native Method)
	at java.net.SocketInputStream.socketRead([email protected]/SocketInputStream.java:116)
	at java.net.SocketInputStream.read([email protected]/SocketInputStream.java:171)
	at java.net.SocketInputStream.read([email protected]/SocketInputStream.java:141)
	at sun.nio.cs.StreamDecoder.readBytes([email protected]/StreamDecoder.java:284)
	at sun.nio.cs.StreamDecoder.implRead([email protected]/StreamDecoder.java:326)
	at sun.nio.cs.StreamDecoder.read([email protected]/StreamDecoder.java:178)
	- locked <0x00000006cfc55b58> (a java.io.InputStreamReader)
	at java.io.InputStreamReader.read([email protected]/InputStreamReader.java:185)
	at java.io.BufferedReader.fill([email protected]/BufferedReader.java:161)
	at java.io.BufferedReader.readLine([email protected]/BufferedReader.java:326)
	- locked <0x00000006cfc55b58> (a java.io.InputStreamReader)
	at java.io.BufferedReader.readLine([email protected]/BufferedReader.java:392)
	at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

"Common-Cleaner" #10 daemon prio=8 os_prio=31 tid=0x00007fb2c9819800 nid=0x7203 in Object.wait() [0x000070001096e000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait([email protected]/Native Method)
	- waiting on <0x00000006cff37bc0> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:151)
	- waiting to re-lock in wait() <0x00000006cff37bc0> (a java.lang.ref.ReferenceQueue$Lock)
	at jdk.internal.ref.CleanerImpl.run([email protected]/CleanerImpl.java:148)
	at java.lang.Thread.run([email protected]/Thread.java:844)
	at jdk.internal.misc.InnocuousThread.run([email protected]/InnocuousThread.java:122)

"Sweeper thread" #9 daemon prio=9 os_prio=31 tid=0x00007fb2c609b000 nid=0x7003 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #8 daemon prio=9 os_prio=31 tid=0x00007fb2c609a000 nid=0x6e03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"C2 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007fb2c7804800 nid=0x6c03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007fb2c5818000 nid=0x6a03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007fb2c5817000 nid=0x6803 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fb2c88ac800 nid=0x6603 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fb2c606c800 nid=0x5603 in Object.wait() [0x00007000101d6000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait([email protected]/Native Method)
	- waiting on <0x00000006cff0d070> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:151)
	- waiting to re-lock in wait() <0x00000006cff0d070> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:172)
	at java.lang.ref.Finalizer$FinalizerThread.run([email protected]/Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fb2c606c000 nid=0x5403 waiting on condition [0x00007000100d3000]
   java.lang.Thread.State: RUNNABLE
	at java.lang.ref.Reference.waitForReferencePendingList([email protected]/Native Method)
	at java.lang.ref.Reference.processPendingReferences([email protected]/Reference.java:174)
	at java.lang.ref.Reference.access$000([email protected]/Reference.java:44)
	at java.lang.ref.Reference$ReferenceHandler.run([email protected]/Reference.java:138)

"VM Thread" os_prio=31 tid=0x00007fb2c9802800 nid=0x5203 runnable 

"GC Thread#0" os_prio=31 tid=0x00007fb2c8805800 nid=0x2803 runnable 

"GC Thread#1" os_prio=31 tid=0x00007fb2c8001000 nid=0x2a03 runnable 

"GC Thread#2" os_prio=31 tid=0x00007fb2c8806000 nid=0x2c03 runnable 

"GC Thread#3" os_prio=31 tid=0x00007fb2c8807000 nid=0x2e03 runnable 

"GC Thread#4" os_prio=31 tid=0x00007fb2c9003000 nid=0x3003 runnable 

"GC Thread#5" os_prio=31 tid=0x00007fb2c8807800 nid=0x3203 runnable 

"GC Thread#6" os_prio=31 tid=0x00007fb2c7801000 nid=0x3403 runnable 

"GC Thread#7" os_prio=31 tid=0x00007fb2c7802000 nid=0x3603 runnable 

"G1 Main Marker" os_prio=31 tid=0x00007fb2c8830800 nid=0x4a03 runnable 

"G1 Marker#0" os_prio=31 tid=0x00007fb2c8831000 nid=0x4e03 runnable 

"G1 Marker#1" os_prio=31 tid=0x00007fb2c8831800 nid=0x5003 runnable 

"G1 Refine#0" os_prio=31 tid=0x00007fb2c580b800 nid=0x4603 runnable 

"G1 Refine#1" os_prio=31 tid=0x00007fb2c880a800 nid=0x4403 runnable 

"G1 Refine#2" os_prio=31 tid=0x00007fb2c8809800 nid=0x4203 runnable 

"G1 Refine#3" os_prio=31 tid=0x00007fb2c8809000 nid=0x4003 runnable 

"G1 Refine#4" os_prio=31 tid=0x00007fb2c7000800 nid=0x3e03 runnable 

"G1 Refine#5" os_prio=31 tid=0x00007fb2c580b000 nid=0x3c03 runnable 

"G1 Refine#6" os_prio=31 tid=0x00007fb2c9004800 nid=0x3a03 runnable 

"G1 Refine#7" os_prio=31 tid=0x00007fb2c8808000 nid=0x3803 runnable 

"G1 Young RemSet Sampling" os_prio=31 tid=0x00007fb2c580c800 nid=0x4803 runnable 

"VM Periodic Task Thread" os_prio=31 tid=0x00007fb2c7007000 nid=0x7803 waiting on condition 

JNI global references: 133


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting for ownable synchronizer 0x00000006cfdf5470, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-0"
"Thread-0":
  waiting for ownable synchronizer 0x00000006cfdf54a0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
	- parking to wait for  <0x00000006cfdf5470> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park([email protected]/LockSupport.java:194)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt([email protected]/AbstractQueuedSynchronizer.java:871)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued([email protected]/AbstractQueuedSynchronizer.java:903)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire([email protected]/AbstractQueuedSynchronizer.java:1226)
	at java.util.concurrent.locks.ReentrantLock.lock([email protected]/ReentrantLock.java:267)
	at com.doctor.thread.DeadLock.operation2(DeadLock.java:32)
	at com.doctor.thread.DeadLock.lambda$main$1(DeadLock.java:52)
	at com.doctor.thread.DeadLock$$Lambda$2/2114694065.run(Unknown Source)
	at java.lang.Thread.run([email protected]/Thread.java:844)
"Thread-0":
	at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
	- parking to wait for  <0x00000006cfdf54a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park([email protected]/LockSupport.java:194)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt([email protected]/AbstractQueuedSynchronizer.java:871)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued([email protected]/AbstractQueuedSynchronizer.java:903)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire([email protected]/AbstractQueuedSynchronizer.java:1226)
	at java.util.concurrent.locks.ReentrantLock.lock([email protected]/ReentrantLock.java:267)
	at com.doctor.thread.DeadLock.operation1(DeadLock.java:18)
	at com.doctor.thread.DeadLock.lambda$main$0(DeadLock.java:48)
	at com.doctor.thread.DeadLock$$Lambda$1/1170794006.run(Unknown Source)
	at java.lang.Thread.run([email protected]/Thread.java:844)

Found 1 deadlock.

    Found 1 deadlock,我们发现死锁发生了。上述代码就是由于锁的顺序不当造成了死锁的发生。

详细代码:https://github.com/sdcuike/java8-learning/blob/master/src/main/java/com/doctor/thread/DeadLock.java

   所以我们并非访问共享资源的时候,锁的顺序或者资源的顺序不当,很容易造成死锁。

   有时候锁的顺序我们调整不了,我们可以用java.util.concurrent.locks.Lock#tryLock():


java.util.concurrent.locks.Lock
boolean tryLock()
Acquires the lock only if it is free at the time of invocation.
Acquires the lock if it is available and returns immediately with the value true. If the lock is not available then this method will return immediately with the value false.
A typical usage idiom for this method would be:
       Lock lock = ...;
      if (lock.tryLock()) {
        try {
          // manipulate protected state
        } finally {
          lock.unlock();
        }
      } else {
        // perform alternative actions
      }
This usage ensures that the lock is unlocked if it was acquired, and doesn't try to unlock if the lock was not acquired.
Returns:
true if the lock was acquired and false otherwise

适当场景使用原子变量

 当多线程共享资源时,我们一般保护对临界区会使用同步机制。在java中我们常用的就是同步关键字synchronized以及并发工具类Lock。

 自java5,又提供了一种方法:原子变量。原子变量的性能要比老的同步机制高,因为原子变量使用的乐观锁。具体请参考jdk包java.util.concurrent.atomic。


锁的粒度越小越好


     临界区的范围约小约好,我们可以通过把一个大的临界区拆分几个不相干的临界区,使用不同的锁能使并发性能

提供。

具有代表性的那就是jdk中的并发容器ConcurrentHashMap,通过分段锁使并发性能提供。


优先使用jdk executors来管理线程


优先使用jdk提供的并发数据结构,而不是造轮子


 jdk提供的并发数据结构那是并发大师Doug Lea的杰作,一般分两种类型:
     (1)非阻塞数据结构(Non-blocking data structures)
     (2)阻塞数据结构(Blocking data structures)
 具体详细请参考java包java.util.concurrent或其他书籍。

使用java 8的streams来处理大数据集


不使用过时的线程api


避免在锁内出现阻塞操作

      




猜你喜欢

转载自blog.csdn.net/doctor_who2004/article/details/79252134
今日推荐