java並發程式設計建議

來源:互聯網
上載者:User
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$ jps8208 Launcher8209 DeadLock8307 Jps94498 77482 3342 Launcher3343 SpringApplicationBoot

進程id為8209,我們看一下線程資訊:

beaver:~ beaver$ jstack 82092018-02-04 14:10:20Full 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(java.base@9.0.1/Native Method)- parking to wait for  <0x00000006cfdf5470> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(java.base@9.0.1/LockSupport.java:194)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(java.base@9.0.1/AbstractQueuedSynchronizer.java:871)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(java.base@9.0.1/AbstractQueuedSynchronizer.java:903)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@9.0.1/AbstractQueuedSynchronizer.java:1226)at java.util.concurrent.locks.ReentrantLock.lock(java.base@9.0.1/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(java.base@9.0.1/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(java.base@9.0.1/Native Method)- parking to wait for  <0x00000006cfdf54a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(java.base@9.0.1/LockSupport.java:194)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(java.base@9.0.1/AbstractQueuedSynchronizer.java:871)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(java.base@9.0.1/AbstractQueuedSynchronizer.java:903)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@9.0.1/AbstractQueuedSynchronizer.java:1226)at java.util.concurrent.locks.ReentrantLock.lock(java.base@9.0.1/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(java.base@9.0.1/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: RUNNABLEat java.net.SocketInputStream.socketRead0(java.base@9.0.1/Native Method)at java.net.SocketInputStream.socketRead(java.base@9.0.1/SocketInputStream.java:116)at java.net.SocketInputStream.read(java.base@9.0.1/SocketInputStream.java:171)at java.net.SocketInputStream.read(java.base@9.0.1/SocketInputStream.java:141)at sun.nio.cs.StreamDecoder.readBytes(java.base@9.0.1/StreamDecoder.java:284)at sun.nio.cs.StreamDecoder.implRead(java.base@9.0.1/StreamDecoder.java:326)at sun.nio.cs.StreamDecoder.read(java.base@9.0.1/StreamDecoder.java:178)- locked <0x00000006cfc55b58> (a java.io.InputStreamReader)at java.io.InputStreamReader.read(java.base@9.0.1/InputStreamReader.java:185)at java.io.BufferedReader.fill(java.base@9.0.1/BufferedReader.java:161)at java.io.BufferedReader.readLine(java.base@9.0.1/BufferedReader.java:326)- locked <0x00000006cfc55b58> (a java.io.InputStreamReader)at java.io.BufferedReader.readLine(java.base@9.0.1/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(java.base@9.0.1/Native Method)- waiting on <0x00000006cff37bc0> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(java.base@9.0.1/ReferenceQueue.java:151)- waiting to re-lock in wait() <0x00000006cff37bc0> (a java.lang.ref.ReferenceQueue$Lock)at jdk.internal.ref.CleanerImpl.run(java.base@9.0.1/CleanerImpl.java:148)at java.lang.Thread.run(java.base@9.0.1/Thread.java:844)at jdk.internal.misc.InnocuousThread.run(java.base@9.0.1/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(java.base@9.0.1/Native Method)- waiting on <0x00000006cff0d070> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(java.base@9.0.1/ReferenceQueue.java:151)- waiting to re-lock in wait() <0x00000006cff0d070> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(java.base@9.0.1/ReferenceQueue.java:172)at java.lang.ref.Finalizer$FinalizerThread.run(java.base@9.0.1/Finalizer.java:216)"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fb2c606c000 nid=0x5403 waiting on condition [0x00007000100d3000]   java.lang.Thread.State: RUNNABLEat java.lang.ref.Reference.waitForReferencePendingList(java.base@9.0.1/Native Method)at java.lang.ref.Reference.processPendingReferences(java.base@9.0.1/Reference.java:174)at java.lang.ref.Reference.access$000(java.base@9.0.1/Reference.java:44)at java.lang.ref.Reference$ReferenceHandler.run(java.base@9.0.1/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: 133Found 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(java.base@9.0.1/Native Method)- parking to wait for  <0x00000006cfdf5470> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(java.base@9.0.1/LockSupport.java:194)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(java.base@9.0.1/AbstractQueuedSynchronizer.java:871)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(java.base@9.0.1/AbstractQueuedSynchronizer.java:903)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@9.0.1/AbstractQueuedSynchronizer.java:1226)at java.util.concurrent.locks.ReentrantLock.lock(java.base@9.0.1/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(java.base@9.0.1/Thread.java:844)"Thread-0":at jdk.internal.misc.Unsafe.park(java.base@9.0.1/Native Method)- parking to wait for  <0x00000006cfdf54a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(java.base@9.0.1/LockSupport.java:194)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(java.base@9.0.1/AbstractQueuedSynchronizer.java:871)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(java.base@9.0.1/AbstractQueuedSynchronizer.java:903)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@9.0.1/AbstractQueuedSynchronizer.java:1226)at java.util.concurrent.locks.ReentrantLock.lock(java.base@9.0.1/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(java.base@9.0.1/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.Lockboolean 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
避免在鎖內出現阻塞操作

參考:
Effective Java (3rd Edition)
Java 9 Concurrency Cookbook - Second Edition

      




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.