JUC并发编程
1.什么是JUC?
java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks 这三个操作线程的包包(简称JUC )。
并发编程的本质:充分利用CPU的资源!
2.Lock锁
锁是一种用于解决安全的机制,java中锁一般都是锁的对象,或者锁的是需要进行修改的属性或方法。
1) 传统:synchronized
public class SynchronizedDemo { public static void main(String[] args) { //多线程操作 Ticket ticket = new Ticket(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"A").start(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"B").start(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"C").start(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"D").start(); } } class Ticket{ private static int number=50; //卖票方式 public synchronized void sale(){ if (number>0){ System.out.println(Thread.currentThread().getName() + "购买了第" + (number--) + "张票,剩余票数为"+number); } } }
2) Lock接口
Lock锁是一个接口,其所有的实现类有:
ReentrantLock(可重入锁)
ReentrantReadWriteLock.ReadLock(可重入读锁)
ReentrantReadWriteLock.WriteLock(可重入写锁)
/** * 公平锁:十分公平,必须先来后到~ * 非公平锁:十分不公平,可以插队(默认为非公平锁). */ public class LockDemo { public static void main(String[] args) { //多线程操作 Ticket2 ticket = new Ticket2(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"A").start(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"B").start(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"C").start(); new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"D").start(); } } //资源类 class Ticket2{ private static int number=50; Lock lock= new ReentrantLock(false);//设置true——公平锁,设置false-非公平锁,默认为非公平锁 //卖票方式 public void sale(){ //加锁 lock.lock(); try { if (number>0){ System.out.println(Thread.currentThread().getName() + "购买了第" + (number--) + "张票,剩余票数为"+number); } } finally { //解锁 lock.unlock(); } } }
synchronized锁与Lock锁的区别:
- synchronized是内置的java关键字,而Lock是一个接口 。
- synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁。
- synchronized会自动释放锁,Lock必须要手动释放锁!否则会造成死锁.
- synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等);Lock锁就不会一直等下
- synchronized 可重入锁 不可以中断的 非公平,Lock 可重入锁 可以判断的 非公平(可以设置)
- synchronized 适合锁少量同步代码,Lock适合锁大量同步代码.
3.生产者和消费者问题
1)synchronized版
public class TestProAndCon1 { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ try { for (int i = 0; i < 20; i++) { data.Production(); } } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); new Thread(()->{ try { for (int i = 0; i < 20; i++) { data.Consumption(); } } catch (InterruptedException e) { e.printStackTrace(); } },"B").start(); } } class Data { private int number = 0; //生产 public synchronized void Production() throws InterruptedException { while (number != 0) { this.wait(); } number++; System.out.println(Thread.currentThread().getName()+"线程生产+1,目前总数"+number); this.notifyAll(); } //消费 public synchronized void Consumption() throws InterruptedException { while (number==0){ this.wait(); } number--; System.out.println(Thread.currentThread().getName()+"线程消费-1,目前总数"+number); this.notifyAll(); } }
2)JUC并发版
public class TestProAndCon2{ public static void main(String[] args) { Data01 data01 = new Data01(); new Thread(() -> { for (int i = 0; i < 10; i++) { data01.printA(); } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data01.printB(); } }, "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data01.printC(); } }, "C").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data01.printD(); } }, "D").start(); } } class Data01 { //可重入锁 private final Lock lock = new ReentrantLock(); //监视器 private final Condition condition1 = lock.newCondition(); private final Condition condition2 = lock.newCondition(); private final Condition condition3 = lock.newCondition(); private final Condition condition4 = lock.newCondition(); //物品数量 private int count = 0; //生产 public void printA() { lock.lock(); try { while (count != 0) { //等待 condition1.await(); } count++; System.out.println(Thread.currentThread().getName() + "生产物品数量+1物品还剩" + count + "个,通知B消费"); //唤醒B condition2.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } //消费 public void printB() { lock.lock(); try { while (count <= 0) { condition2.await(); } count--; System.out.println(Thread.currentThread().getName() + "消费物品数量-1物品还剩" + count + "个,通知C生产"); //通知C condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } //生产 public void printC() { try { lock.lock(); while (count != 0) { //等待 condition3.await(); } count++; System.out.println(Thread.currentThread().getName() + "生产物品数量+1物品还剩" + count + "个,通知D消费"); //唤醒D condition4.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } //消费 public void printD() { lock.lock(); try { while (count <= 0) { condition4.await(); } count--; System.out.println(Thread.currentThread().getName() + "消费物品数量-1物品还剩" + count + "个,通知A生产"); //通知A condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
4.如何判断锁的是什么?
1)场景一:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗? //不需要,因为doOther方法没有使用synchronized修饰 public class Exam01 { public static void main(String[] args) { MyClass myClass=new MyClass(); Thread t1=new MyThread(myClass); Thread t2=new MyThread(myClass); t1.setName("t1"); t2.setName("t2"); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } } class MyThread extends Thread{ private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } public void run() { if(Thread.currentThread().getName().contentEquals("t1")) { mc.doSome(); } if(Thread.currentThread().getName().contentEquals("t2")) { mc.doOther(); } } } class MyClass{ public synchronized void doSome() { System.out.println("doSome begin"); try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("doSome over"); } public void doOther() { System.out.println("doOther Begin"); System.out.println("doOther Over"); } }
2)场景二:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗? //需要,加了synchronized,并且锁的是同一个对象 myClass public class Exam02 { public static void main(String[] args) { MyClass myClass=new MyClass(); Thread t1=new MyThread(myClass); Thread t2=new MyThread(myClass); t1.setName("t1"); t2.setName("t2"); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } } class MyThread extends Thread{ private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } public void run() { if(Thread.currentThread().getName().contentEquals("t1")) { mc.doSome(); } if(Thread.currentThread().getName().contentEquals("t2")) { mc.doOther(); } } } class MyClass{ public synchronized void doSome() { System.out.println("doSome begin"); try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("doSome over"); } public synchronized void doOther() { System.out.println("doOther Begin"); System.out.println("doOther Over"); } }
3)场景三:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗? //不需要,加了synchronized,但锁的是不同的对象 myClass1和myClass2 public class Exam03 { public static void main(String[] args) { MyClass myClass1=new MyClass(); MyClass myClass2=new MyClass(); Thread t1=new MyThread(myClass1); Thread t2=new MyThread(myClass2); t1.setName("t1"); t2.setName("t2"); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } } class MyThread extends Thread{ private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } public void run() { if(Thread.currentThread().getName().contentEquals("t1")) { mc.doSome(); } if(Thread.currentThread().getName().contentEquals("t2")) { mc.doOther(); } } } class MyClass{ public synchronized void doSome() { System.out.println("doSome begin"); try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("doSome over"); } public synchronized void doOther() { System.out.println("doOther Begin"); System.out.println("doOther Over"); } }
4)场景四:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗? //需要,静态方法上加synchronized 是类锁,不管你创建了几个对象,类锁只有1把 public class Exam04 { public static void main(String[] args) { MyClass mc1 = new MyClass(); MyClass mc2 = new MyClass(); Thread t1 = new MyThread(mc1); Thread t2 = new MyThread(mc2); t1.setName("t1"); t2.setName("t2"); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }//保证t1先执行 t2.start(); } } class MyThread extends Thread{ private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } public void run() { if(Thread.currentThread().getName().contentEquals("t1")) { mc.doSome(); } if(Thread.currentThread().getName().contentEquals("t2")) { mc.doOther(); } } } class MyClass{ public synchronized static void doSome() { System.out.println("doSome begin"); try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("doSome over"); } public synchronized static void doOther() { System.out.println("doOther Begin"); System.out.println("doOther Over"); } }
5)场景五:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗? //不需要,因为一个锁的是Class类,一个锁的是对象 public class Exam05 { public static void main(String[] args) { MyClass mc1 = new MyClass(); MyClass mc2 = new MyClass(); Thread t1 = new MyThread(mc1); Thread t2 = new MyThread(mc2); t1.setName("t1"); t2.setName("t2"); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }//保证t1先执行 t2.start(); } } class MyThread extends Thread{ private MyClass mc; public MyThread(MyClass mc) { this.mc = mc; } public void run() { if(Thread.currentThread().getName().contentEquals("t1")) { mc.doSome(); } if(Thread.currentThread().getName().contentEquals("t2")) { mc.doOther(); } } } class MyClass{ public synchronized static void doSome() { System.out.println("doSome begin"); try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("doSome over"); } public static void doOther() { System.out.println("doOther Begin"); System.out.println("doOther Over"); } }
小节:
- 如果是非静态的synchronized锁的是调用它的对象!
- 如果是静态的synchronized锁的是Class类模板!
5.集合安全问题
1)List
在单线程中ArrayList是安全的,但在并发中ArrayList是不安全的。如下:多线程向ArrayList中插入数据。
public class TestList { public static void main(String[] args) { test1(); //test2(); //test3(); } public static void test1(){ List<String> list = new ArrayList<>(); for (int i = 1; i <= 100; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+list); }, String.valueOf(i)).start(); } } public static void test2(){ List<String> list = new CopyOnWriteArrayList<>(); for (int i = 1; i <= 100; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+list); },String.valueOf(i)).start(); } } public static void test3(){ List<String> oldlist = new ArrayList<>(); List<String> list= Collections.synchronizedList(oldlist); for (int i = 1; i <= 100; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+list); }, String.valueOf(i)).start(); } } }
运行test1()方法,就出现了ConcurrentModificationException(并发修改异常)
解决方案:
- 使用java并发包concurrent下的CopyOnWriteArrayList!见test2()方法。
- 使用Collections.synchronizedList()方法将集合转换为安全的集合。见test3()方法。
2)Map
同样HashMap在并发情况下也是也是不安全的。
public class TestMap { public static void main(String[] args) { test1(); //test2(); //test3(); } public static void test1(){ HashMap<Object, Object> hashMap = new HashMap<>(); for (int i = 0; i < 100; i++) { new Thread(()->{ hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+hashMap); }).start(); } } public static void test2(){ ConcurrentHashMap<Object, Object> hashMap = new ConcurrentHashMap<>(); for (int i = 0; i < 100; i++) { new Thread(()->{ hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+hashMap); }).start(); } } public static void test3(){ HashMap<Object, Object> oldHashMap = new HashMap<>(); Map<Object, Object> hashMap = Collections.synchronizedMap(oldHashMap); for (int i = 0; i < 100; i++) { new Thread(()->{ hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+hashMap); }).start(); } } }
运行test1()方法,也会出现了ConcurrentModificationException(并发修改异常)。
解决方案:
- 使用java并发包concurrent下的ConcurrentHashMap类。见test2()方法。
- 使用Collections.synchronizedMap()把Map转换为安全的集合。见test3()方法。
2)Set
同样HashSet在并发情况下也是也是不安全的。
public class TestSet { public static void main(String[] args) { test1(); //test2(); //test3(); } //同理可得:ConcurrentModificationException(并发修改异常) public static void test1(){ Set<String> set = new HashSet<>(); for (int i = 1; i <= 100; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+set); },String.valueOf(i)).start(); } } public static void test2(){ Set<String> set = new ConcurrentSkipListSet<>(); for (int i = 1; i <= 100; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+set); },String.valueOf(i)).start(); } } public static void test3(){ Set<String> oldSet = new HashSet<>(); Set<String> set = Collections.synchronizedSet(oldSet); for (int i = 1; i <= 100; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(Thread.currentThread().getName()+"----"+set); },String.valueOf(i)).start(); } } }
运行test1()方法,也会出现了ConcurrentModificationException(并发修改异常)。
解决方案:
- 使用java并发包concurrent下的ConcurrentSkipListSet类!见test()方法。
- 使用Collections.synchronizedSet()方法将集合转换为安全的set集合!见test3()方法。
6.读写锁 ReadWriteLock
ReadWriteLock叫做读写锁是java.util.concurrent.locks包下的,它的写锁一次只被一个线程共享操作,读锁则可以被所有线程共享,所以又称为独占锁(写锁)和共享锁(读锁)。
public class TestReadWriteLock { public static void main(String[] args) { MyCacheLock myCache = new MyCacheLock(); //MyCache myCache = new MyCache(); //存写的线程 for (int i = 0; i < 5; i++) { int temp=i; new Thread(()->{ myCache .put(String.valueOf(temp),temp); },String.valueOf(i)).start(); } //读写的线程 for (int i = 0; i < 5; i++) { int temp=i; new Thread(()->{ myCache.get(String.valueOf(temp)); },String.valueOf(i)).start(); } } } /** * 自定义缓存 */ class MyCache{ private volatile HashMap<String,Object> map=new HashMap<>(); //存写 public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"正在写入"+key+":"+value); map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入完毕"); } //读取 public void get(String key){ System.out.println(Thread.currentThread().getName()+"正在读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取完毕"); } } /** * 加锁的自定义缓存 */ class MyCacheLock{ private volatile HashMap<String,Object> map=new HashMap<>(); //读写锁,更加细腻度的控制 private final ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); //存写 (存写的时候只希望有一个线程进行) public void put(String key,Object value){ //上一把写锁 lock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"正在写入"+key+":"+value); map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入完毕"); } finally { lock.writeLock().unlock(); } } //读取 (所有人都可以读) public void get(String key){ lock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"正在读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取完毕"); } finally { lock.readLock().unlock(); } } }
小结:
独占锁(写锁):一次只能被一个线程占用 。
共享锁(读锁):多线程可以同时占有。
- 读-读:可以共存!
- 读-写:不能共存!
- 写-写:不能共存!
7.volatile
volatile是java虚拟机提供的轻量级的同步机制。
- 保证可见性
- 不保证原子性
- 禁止指令重排
保证可见性
public class TestJMM1 { //加入volatile可以保证程序的可见性 private static volatile boolean flag=true; public static void main(String[] args) throws InterruptedException { new Thread(()->{ while (flag){ System.out.println("hello"); } }).start(); System.out.println("停顿!"); TimeUnit.SECONDS.sleep(1); flag=false; System.out.println("修改完毕!"); } }
不保证原子性
public class TestJMM2 { //volatile不保证原子性 private volatile static int flag=0; public static void add(){ flag++; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ Thread.yield(); } System.out.println(Thread.currentThread().getName()+":"+flag); } }
运行效果:
使用原子类
public class TestJMM3 { private volatile static AtomicInteger flag=new AtomicInteger(); public static void add(){ flag.getAndIncrement();//进行+1操作 } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ Thread.yield(); } System.out.println(Thread.currentThread().getName()+":"+flag); } }
8.阻塞队列 BlockingQueue
阻塞队列的特点:
- 在队列为空的时候 ,获取元素的线程会等待队列变为非空。
- 当队列满时 ,存储元素的线程会等待队列可用。
BlockingQueue的实现类有:
ArrayBlockingQueue : 一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue : 一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue : 一个支持优先级排序的无界阻塞队列。
DelayQueue: 一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue: 一个不存储元素的阻塞队列。
LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。
使用队列
四组API:
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞 等待 | 超时等待 |
添加 | add | offer | put | offer(等待时间,等待单位) |
移除 | remove | poll | take | poll(等待时间,等待单位) |
检测队首元素 | element | peek | peek | peek |
ArrayBlockingQueue -基于数组结构的阻塞队列
public class TestBlockingQueue { public static void main(String[] args) throws Exception{ //test1(); //test2(); //test3(); test4(); } //会抛出异常 public static void test1(){ ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3); System.out.println(objects.add("a")); System.out.println(objects.add("b")); System.out.println(objects.add("c")); //IllegalStateException: Queue full(队列已满异常!) //System.out.println(objects.add("d")); System.out.println("==================="); System.out.println(objects.remove()); System.out.println(objects.remove()); System.out.println(objects.remove()); //NoSuchElementException(队列为空异常!) //System.out.println(objects.remove()); } //有返回值.不抛出异常 public static void test2(){ ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3); System.out.println(objects.offer("a")); System.out.println(objects.offer("b")); System.out.println(objects.offer("c")); System.out.println(objects.offer("d"));//false System.out.println("==================="); System.out.println(objects.poll()); System.out.println(objects.poll()); System.out.println(objects.poll()); System.out.println(objects.poll());//null } //等待,阻塞(一直阻塞) public static void test3() throws InterruptedException { ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3); arrayBlockingQueue.put("a"); arrayBlockingQueue.put("b"); arrayBlockingQueue.put("c"); //arrayBlockingQueue.put("d");//队列没有位置了则会等待 System.out.println(arrayBlockingQueue.take()); System.out.println(arrayBlockingQueue.take()); System.out.println(arrayBlockingQueue.take()); //System.out.println(arrayBlockingQueue.take()); //队列没有元素了则会等待 } //超时等待 public static void test4() throws InterruptedException { ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3); System.out.println(arrayBlockingQueue.offer("a")); System.out.println(arrayBlockingQueue.offer("b")); System.out.println(arrayBlockingQueue.offer("c")); System.out.println(arrayBlockingQueue.offer("d", 2, TimeUnit.SECONDS));//等待超过两秒就退出 System.out.println(arrayBlockingQueue.element()); System.out.println(arrayBlockingQueue.peek()); System.out.println("==================="); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));//等待超过两秒就退出 //System.out.println(arrayBlockingQueue.element()); //检测队首元素,没有抛出异常 System.out.println(arrayBlockingQueue.peek()); //检测队首元素,没有返回null } }
SynchronousQueue
特点: 没有容量,当进去一个元素必须等待取出才能再次进入元素!
public class TestSynchronousQueue { public static void main(String[] args) throws InterruptedException { SynchronousQueue<Object> objects = new SynchronousQueue<>(); new Thread(()->{ try { objects.put("a"); System.out.println(Thread.currentThread().getName()+"put a"); objects.put("b"); System.out.println(Thread.currentThread().getName()+"put b"); objects.put("c"); System.out.println(Thread.currentThread().getName()+"put c"); } catch (InterruptedException e) { e.printStackTrace(); } },"T1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"->"+objects.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"->"+objects.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"->"+objects.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"T2").start(); } }
常用辅助类
1.CountDownLatch类
使一个线程等待其他线程各自执行完毕后再执行。
常用方法:
1)countDownLatch.countDown();//表示数量-1
2)countDownLatch.await(); //等待计数器归零在往下执行!
当每次有线程调用 countDown()方法则数量减一,假设数量变为0,await()方法就会被唤醒继续执行。
//计数器 public class TestCountDownLatch { public static void main(String[] args) throws InterruptedException { //总数是6 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; i < 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName()+"走了"); countDownLatch.countDown();//表示数量-1 },String.valueOf(i)).start(); } //等待计数器归零在往下执行! countDownLatch.await(); System.out.println("关门!"); } }
2.CyclicBarrier类
用于对多个线程任务进行同步执行。
主要方法:await 在所有线程任务都到达之前,线程任务都是阻塞状态。
public class TestCyclicBarrier { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("选手已准备就位!"); }); for (int i=1;i<=7;i++){ int atI = i; new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+" " + atI +"号选手准备好了!"); cyclicBarrier.await(); //加法计数 等待 } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },"线程"+i).start(); } } }
3.Semaphore类
信号量,在信号量定义两种操作acquire和release。
常用方法:
1)semaphore.acquire(); //获得
2)semaphore.release(); //释放
当一个线程调用acquire操作,它通过成功获取信号量(信号量-1),有阻塞,直到有线程释放信号量,或者超时。调用release操作,实际上将信号量的值+1,然后唤醒等待的线程。可以实现多个共享资源的互斥使用,并发限流,控制最大线程数。
/** * Semaphore semaphore = new Semaphore(3); * 设置限流数量 */ public class TestSemaphore { public static void main(String[] args) { //线程数量,停车位,限流的时候会用 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 8; i++) { new Thread(()->{ //acquire() 得到 //release() 释放 try { //得到车位 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"得到车位!"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"停车两秒后离开车位!"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //离开车位 semaphore.release(); } }).start(); } } }