不知道最近有没有小伙伴去面试,替输今天了不起回想到了早期去面试遇到的多线一个多线程面试问题。
面试问题是程交出一个笔试题:
两个线程依次交替输出A~Z,1到26,替输形如A1B2C3D4...
当时的多线我还很菜,用了原生的程交出线程,借助wait和notify方法实现。替输
伙伴们你们也可以先暂停,多线自己思考下用什么方式来实现。程交出
今天了不起和伙伴们一起来基于JDK1.8进行实现方式的替输探索,请看下文。多线
wait()方法会使当前线程释放锁,程交出并进入等待状态,替输直到以下情况之一发生:
notify()方法用于唤醒一个正在等待的线程,使其从wait()方法中返回。
结合一个出让等待的机制,就这样交替实现。
public class T06_00_sync_wait_notify { public static void main(String[] args) { final Object o = new Object(); char[] aI = "1234567".toCharArray(); char[] aC = "ABCDEFG".toCharArray(); new Thread(()->{ synchronized (o) { for(char c : aI) { System.out.print(c); try { o.notify(); o.wait(); //让出锁 } catch (InterruptedException e) { e.printStackTrace(); } } o.notify(); //必须,否则无法停止程序 } }, "t1").start(); new Thread(()->{ synchronized (o) { for(char c : aC) { System.out.print(c); try { o.notify(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } o.notify(); } }, "t2").start(); }}
运行结果:
图片
思考:伙伴们,如果我想保证t2在t1之前打印,也就是说保证首先输出的是A而不是1,这个时候该如何做?
CountDownLatch是Java多线程中的一个同步工具类,它可以让一个或多个线程等待其他线程完成操作后再继续执行。
具体来说,CountDownLatch有两个主要方法:
public class T07_00_sync_wait_notify { private static CountDownLatch latch = new CountDownLatch(1); public static void main(String[] args) { final Object o = new Object(); char[] aI = "1234567".toCharArray(); char[] aC = "ABCDEFG".toCharArray(); new Thread(()->{ try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (o) { for(char c : aI) { System.out.print(c); try { o.notify(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } o.notify(); } }, "t1").start(); new Thread(()->{ synchronized (o) { for(char c : aC) { System.out.print(c); latch.countDown(); try { o.notify(); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } o.notify(); } }, "t2").start(); }}
运行结果:
图片
我们可以通过ReentrantLock获取条件锁,通过它提供的方法来实现。
具体来说,ReentrantLock的Condition接口提供了以下三个方法:
import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class T08_00_lock_condition { public static void main(String[] args) { char[] aI = "1234567".toCharArray(); char[] aC = "ABCDEFG".toCharArray(); Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(()->{ try { lock.lock(); for(char c : aI) { System.out.print(c); condition.signal(); condition.await(); } condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }, "t1").start(); new Thread(()->{ try { lock.lock(); for(char c : aC) { System.out.print(c); condition.signal(); condition.await(); } condition.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }, "t2").start(); }}
运行结果:
图片
Condition本质是锁资源上不同的等待队列,我们也可以获取不同的等待队列来实现。
import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class T09_00_lock_condition { public static void main(String[] args) { char[] aI = "1234567".toCharArray(); char[] aC = "ABCDEFG".toCharArray(); Lock lock = new ReentrantLock(); Condition conditionT1 = lock.newCondition(); Condition conditionT2 = lock.newCondition(); new Thread(()->{ try { lock.lock(); for(char c : aI) { System.out.print(c); conditionT2.signal(); conditionT1.await(); } conditionT2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }, "t1").start(); new Thread(()->{ try { lock.lock(); for(char c : aC) { System.out.print(c); conditionT1.signal(); conditionT2.await(); } conditionT1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }, "t2").start(); }}
TransferQueue是Java并发包中的一个阻塞队列,它可以用于多线程之间的数据交换和同步。
LinkedTransferQueue继承自TransferQueue,并且还可以支持异步操作。
图片
LinkedTransferQueue的take()方法和transfer()方法都是用于从队列中取出元素的方法,但它们的使用场景和行为有所不同。
take()方法是一个阻塞方法,它会一直阻塞直到队列中有可用元素,才将队列中的元素取出并返回。
transfer()方法也是一个阻塞方法,它会将指定的元素插入到队列中,并等待另一个线程从队列中取出该元素。如果队列中没有等待的线程,则当前线程会一直阻塞,直到有其他线程从队列中取走该元素为止。
那么我们就利用这一点它必须要另外一个线程来取进而实现把值交替输出。
import java.util.concurrent.LinkedTransferQueue;import java.util.concurrent.TransferQueue;public class T13_TransferQueue { public static void main(String[] args) { char[] aI = "1234567".toCharArray(); char[] aC = "ABCDEFG".toCharArray(); TransferQueue<Character> queue = new LinkedTransferQueue<Character>(); new Thread(()->{ try { for (char c : aI) { System.out.print(queue.take()); queue.transfer(c); } } catch (InterruptedException e) { e.printStackTrace(); } }, "t1").start(); new Thread(()->{ try { for (char c : aC) { queue.transfer(c); System.out.print(queue.take()); } } catch (InterruptedException e) { e.printStackTrace(); } }, "t2").start(); }}
运行结果:
图片
LockSupport是Java并发包中的一个工具类,它可以用于线程的阻塞和唤醒。
你可以把它类比成Object的wait()和notify()方法,但LockSupport是比它们更加灵活和可控的。
LockSupport提供了park()和unpark()方法:
当一个线程调用park()方法时,它会被阻塞,直到另一个线程调用该线程的unpark()方法才会被唤醒。
如果调用unpark()方法时,该线程还没有调用park()方法,则该线程调用park()方法时不会被阻塞,可以直接返回。
import java.util.concurrent.locks.LockSupport;//Locksupport park 当前线程阻塞(停止)//unpark(Thread t)public class T02_00_LockSupport { static Thread t1 = null, t2 = null; public static void main(String[] args) throws Exception { char[] aI = "1234567".toCharArray(); char[] aC = "ABCDEFG".toCharArray(); t1 = new Thread(() -> { for(char c : aI) { System.out.print(c); LockSupport.unpark(t2); //叫醒T2 LockSupport.park(); //T1阻塞 } }, "t1"); t2 = new Thread(() -> { for(char c : aC) { LockSupport.park(); //t2阻塞 System.out.print(c); LockSupport.unpark(t1); //叫醒t1 } }, "t2"); t1.start(); t2.start(); }}
运行结果:
图片
创建一个枚举类ReadyToRun,利用while(true)死等和枚举类指向对象不同作标志位交替输出。
public class T03_00_cas { enum ReadyToRun { T1, T2} static volatile ReadyToRun r = ReadyToRun.T1; public static void main(String[] args) { char[] aI = "1234567".toCharArray(); char[] aC = "ABCDEFG".toCharArray(); new Thread(() -> { for (char c : aI) { while (r != ReadyToRun.T1) { } System.out.print(c); r = ReadyToRun.T2; } }, "t1").start(); new Thread(() -> { for (char c : aC) { while (r != ReadyToRun.T2) { } System.out.print(c); r = ReadyToRun.T1; } }, "t2").start(); }}
运行结果:
图片
好了,关于这个面试题的解法了不起暂时就想到这6种情况。
这个面试题也是一道经典的多线程面试题,如果你能将这几种情况掌握,定会另面试官刮目相看。
如果你们还有新的方法欢迎和了不起一起探讨研究,毕竟代码是死的人是活的。
责任编辑:武晓燕 来源: Java面试教程 多线程面试工具类(责任编辑:探索)
中国银行(03988.HK)拟赎回全部2.8亿股第二期境内优先股 每股面值人民币100元
七夕最佳礼物之选 Dyson Supersonic甄选礼盒款上线
中盈盛达融资担保(01543.HK)完成发行2.60亿元公司债 票面利率为4.60%
影石Insta360 X3新配件上市 360° 水下全隐形!
售价2999元起 佳能发布掌上Vlog机PowerShot V10