最近一直在學習和實踐JAVA併發編程,也從書中總結了一些經驗,在這裏書寫一下可以馬上上手利用的內容,日後再慢慢補充完善。
1.在構建守護線程時,不能依靠finally塊中的內容,來確保執行關閉或清理資源的邏輯。
有如下示例代碼:
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("理論上,是執行不了這個方法的,因爲線程是守護線程");
}
}
});
thread.setDaemon(true);
thread.start();
System.out.println("Main線程執行完畢");
}
}
運行結果是隻會打印出“Main線程執行完畢這一句話”
2.java.util.concurrent.atomic包提供了高效的,線程安全地更新一個變量方式。
2.1原子更新基本類型類:原子方式更新基本數據類型
public class Main {
// 哪怕這裏加了volatile也是沒用的,因爲volatile可以保證線程之間的可見性,並不能保證原子性
static volatile int n = 0;
static AtomicInteger m = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
// 這裏結束的數值不能設置得太小,因爲太小的話,速度太快,無法體現出效果
for (int i = 0; i < 10000; i++) {
// 你這裏寫成n = n + 1或者n +=1,也是一樣基本得不到正確結果的
n++;
m.incrementAndGet();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
n++;
m.incrementAndGet();
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("n" + n);
System.out.println("m" + m.get());
}
}
public class Main {
static volatile CountDownLatch latch = new CountDownLatch(2);
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("線程1準備睡眠");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("線程1睡眠結束");
latch.countDown();
}
};
Thread t2 = new Thread() {
@Override
public void run() {
System.out.println("線程2準備睡眠");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("線程2睡眠結束");
latch.countDown();
}
};
t1.start();
t2.start();
System.out.println("main線程要準備被阻塞了");
latch.await();
System.out.println("main線程得以繼續執行");
}
}
運行結果:4.同步屏障CyclicBarrier
// 爲了顯示時間方便,直接使用了new Date().toLocaleString(),但是這是一個被廢棄的方法,而且實際開發中,不應該這樣寫
public class Main {
static CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
public void run() {
System.out.println("已經有兩個線程執行了await()方法了"
+ new Date().toLocaleString());
}
});
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("線程1準備睡眠" + new Date().toLocaleString());
try {
Thread.sleep(1000);
System.out.println("線程1睡眠結束" + new Date().toLocaleString());
cyclicBarrier.await();
} catch (Exception e) {
}
System.out.println("線程1await結束" + new Date().toLocaleString());
}
};
Thread t2 = new Thread() {
@Override
public void run() {
System.out.println("線程2準備睡眠" + new Date().toLocaleString());
try {
Thread.sleep(2000);
System.out.println("線程2睡眠結束" + new Date().toLocaleString());
cyclicBarrier.await();
} catch (Exception e) {
}
System.out.println("線程2await結束" + new Date().toLocaleString());
}
};
t1.start();
t2.start();
System.out.println("main線程要準備被阻塞了" + new Date().toLocaleString());
System.out.println("main線程得以繼續執行" + new Date().toLocaleString());
}
}
- 避免一個線程同時獲取多把鎖
- 避免一個線程在鎖內同時佔用多個資源,儘量保證每個鎖只佔用一個資源
- 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內部鎖機制。
- 對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裏,否則會出現解鎖失敗的情況。
public class Main {
static Object object = new Object();
public static void main(String[] args) throws Exception {
methodA();
}
public static void methodA() {
System.out.println("methodA想鎖住了object");
synchronized (object) {
System.out.println("methodA鎖住了object,並且準備調用methodB");
methodB();
}
}
public static void methodB() {
// 因爲JAVA的鎖是可重入的,所以這裏另開一個線程
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("methodB想鎖住了object");
synchronized (object) {
System.out.println("methodB鎖住了object,並且準備調用methodA");
methodA();
}
}
};
thread.start();
try {
// methodB想等待線程執行完之後,才結束,但是它不知道methodA調用methodB的時候,已經鎖上了object,而methodB又在線程中企圖鎖object,於是就發送了死鎖。這是一種容易被忽略的死鎖情況
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//一個獨佔鎖的簡易實現(不可重入)
class Mutex implements Lock {
private static class Sync extends AbstractQueuedSynchronizer {
/**
*
*/
private static final long serialVersionUID = -642099341499014369L;
/**
* 是否處於佔用狀態
*/
@Override
protected boolean isHeldExclusively() {
// 1表示鎖被佔用
return getState() == 1;
}
/**
* 當狀態爲0的時候獲取鎖
*/
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/**
* 釋放鎖並將狀態設置爲0
*/
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.tryAcquire(1);
}
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
}
支持多個線程同時訪問的鎖(不可重入)// 支持共享式訪問(即同一時刻支持多個線程訪問)
class ArbitraryCountLock implements Lock {
private int lockCount = 2;
private final Sync sync;
public ArbitraryCountLock(int lockCount) {
super();
this.lockCount = lockCount;
sync = new Sync(lockCount);
}
public void lock() {
sync.acquireShared(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean tryLock() {
return sync.tryAcquireShared(1) > 0 ? true : false;
}
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(time));
}
public void unlock() {
sync.releaseShared(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public int getLockCount() {
return lockCount;
}
private static class Sync extends AbstractQueuedSynchronizer {
/**
*
*/
private static final long serialVersionUID = -642099341499014369L;
Sync(int count) {
if (count < 1) {
throw new RuntimeException("鎖的數量最小爲1");
}
setState(count);
}
// 在失敗時返回負值;如果共享模式下的獲取成功但其後續共享模式下的獲取不能成功,則返回
// 0;如果共享模式下的獲取成功並且其後續共享模式下的獲取可能夠成功,則返回正值
@Override
protected int tryAcquireShared(int reduceCount) {
while (true) {
int current = getState();
int newCount = current - reduceCount;
if (newCount < 0 || compareAndSetState(current, newCount)) {
return newCount;
}
}
}
@Override
protected boolean tryReleaseShared(int returnCount) {
while (true) {
int current = getState();
int newCount = current + returnCount;
if (compareAndSetState(current, newCount)) {
return true;
}
}
}
Condition newCondition() {
return new ConditionObject();
}
}
}
static Object |
getBlocker(Thread t) 返回提供給最近一次尚未解除阻塞的 park 方法調用的 blocker 對象,如果該調用不受阻塞,則返回 null。 |
static void |
park() 爲了線程調度,禁用當前線程,除非許可可用。 |
static void |
park(Object blocker) 爲了線程調度,在許可可用之前禁用當前線程。 |
static void |
parkNanos(long nanos) 爲了線程調度禁用當前線程,最多等待指定的等待時間,除非許可可用。 |
static void |
parkNanos(Object blocker,
long nanos) 爲了線程調度,在許可可用前禁用當前線程,並最多等待指定的等待時間。 |
static void |
parkUntil(long deadline) 爲了線程調度,在指定的時限前禁用當前線程,除非許可可用。 |
static void |
parkUntil(Object blocker,
long deadline) 爲了線程調度,在指定的時限前禁用當前線程,除非許可可用。 |
static void |
unpark(Thread thread) 如果給定線程的許可尚不可用,則使其可用。 |
Delayed 元素的一個無界阻塞隊列,只有在延遲期滿時才能從中提取元素。該隊列的頭部 是延遲期滿後保存時間最長的 Delayed 元素。如果延遲都還沒有期滿,則隊列沒有頭部,並且 poll 將返回 null。當一個元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一個小於等於 0 的值時,將發生到期。即使無法使用 take 或 poll 移除未到期的元素,也不會將這些元素作爲正常元素對待。例如,size 方法同時返回到期和未到期元素的計數。此隊列不允許使用 null 元素。