一:六種方法
題目:編寫程序實現兩個線程一個線程輸出0、2、3…51,另外一個線程實現A...Z, a...z,(0A1B2C....49x50y51z)
很多地方都可以見到類似的筆試題或者面試題,今天小輝以自己認知的範圍內列舉了如下6種方法,如果你們還有其他的方法留言給小輝,小輝立馬學習改正。
1:使用synchronized & wait & notify
public static void useSynchronizedAndWaitAndNotify() {
Object object = new Object();
new Thread(() -> {
synchronized (object) {
for (int i = 0; i < 52; i++) {
try {
object.notify();
System.out.print(i);
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(() -> {
synchronized (object) {
for (int i = 0; i < 52; i++) {
try {
char ch = getChar(i);
object.notify();
System.out.print(ch);
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
2:使用LinkedTransferQueue
使用put和transfer都可以,put是異步的,transfer是阻塞的。
public static void useTransferQueue() {
LinkedTransferQueue queue1 = new LinkedTransferQueue();
LinkedTransferQueue queue2 = new LinkedTransferQueue();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
try {
queue1.transfer(i);
// queue1.put(i);
System.out.print(queue2.take());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
try {
System.out.print(queue1.take());
char ch = getChar(i);
queue2.transfer(ch);
// queue2.put(ch);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
3:使用Lock & Condition
使用Lock鎖和Condition條件,這種寫法注意的是加鎖後一定要解鎖。使用這樣的寫法你寫多個線程交替循環執行也很容易實現,業務代碼放在signal和await前面,中間,後面有一些差別的哦。
public static void useLockAndCondition() {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
lock.lock();
try {
System.out.print(i);
condition2.signal();
condition1.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
lock.lock();
char ch = getChar(i);
try {
System.out.print(ch);
condition1.signal();
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
}
4:使用volatile & 循環
使用 volatile的常量,然後使用循環,由於使用循環去獲取flag的值,如果有一個業務中斷出問題了,其他哪個可能就會進入死循環,甚用。
private static volatile boolean flag = true;
/**
* 使用volatile & 循環
*/
public static void useVolatile() {
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (flag) {
System.out.print(i);
flag = false;
}
while (!flag) {
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (!flag) {
char ch = getChar(i);
System.out.print(ch);
flag = true;
}
while (flag) {
}
}
}).start();
}
5:使用Atomic(CAS) & 循環
使用Atomic和循環,Atomic看源碼就是使用了CAS算法進行自加自減操作的,這個也要保證業務代碼的異常捕獲處理,一個線程中斷,另外一個線程也應該中斷,否則又可以背鍋了。
private static AtomicInteger atomic = new AtomicInteger(0);
/**
* 使用Atomic(CAS) & 循環
*/
public static void useAtomic() {
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (atomic.get() % 2 == 0) {
System.out.print(i);
atomic.incrementAndGet();
}
while (atomic.get() % 2 != 0) {
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (atomic.get() % 2 != 0) {
char ch = getChar(i);
System.out.print(ch);
atomic.incrementAndGet();
}
while (atomic.get() % 2 == 0) {
}
}
}).start();
}
6:使用LockSupport
LockSupport
是一個線程阻塞工具類,所有的方法都是靜態方法,可以讓線程在任意位置阻塞,當然阻塞之後肯定得有喚醒的方法。
private static Thread t1;
private static Thread t2;
/**
* 使用LockSupport
*/
public static void uselockSupport() {
t1 = new Thread(() -> {
for (int i = 0; i < 52; i++) {
System.out.print(i);
LockSupport.unpark(t2);
LockSupport.park();
}
});
t2 = new Thread(() -> {
for (int i = 0; i < 52; i++) {
char ch = getChar(i);
LockSupport.park();
System.out.print(ch);
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
}
二、性能簡單比較
小輝寫了6個方法,但是也不知道那個方法是性能好的,所以小輝偷偷寫了一個main函數運行了一波結果如下:
由於數據不是很大所以差別不明顯,但是線程安全方面考慮第一種方法是不錯的,其他的要使用哪種方法就看你比較後自己的選擇了。
完整代碼如下:
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author yh128
* @version 1.0.0
* @ClassName Threads.java
* @Description 兩線程交替打印數據
* @Param
* @createTime 2019年12月25日 15:09:00
*/
public class Threads {
private static String timeStr = "耗時:%s ms";
private static String title = "\n--------------[%s] ";
/**
* 使用synchronized & wait & notify
*/
public static void useSynchronizedAndWaitAndNotify() {
Object object = new Object();
new Thread(() -> {
synchronized (object) {
for (int i = 0; i < 52; i++) {
try {
object.notify();
System.out.print(i);
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(() -> {
synchronized (object) {
for (int i = 0; i < 52; i++) {
try {
char ch = getChar(i);
object.notify();
System.out.print(ch);
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* 使用LinkedTransferQueue
*/
public static void useTransferQueue() {
LinkedTransferQueue queue1 = new LinkedTransferQueue();
LinkedTransferQueue queue2 = new LinkedTransferQueue();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
try {
queue1.transfer(i);
// queue1.put(i);
System.out.print(queue2.take());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
try {
System.out.print(queue1.take());
char ch = getChar(i);
queue2.transfer(ch);
// queue2.put(ch);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 使用Lock & Condition
*/
public static void useLockAndCondition() {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
lock.lock();
try {
System.out.print(i);
condition2.signal();
condition1.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
lock.lock();
char ch = getChar(i);
try {
System.out.print(ch);
condition1.signal();
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
}
public static volatile boolean flag = true;
/**
* 使用volatile & 循環
*/
public static void useVolatile() {
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (flag) {
System.out.print(i);
flag = false;
}
while (!flag) {
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (!flag) {
char ch = getChar(i);
System.out.print(ch);
flag = true;
}
while (flag) {
}
}
}).start();
}
private static AtomicInteger atomic = new AtomicInteger(0);
/**
* 使用Atomic(CAS) & 循環
*/
public static void useAtomic() {
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (atomic.get() % 2 == 0) {
System.out.print(i);
atomic.incrementAndGet();
}
while (atomic.get() % 2 != 0) {
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 52; i++) {
if (atomic.get() % 2 != 0) {
char ch = getChar(i);
System.out.print(ch);
atomic.incrementAndGet();
}
while (atomic.get() % 2 == 0) {
}
}
}).start();
}
private static Thread t1;
private static Thread t2;
/**
* 使用LockSupport
*/
public static void uselockSupport() {
t1 = new Thread(() -> {
for (int i = 0; i < 52; i++) {
System.out.print(i);
LockSupport.unpark(t2);
LockSupport.park();
}
});
t2 = new Thread(() -> {
for (int i = 0; i < 52; i++) {
char ch = getChar(i);
LockSupport.park();
System.out.print(ch);
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
}
public static void main(String[] args) throws InterruptedException {
// 第一個是包含初始化時間所以不精確不算在比較中
long start = System.currentTimeMillis();
System.out.println(String.format(title, "使用synchronized & wait & notify(包含了啓動時間所以再開啓一個來比較)"));
useSynchronizedAndWaitAndNotify();
Thread.sleep(500);
System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
start = System.currentTimeMillis();
System.out.println(String.format(title, "使用synchronized & wait & notify"));
useSynchronizedAndWaitAndNotify();
Thread.sleep(500);
System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
start = System.currentTimeMillis();
System.out.println(String.format(title, "使用LinkedTransferQueue"));
useTransferQueue();
Thread.sleep(500);
System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
start = System.currentTimeMillis();
System.out.println(String.format(title, "使用Lock & Condition"));
useLockAndCondition();
Thread.sleep(500);
System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
start = System.currentTimeMillis();
System.out.println(String.format(title, "使用volatile & 循環"));
useVolatile();
Thread.sleep(500);
System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
start = System.currentTimeMillis();
System.out.println(String.format(title, "使用Atomic(CAS) & 循環"));
useAtomic();
Thread.sleep(500);
System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
start = System.currentTimeMillis();
System.out.println(String.format(title, "使用LockSupport"));
uselockSupport();
Thread.sleep(500);
System.out.println(String.format(timeStr, System.currentTimeMillis() - start - 500));
// 關閉應用
System.exit(-1);
}
/**
* 獲取字母
*
* @param i
* @return
*/
private static char getChar(int i) {
if (i >= 26) {
return (char) (71 + i);
}
return (char) (65 + i);
}
}
這是小輝發現的6種方法有問題可以留言或者評論哦,歡迎你的技術性騷擾哦\(^o^)/。