幾個小知識
1. 在異常信息中包含異常的細節
2. 保持操作失敗的原子性,舉個Stack中的例子
public synchronized E peek() { int len = size(); if (len == 0) throw new EmptyStackException(); return elementAt(len - 1); }
如果沒有這個len的判斷,如果為0也會抛出異常,但是問題是len就改變了,以後的操作就會抛出數組越界異常,對與一個對象來講數組越界是不恰當的,這是第一種方法。
第二種方法是在對對象進行修改前對數據進行足夠的審核,如果不符合就抛出異常停止。
第三種方法就是克隆一下先前的對象,這個有點像GUI中的undo,但是在實際中爲了保證原子性好像沒什麽人這麽做
3. 不要忽略一場,這與第一點不謀而合,如果實在想忽略一定要加一段注釋
4. 同步的用法,這塊算是個程序員都要掌握的地方,但是很多錯誤的理解,同步其實是通信,這裏擧個例子
private static boolean stopRequested; public static void main(String[] args) throws InterruptedException { Thread bacThread = new Thread(() -> { int i = 0; if (!stopRequested) { while (true) { i++; System.out.println(i); } } }); bacThread.start(); TimeUnit.SECONDS.sleep(1000); stopRequested = true; }
這段代碼就是典型的活性失敗,下面是一個正確的同步代碼
private static Boolean stopRequested; private static synchronized void requestStop() { stopRequested = true; } private static synchronized Boolean stopRequested() { return stopRequested; } public static void main(String[] args) throws InterruptedException { Thread bacThread = new Thread(() -> { int i = 0; while (!stopRequested()) { i++; System.out.println(i); } }); stopRequested = false; bacThread.start(); TimeUnit.SECONDS.sleep(1); // Thread.sleep(5000); requestStop(); }
這裏必須要將讀寫都同步,這裏也可用volatile,因爲原邏輯不是要互斥,而是要通信
虽然 volatile 修饰 符不执行互斥访问,但它可以保证任何一个线程在读取该字段的时候都将看到近刚刚被写入的值,但是如果是下面這種就會出問題
private static volatile int num = 0; public static int generateNum() { return num++; }
因爲++不是一個原子操作,它有兩個步驟,所以多綫程直接爆炸。下面有一種AtomicLong可以解決。java.util.concurrent.atomic 这个包为在 单个变量上进行免锁定、线程安全的编程提供了基本类型。虽然 volatile 只提供了同步的通信效果,但这个包还提供 了原子性。
private static final AtomicLong num = new AtomicLong(); public static long generateNum() { return num.getAndIncrement(); }
5. 为了避免死锁和数据破坏,千万不要从同步区字段内部调用外来方法。更通俗地讲,要尽量将同步区 字段内部的工作量限制到少。当你在设计一个可变类的时候,要考虑一下它们是否应该自己完成同步操作。在如今 这个多核的时代,这比永远不要过度同步来得更重要。只有当你有足够的理由一定要在内部同步类的时候,才应该这 么做,同时还应该将这个决定清楚地写到文档中
6. 慎用序列化