这个问题考察的是对多线程和juc下面某些类的理解,总的来说,思路就是当一个线程t1输出当前字符之后,要阻塞或者等待,让另外一个线程t2输出,然后t2阻塞或者等待,有以下几种思路,废话不多说,直接上代码
1、使用传统的sychronized加锁的方式:
public class OrderPrintSynchronized {
public static String s1 = "123456";
public static String s2 = "ABCDEF";
public static Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
int length = s1.length();
synchronized (lock) {
for (int i = 0; i < length; i++) {
System.err.println(s1.charAt(i));
try {
lock.notify();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.notify();
}
});
Thread t2 = new Thread(() -> {
int length = s2.length();
synchronized (lock) {
for (int i = 0; i < length; i++) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println(s2.charAt(i));
lock.notify();
}
}
});
t2.start();
TimeUnit.SECONDS.sleep(1);
t1.start();
TimeUnit.SECONDS.sleep(1);
}
}
2、使用LockSupport:
public class OrderPrintLockSupport {
public static String s1 = "123456";
public static String s2 = "ABCDEF";
private static Thread t1;
private static Thread t2;
public static void main(String[] args) throws Exception {
t1 = new Thread(() -> {
int length = s1.length();
for (int i = 0; i < length; i++) {
System.err.println(s1.charAt(i));
LockSupport.unpark(t2);
LockSupport.park();
}
});
t2 = new Thread(() -> {
int length = s2.length();
for (int i = 0; i < length; i++) {
LockSupport.park();
System.err.println(s2.charAt(i));
LockSupport.unpark(t1);
}
});
t2.start();
TimeUnit.SECONDS.sleep(1);
t1.start();
TimeUnit.SECONDS.sleep(1);
}
}
3、使用SynchronousQueue和TransferQueue,这两个道理都差不多,都是利用队列只能有一个元素并且这个元素必须要有别的线程去处理,否则当前线程会一直阻塞,这里投机取巧了,让别另外一个线程输出原本该当前线程输出的数据:
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TransferQueue;
public class OrderPrintTransferQueue {
public static String s1 = "123456";
public static String s2 = "ABCDEF";
private static TransferQueue<String> stringSynchronousQueue = new LinkedTransferQueue<>();
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
int length = s1.length();
for (int i = 0; i < length; i++) {
try {
stringSynchronousQueue.put(s1.charAt(i) + "");
System.err.println(stringSynchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
int length = s2.length();
for (int i = 0; i < length; i++) {
try {
System.err.println(stringSynchronousQueue.take());
stringSynchronousQueue.put(s2.charAt(i) + "");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2.start();
TimeUnit.SECONDS.sleep(1);
t1.start();
TimeUnit.SECONDS.sleep(1);
}
}
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class OrderPrintSynchronousQueue {
public static String s1 = "123456";
public static String s2 = "ABCDEF";
private static SynchronousQueue<String> stringSynchronousQueue = new SynchronousQueue<>();
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
int length = s1.length();
for (int i = 0; i < length; i++) {
try {
stringSynchronousQueue.put(s1.charAt(i) + "");
System.err.println(stringSynchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
int length = s2.length();
for (int i = 0; i < length; i++) {
try {
System.err.println(stringSynchronousQueue.take());
stringSynchronousQueue.put(s2.charAt(i) + "");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2.start();
TimeUnit.SECONDS.sleep(1);
t1.start();
TimeUnit.SECONDS.sleep(1);
}
}
4、atomic加自旋:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class OrderPrintAtomic {
public static String s1 = "123456";
public static String s2 = "ABCDEF";
private static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
int length = s1.length();
for (int i = 0; i < length; i++) {
while (!atomicBoolean.get()) {
break;
}
System.err.println(s1.charAt(i));
atomicBoolean.set(true);
}
});
Thread t2 = new Thread(() -> {
int length = s2.length();
for (int i = 0; i < length; i++) {
while (atomicBoolean.get()) {
break;
}
System.err.println(s2.charAt(i));
atomicBoolean.set(false);
}
});
t2.start();
TimeUnit.SECONDS.sleep(1);
t1.start();
TimeUnit.SECONDS.sleep(1);
}
}
5、使用CompletableFuture:
private static void printTwoString() throws ExecutionException, InterruptedException {
String a = "ABCEDF";
String b = "123456";
int len = a.length();
CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
for (int i = 0; i < len; i++) {
int finalI = i;
future = future.thenCompose(ignored -> CompletableFuture.runAsync(() -> {
System.err.print(a.charAt(finalI));
}).thenRun(() -> {
System.err.print(b.charAt(finalI));
}));
//一定要注意这一行必须是在for循环里面,否则可能会出现后面提交的任务先执行,导致输出的顺序不对
future.join();
}
}