同步訪問共享的可變數據
synchronized : 可以保證在同一時刻,只有一個線程可以執行某一個方法, 或者某一個代碼塊.
多線程訪問, 可能會導致同一對象狀態發生變化. 同步 可以使多線程看到由同一個鎖保護的之前所有的修改效果.
非long或double類型的變量, 讀寫操作一個變量是原子的
在線程之間進行可靠的通信, 也爲了互斥訪問, 同步是必要的.
不要使用 Thread.stop() , 要阻止一個線程妨礙另一個線程 ,正確做法如下 :
public class StopThread { public static boolean stopRequested; private static synchronized void requestStop() { stopRequested = true; } public static synchronized boolean stopRequested() { return stopRequested; } public void stopThread() throws InterruptedException { Thread backgroundThread = new Thread(new Runnable() { @Override public void run() { int i = 0; while (!stopRequested()) { i++; } } }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); requestStop(); } }
如果讀寫操作都沒有進行同步 , 那麼同步就不會起作用.
public class StopThread { public static volatile boolean stopRequested; public void stopThread() throws InterruptedException { Thread backgroundThread = new Thread(new Runnable() { @Override public void run() { int i = 0; while (!stopRequested) { i++; } } }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); stopRequested = true; } }
多個線程共享可變數據的時候, 每個讀或者寫數據的線程都必須執行同步
避免過度同步
- 共享數據類型,需要使用線程安全的數據結構 , 例如 : CopyOnWriteArrayList
executor和task優先於線程
- 儘量避免自己編寫工作隊列, 儘量不要直接使用線程, 使用java提供的executor線程池
併發工具優先於wait和notify
線程高級工具 :
- Executor Framework
- 併發集合
- 同步器 : CountDownLatch 和 Semaphore
public class Synchronizer { final Executor executor = Executors.newSingleThreadExecutor(); final CountDownLatch ready = new CountDownLatch(3); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch done = new CountDownLatch(3); final Runnable runnable = new Runnable() { @Override public void run() { //do nothing } }; private long test() throws InterruptedException { for (int i = 0;i < 3; i++) { executor.execute(new Runnable() { @Override public void run() { ready.countDown(); try { start.await(); runnable.run(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { done.countDown(); } } }); } ready.await(); long startNanos = System.nanoTime(); start.countDown(); done.await(); return System.nanoTime() - startNanos; } }
- 調用wait方法,應當用循環之內調用 , 永遠不要在循環外調用
- 優先使用notifyAll , 當只有一個線程被喚醒才使用notify
延遲初始化
lazy initializition holder class 模式 : 常見用於單例模式
private static class FieldHolder { static final FieldType field = computeFieldValue(); } static FieldType getFieldType() { return FieldHolder.field; }
避免使用線程組
- ThreadGroup 已經爲過時的API, 沒必要繼續使用