一.题目
分析:
这道题考的是多线程的之间通信,下面主要使用几种思路去解决这个问题,比如阻塞,锁,自旋这些.
第一种思路-阻塞
总体思路就是在一个线程打印的时候,想办法将另一个线程阻塞.从而进行交替打印.
1.使用LockSupport进行阻塞:
static char[] num=new char[]{'1','2','3','4','5','6'};
static char[] letter=new char[]{'A','B','C','D','E','F'};
static Thread t2 =null;
static Thread t1 =null;
public static void main(String[] args) {
t1 = new Thread(() -> {
for (char curChar : num) {
LockSupport.park();
System.out.print(curChar);
LockSupport.unpark(t2);
}
});
t2 = new Thread(() -> {
for (char curChar : letter) {
System.out.print(curChar);
LockSupport.unpark(t1);
LockSupport.park();
}
});
t1.start();
t2.start();
}
2.使用阻塞队列进行阻塞.
static char[] num=new char[]{'1','2','3','4','5','6'};
static char[] letter=new char[]{'A','B','C','D','E','F'};
static String str="flag";
public static void main(String[] args) {
//使用阻塞队列的阻塞特性,进行线程的控制
//其实底层还用的是Reentrantlok
LinkedBlockingQueue<String> blockingQueueNum = new LinkedBlockingQueue<>(1);
LinkedBlockingQueue<String> blockingQueueLetter = new LinkedBlockingQueue<>(1);
try {
blockingQueueNum.put(str);
blockingQueueLetter.put(str);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
for (char curChar : num) {
try {
blockingQueueNum.put(str);
System.out.print(curChar);
blockingQueueLetter.poll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
for (char curChar : letter) {
try {
System.out.print(curChar);
blockingQueueNum.poll();
blockingQueueLetter.put(str);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
第二种思路-自旋
主要就是给一个条件,只有在符合这个条件时才执行打印操作,否则就一直循环循环,也就是自旋.
自旋在特定的场景下是适合的,比如说业务操作执行时间很短,线程数也不多,自旋两圈就能够获得执行代码的条件.
static char[] num=new char[]{'1','2','3','4','5','6'};
static char[] letter=new char[]{'A','B','C','D','E','F'};
//true 输出字母, false 输出数字
static volatile boolean flag=false;
public static void main(String[] args) {
new Thread(() -> {
for (char curChar : num) {
while(!flag){} //不断自旋
System.out.print(curChar);
flag=false;
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (char curChar : letter) {
while(flag){} //不断自旋
System.out.print(curChar);
flag=true;
}
}
}).start();
}
第三种思路-锁+阻塞
这个也是最常见的解法,两个线程共用一个锁,获取锁之后才执行打印操作,也是在获取锁之后进行线程的阻塞唤醒.
1.使用synchronized关键字
static char[] num=new char[]{'1','2','3','4','5','6'};
static char[] letter=new char[]{'A','B','C','D','E','F'};
static Object lock=new Object();
public static void main(String[] args) {
new Thread(() -> {
for (char curChar : num) {
synchronized (lock){
System.out.print(curChar);
try {
lock.notify();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.notify();
}
}
}).start();
new Thread(() -> {
for (char curChar : letter) {
synchronized (lock){
System.out.print(curChar);
try {
lock.notify();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.notify();
}
}
}).start();
}
2.使用ReentrantLock.
static char[] num=new char[]{'1','2','3','4','5','6'};
static char[] letter=new char[]{'A','B','C','D','E','F'};
static ReentrantLock lock=new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(() -> {
for (char curChar : num) {
try{
lock.lock();
System.out.print(curChar);
condition.signal();
condition.await();
}catch (Exception e){
e.printStackTrace();
}finally {
condition.signal();
lock.unlock();
}
}
}).start();
new Thread(() -> {
for (char curChar : letter) {
try{
lock.lock();
System.out.print(curChar);
condition.signal();
condition.await();
}catch (Exception e){
e.printStackTrace();
}finally {
condition.signal();
lock.unlock();
}
}
}).start();
}
总结:
在上面几种解法中,首先推荐的就是第一种,也就是使用LockSupport,因为代码很简洁,性能也可以,在ReenTrantLock或阻塞队列中的底层阻塞方法也是调用的LockSupport中的方法,自旋方式是仅在某种条件下适合的.