文章目录
有两组数据:
第 1 组:ABCDEFGHI
第 2 组:123456789
怎么样才能输出 A1B2C3D4… 呢,下面给出 7 种实现方案,处欢迎大家积极讨论。
方式1、死循环
package taishangcode;
/**
* @Description:利用死循环 交叉输出 A1B2C3...
* 公众号:taishangcode
*/
public class ForPrint {
static volatile int a = 0; // 加上 volatile 使其中一个线程对 a 修改的结果对另一线程透明
public static void main(String[] args) {
new Thread(() -> {
for (char c = 'A'; c < 'J'; c++) {
// 等同 while(a!=0){}
for(; a!=0;) {}
System.out.print(c);
a=1;
}
}).start();
new Thread(() -> {
for (int i = 1; i < 10; i++) {
// 等同 while(a!=1){}
for(; a!=1;) {}
System.out.print(i);
a=0;
}
}).start();
}
}
方式2、LockSupport
package taishangcode;
import java.util.concurrent.locks.LockSupport;
/**
* @Description:LockSupport 交叉输出 A1B2C3...
* 公众号:taishangcode
*/
public class LockSupportPrint {
static Thread t1,t2;
public static void main(String[] args) {
t1 = new Thread(() -> {
for (char c = 'A'; c < 'J'; c++) {
System.out.print(c);
LockSupport.unpark(t2);
LockSupport.park();
}
});
t2 = new Thread(() -> {
for (int i = 1; i < 10; i++) {
LockSupport.park();
System.out.print(i);
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
}
}
方式3、synchronized
package taishangcode;
/**
* @Description:synchronized 结合 wait() 与 notify() 交叉输出 A1B2C3...
* 公众号:taishangcode
*/
public class SynchronizedPrint {
static Object obj = new Object();
static volatile int n = 1;
public static void main(String[] args) {
new Thread(() -> {
try {
synchronized (obj) {
for (char c = 'A'; c < 'J'; c++) {
System.out.print(c);
if (n != 1) { // 首次不通知
obj.notify();
}
obj.wait();
}
obj.notify(); // 唤醒最后阻塞的线程
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
synchronized (obj) {
for (int i = 1; i < 10; i++) {
if (n == 1) {
obj.wait(10);
n++;
}
System.out.print(i);
obj.notify();
obj.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
方式4、Lock 与 Condition
package taishangcode;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Description:使用 Lock 与 Condition 输出 A1B2C3...
*/
public class ConditionPrint {
static Lock lock = new ReentrantLock();
static Condition c1 = lock.newCondition();
static Condition c2 = lock.newCondition();
static volatile int n = 1;
public static void main(String[] args) {
new Thread(() -> {
try {
lock.lock();
for (char c = 'A'; c < 'J'; c++) {
System.out.print(c);
if (n != 1) { //第一次不通知
c2.signal();
}
c1.await();
}
c2.signal(); // 唤醒最后阻塞的线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
try {
lock.lock();
for (int i = 1; i < 10; i++) {
if (n == 1) { // 利用超时机制使该线程在上个线程之后进行
c2.await(10, TimeUnit.MILLISECONDS);
n++;
}
System.out.print(i);
c1.signal();
c2.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
}
}
方式5、CyclicBarrier
package taishangcode;
import java.util.concurrent.CyclicBarrier;
/**
* @Description:使用 CyclicBarrier 交叉输出 A1B2C3...
* 公众号:taishangcode
*/
public class CyclicBarrierPrint {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
new Thread(() -> {
try {
for (char c = 'A'; c < 'J';) {
System.out.print(c);
cyclicBarrier.await();
c++;
cyclicBarrier.await();
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
for (int i = 1; i < 10;) {
cyclicBarrier.await();
System.out.print(i);
cyclicBarrier.await();
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
方式6、TransferQueue
package taishangcode;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
/**
* @Description:用 LinkedTransferQueue 交叉输出 A1B2C3...
*/
public class TransferQueuePrint {
static TransferQueue<String> transferQueue = new LinkedTransferQueue<String>();
public static void main(String[] args) {
new Thread(() -> {
try {
for (char c = 'A'; c < 'J'; c++) {
transferQueue.transfer(c+"");
System.out.print(transferQueue.take());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
for (int i = 1; i < 10; i++) {
System.out.print(transferQueue.take());
transferQueue.transfer(i+"");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
方式7、BlockingDeque
package taishangcode;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
/**
* @Description:使用 Lock 与 Condition 输出 A1B2C3...
*/
public class BlockingDequePrint {
// take() 和 put() 均是阻塞方法,程序顺序决定了这里会交叉输出
//当然保险起见,这里两个双端队列初始化时可指定容量为 1
static BlockingDeque<String> bd1 = new LinkedBlockingDeque<String>();
static BlockingDeque<String> bd2 = new LinkedBlockingDeque<String>();
public static void main(String[] args) {
new Thread(() -> {
try {
for (char c = 'A'; c < 'J'; c++) {
bd1.put(c+"");
System.out.print(bd2.take());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
for (int i = 1; i < 10; i++) {
System.out.print(bd1.take());
bd2.put(i+"");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
八、总结
除方式一利用死循环的特点实现除外,
wait() 与 notify() 调用的是 native 方法,即 JVM 底层进行的实现。
LinkedBlockingDeque、CyclicBarrier 源码中都用到了 Lock 与 Condition。
而 Condition 与 LinkedTransferQueue 的源码中又都用到了 LockSupport 类的操作。
LockSupport 的 park() 和 unpark() 又都是由 sun.misc.Unsafe 类提供的。