比較LongAdder、AtomicLong、synchronized(Long)效率
public class AtomicVsSyncVsLongAdder {
static long count2 = 0L;
static AtomicLong count1 = new AtomicLong(0L);
static LongAdder count3 = new LongAdder();
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[1000];
for (int i = 0; i < threads.length; i++) {
threads[i] =
new Thread(() -> {
for (int k = 0; k < 100000; k++) count1.incrementAndGet();
});
}
long start = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
long end = System.currentTimeMillis();
//TimeUnit.SECONDS.sleep(10);
System.out.println("Atomic: " + count1.get() + " time " + (end - start));
//-----------------------------------------------------------
Object lock = new Object();
for (int i = 0; i < threads.length; i++) {
threads[i] =
new Thread(() -> {
for (int k = 0; k < 100000; k++)
synchronized (lock) {
count2++;
}
});
}
start = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
end = System.currentTimeMillis();
System.out.println("Sync: " + count2 + " time " + (end - start));
//----------------------------------
for (int i = 0; i < threads.length; i++) {
threads[i] =
new Thread(() -> {
for (int k = 0; k < 100000; k++) count3.increment();
});
}
start = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
end = System.currentTimeMillis();
//TimeUnit.SECONDS.sleep(10);
System.out.println("LongAdder: " + count1.longValue() + " time " + (end - start));
}
static void microSleep(int m) {
try {
TimeUnit.MICROSECONDS.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
這個小程序用了1000個線程,每個線程分別將值從0到100000遞增,然後計算所用的時間,結果如下圖所示
可以看到LongAdder效率最高,其次是AtomicLong,synchronize最慢
原因是AtomicLong是CAS(無鎖)效率比synchronize高,而LongAdder是使用分段鎖,將1000個線程分成5份,200個線程一組去加值,最後將5個加起來的結果再相加,優勢是在線程較多的情況下效率比AtomicLong更高
可重入鎖 ReentrantLock
- ReentrantLock是一種可重入鎖,跟synchronized作用差不多,但功能比synchronized要多,比如ReentrantLock可以設置等待時間,假如在指定時間內未拿到鎖,則會放棄,代碼如下
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 使用tryLock進行嘗試鎖定,不管鎖定與否,方法都將繼續執行
* 可以根據tryLock的返回值來判定是否鎖定
* 也可以指定tryLock的時間,由於tryLock(time)拋出異常,所以要注意unclock的處理,必須放到finally中
*/
void m2() {
boolean locked = false;
try {
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2 ..." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(locked) lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockDemo2 rl = new ReentrantLockDemo2();
new Thread(rl::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(rl::m2).start();
}
結果:m2 …false,如果將循環次數改爲4,則結果爲m2 …true
公平鎖 非公平鎖
ReentrantLock可以在構造的時候進行設置是否爲公平鎖,公平鎖不是絕對公平,而是相對公平,看代碼
public class ReentrantLockDemo3 extends Thread {
//參數爲true表示爲公平鎖,請對比輸出結果
private static ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "獲得鎖");
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ReentrantLockDemo3 rl = new ReentrantLockDemo3();
Thread th1 = new Thread(rl);
Thread th2 = new Thread(rl);
th1.start();
th2.start();
}
結果是兩個線程基本交替打印
CountDownLatch 門栓
- 是門栓,可以用在等待線程執行完畢後再進行業務的場景下,代碼如下
public class CountDownLatchDemo {
public static void main(String[] args) {
usingCountDown();
// usingJoin();
}
private static void usingCountDown() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
int finalI = i;
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 100; j++) {
result += j;
}
System.out.println("thread " + finalI + " result = " + result);
latch.countDown();
});
}
for (Thread thread : threads) {
thread.start();
}
try {
// 這段代碼處於阻塞狀態,當100個線程都執行完畢的時候纔打印countDown end...
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("countDown end...");
}
private static void usingJoin() {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
int finalI = i;
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 100; j++) {
result += j;
}
System.out.println("thread " + finalI + " result = " + result);
});
}
for (Thread thread1 : threads) {
thread1.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end join");
}
}
CyclicBarrier 柵欄
- 柵欄的作用是等待,等待線程達到一定數量的時候,推到,運行一段邏輯,請看下面的示例
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(20, () -> System.out.println("滿人,發車"));
for (int i = 0; i < 100; i++) {
new Thread(() -> {
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
執行結果:就是打印5次 滿人,發車
讀寫鎖 ReadWriteLock
- 讀鎖ReentrantReadWriteLock.WriteLock本質上是一把共享鎖
- 寫鎖ReentrantReadWriteLock.ReadLock本質上是一把排它鎖
看下面一段程序,可以發現假如都是用共享鎖,大概需要20秒的時間才能執行完,但是當是用讀鎖和寫鎖,則大概只需要3秒鐘時間
public class ReadWriteLockDemo {
private static int value;
// 定義一個排他鎖
static Lock lock = new ReentrantLock();
// 定義讀寫鎖
static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 讀鎖
static Lock readLock = readWriteLock.readLock();
// 寫鎖
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
TimeUnit.SECONDS.sleep(1);
System.out.println("read over...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int v) {
try {
lock.lock();
TimeUnit.SECONDS.sleep(1);
value = v;
System.out.println("write over!");
//模擬寫操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
// 排他鎖
// Runnable readR = ()->read(lock);
// 讀鎖 共享鎖
Runnable readR = () -> read(readLock);
// 排他鎖
// Runnable writeR = () -> write(lock, 10);
// 寫鎖 排它鎖
Runnable writeR = () -> write(writeLock, 10);
for(int i=0; i<18; i++) new Thread(readR).start();
for(int i=0; i<2; i++) new Thread(writeR).start();
}
}
信號量 Semaphore
- Semaphore作用是限流,用來限制同時運行的線程數
- 車道 加油站
簡單使用代碼
public class SemaphoreDemo {
public static void main(String[] args) {
//Semaphore s = new Semaphore(2);
Semaphore s = new Semaphore(2, true);
//允許一個線程同時執行
//Semaphore s = new Semaphore(1);
new Thread(()->{
try {
s.acquire();
System.out.println("T1 running...");
Thread.sleep(200);
System.out.println("T1 running...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release();
}
}).start();
new Thread(()->{
try {
s.acquire();
System.out.println("T2 running...");
Thread.sleep(200);
System.out.println("T2 running...");
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}