线程同步一致性经典面试题
如题,有两个线程A、B,A线程向一个集合(List<String>) 里面依次添加元素“abc”字符串, 一共添加十次,当添加到第五次的时候,希望B线程能够收到A线程的通知,然后B线程执行相关的业务操作,我们应该如何进行设计?
实现方式1:采用volatile关键字
- Volatile概念:Volatile关键字的主要作用是使共享变量在多个线程间可见。
- 作用:
- 在多线程间可以进行变量的变更,使得线程间进行数据的共享可见
- 阻止指令重排序,happens-before
import java.util.ArrayList; import java.util.List; public class AddList { private volatile static List<String> list = new ArrayList<>(); private static void add(){ list.add("abc"); } private static int getSize(){ return list.size(); } public static void main(String[] args){ Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ add(); System.out.println("线程" + Thread.currentThread().getName() + "执行了" + i + "次"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { while(true) { if (getSize() == 5) { System.out.println("接收通知"+list.get(4)); throw new RuntimeException(); } } } },"t2"); t2.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } }
实现方式2:使用wait / notify 方法实现线程间的通信,wait 和 notify 必须配合 synchronized 关键字使用
import java.util.ArrayList; import java.util.List; public class AddList { private static List<String> list = new ArrayList<>(); private static void add(){ list.add("abc"); } private static int getSize(){ return list.size(); } public static void main(String[] args){ Object lock = new Object(); Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ synchronized (lock) { add(); System.out.println("线程" + Thread.currentThread().getName() + "执行了" + i + "次"); if (getSize() == 5) { lock.notify(); System.out.println("发送通知" + list.get(4)); } } } } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { while(true) { synchronized (lock) { if (getSize() != 5) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("接收通知"+list.get(4)); throw new RuntimeException(); } } },"t2"); t2.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } }
实现方式3:使用CountDownLatch用于监听某些初始化操作,并且线程进行阻塞,等初始化执行完毕后,通知主线程继续工作执行
import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.SynchronousQueue; public class AddList { private static List<String> list = new ArrayList<>(); private static void add(){ list.add("abc"); } private static int getSize(){ return list.size(); } public static void main(String[] args){ final CountDownLatch latch = new CountDownLatch(1); Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ add(); System.out.println("线程"+Thread.currentThread().getName()+"执行了"+i+"次"); if(getSize()==5){ latch.countDown(); System.out.println("发送通知"+list.get(4)); } } } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { while(true) { if (getSize() != 5) { try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("接收通知"+list.get(4)); throw new RuntimeException(); } } },"t2"); t2.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } }
实现方式4:采用SynchronousQueue,一种没有缓冲的队列,生产者产生的数据直接会被消费者获取并消费
import java.util.ArrayList; import java.util.List; import java.util.concurrent.SynchronousQueue; public class AddList { private static List<String> list = new ArrayList<>(); private static void add(){ list.add("abc"); } private static int getSize(){ return list.size(); } public static void main(String[] args){ SynchronousQueue<String> queue = new SynchronousQueue<>(); Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ add(); System.out.println("线程"+Thread.currentThread().getName()+"执行了"+i+"次"); if(getSize()==5){ queue.add(list.get(i)); System.out.println("发送通知"+list.get(4)); } } } },"t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { if (queue.take() != null) { System.out.println("接到通知"+list.get(4)); } } catch (InterruptedException e) { e.printStackTrace(); } } },"t2"); t2.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } }