多线程进阶=>JUC并发编程
1、线程和进程
线程、进程,如果不能用一句话输出来的技术、不扎实
进程:一个程序,QQ.exe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个线程!
Java默认有几个线程? ---2个线程:main线程,GC线程
线程:开了一个线程Typora,写字自动保存(线程负责)
对于Java而言:Thread、Runnable、Callable
Java真的可以开启线程吗? --->不行
public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } //本地方法,底层的C++,Java无法直接操作硬件 private native void start0();
并发、并行
并发编程:并发,并行
并发:多线程操作同一个资源
CPU一核,模拟出来多条线程,快速交替
并行:多个线程一起行走
CPU多核,多个线程才可以同时执行;线程池
public class Test1 { public static void main(String[] args) { //获取CPU的核数 //CPU密集型,IO密集型 System.out.println(Runtime.getRuntime().availableProcessors()); } }
并发编程的本质:充分利用CPU的资源
2、Lock锁(重点)
传统Synchronized
package lin.demo01; //基本的买票例子 /* * 公司中的开发,真正的多线程开发,降低耦合性 * 线程就是一个单独的资源类,没有任何的附属的操作! * 1、属性,方法 * */ public class SaleTicketDemo01 { 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(); } } //资源类,OOP class Ticket{ // 属性,方法 private int number = 50; //买票的方式 //synchronized:本质就是队列,锁 public synchronized void sale(){ if (number > 0 ) { System.out.println(Thread.currentThread().getName()+"买了第"+ number-- +"票-----剩余"+number+"张票"); } } }
Lock 接口
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队(默认)
package lin.demo01; //基本的买票例子 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * 公司中的开发,真正的多线程开发,降低耦合性 * 线程就是一个单独的资源类,没有任何的附属的操作! * 1、属性,方法 * */ public class SaleTicketDemo02 { public static void main(String[] args) { //并发:多个线程操作同一个类 Ticket2 ticket2 = new Ticket2(); new Thread(()->{ for (int i = 0; i < 60; i++) ticket2.sale2(); },"A").start(); new Thread(()->{ for (int i = 0; i < 60; i++) ticket2.sale2(); },"B").start(); new Thread(()->{ for (int i = 0; i < 60; i++) ticket2.sale2(); },"C").start(); } } //资源类,OOP //Lock锁三部曲 //1、new ReentrantLock() 2、lock.lock();加锁 3、finally=>lock.unlock();解锁 class Ticket2{ // 属性,方法 private int number = 50; Lock lock = new ReentrantLock(); //买票的方式 public void sale2(){ lock.lock();//加锁 try{ //业务代码 if (number > 0 ) { System.out.println(Thread.currentThread().getName()+"买了第"+ number-- +"票-----剩余"+number+"张票"); } }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } }
Synchronized 和 Lock区别
1:、Synchronized 内置的Java关键字,Lock锁是一个Java类
2、Synchronized 无法判断锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,Lock 必须要手动释放锁,如果不释放锁,死锁
4、Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等),Lock锁就不一定等待下去
5、Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)
6、Synchronized 适合少量的代码同步问题,Lock 适合大量的代码同步问题
锁是什么,如何判断锁的是谁!
3、生产者和消费者问题
生产者和消费者 Synchronized 版
package lin.Product_Customer; //生产者与消费者 /* * 线程之间的通信问题:生产者与消费者问题 等待唤醒 通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0 * A num-1 * B num+1 * */ public class Test01 { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); } } //判断等待->业务->通知 class Data{ //数字,资源类 private int num = 0; //+1 public synchronized void increment() throws InterruptedException { if (num != 0) { //等待 this.wait(); } num++; System.out.println(Thread.currentThread().getName()+"=>"+num); //通知其他线程 this.notifyAll(); } //-1 public synchronized void decrement() throws InterruptedException { if (num == 0) { //等待 this.wait(); } num--; System.out.println(Thread.currentThread().getName()+"=>"+num); //通知其他线程 this.notifyAll(); } }
问题存在 ,A,B,C,D四个线程,还安全吗?
解决:if判断改为while循环
JUC版的生产者消费者问题
代码实现:
package lin.Product_Customer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //生产者与消费者 /* * 线程之间的通信问题:生产者与消费者问题 等待唤醒 通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0 * A num-1 * B num+1 * */ public class Test02 { public static void main(String[] args) { Data2 data2 = new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data2.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data2.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data2.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data2.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } } //判断等待->业务->通知 class Data2{ //数字,资源类 private int num = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //condition.await(); 等待 //condition.signalAll(); 唤醒全部 //+1 public void increment() throws InterruptedException { lock.lock(); try { while (num != 0) { //等待 condition.await(); } num++; System.out.println(Thread.currentThread().getName()+"=>"+num); //通知其他线程 condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } //-1 public void decrement() throws InterruptedException { lock.lock(); try { while (num == 0) { //等待 condition.await(); } num--; System.out.println(Thread.currentThread().getName()+"=>"+num); //通知其他线程 condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
任何一个技术,决定不是仅仅只是覆盖了原来的技术,优势和补充!
Condition 精准的通知和唤醒线程
代码测试:--->解决方法(Condition监视器)
package lin.Product_Customer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * 目的:A执行完调用B,B执行完调用C,C执行完调用A * */ public class Test03 { public static void main(String[] args) { Data3 data3 =new Data3(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.printA(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.printB(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.printC(); } },"C").start(); } } class Data3{ //资源类 Lock锁 private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private int flag=1; public void printA(){ lock.lock(); try{ //业务代码:判断->执行->通知 while (flag!=1){ condition1.await(); } System.out.println(Thread.currentThread().getName()+"执行"); flag=2;//唤醒指定的人 condition2.signal(); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public void printB(){ lock.lock(); try{ //业务代码:判断->执行->通知 while (flag!=2){ condition2.await(); } System.out.println(Thread.currentThread().getName()+"执行"); flag=3; //唤醒指定的人 condition3.signal(); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public void printC(){ lock.lock(); try{ //业务代码:判断->执行->通知 while (flag!=3){ condition3.await(); } flag=1;//唤醒指定的人 System.out.println(Thread.currentThread().getName()+"执行"); condition1.signal(); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } }
4、8锁现象
如何判断锁的是谁!永远的知道什么是锁,锁到底锁的是谁
深刻理解我们的锁
package lin.lock8; import java.util.concurrent.TimeUnit; /* * 8锁:就是关于锁的8个问题 * 1:标准情况下,两个线程先打印 sendSms 还是 call? --->先打印sendSms 再打印call * 2:sendSms延迟4秒,两个线程先打印 sendSms 还是 call? --->先打印sendSms 再打印call * * */ public class Test1 { public static void main(String[] args) { Phone phone = new Phone(); //锁的存在 new Thread(()->{ phone.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); },"B").start(); } } class Phone{ //Synchronized 锁的对象是方法的调用者 //两个方法用的是同一个锁,谁先拿到就谁先执行 public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSms"); } public synchronized void call(){ System.out.println("call"); } }
package lin.lock8; import java.util.concurrent.TimeUnit; /* * 8锁:就是关于锁的8个问题 * 3:增加了一个普通的方法!三个线程先打印 sendSms 还是 call 还是 hello? --->先打印hello,再sendSms,再call * 4:两个对象,两个同步方法,先打印哪个? --->先call,在hello,再sendSms * */ public class Test2 { public static void main(String[] args) { //两个对象,两个调用者,有两把锁,锁的对象不一样 Phone2 phone2 = new Phone2(); Phone2 phone2_1 = new Phone2(); //锁的存在 new Thread(()->{ phone2.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone2_1.call(); },"B").start(); new Thread(()->{ phone2.hello(); }).start(); } } class Phone2{ //Synchronized 锁的对象是方法的调用者 //两个方法用的是同一个锁,谁先拿到就谁先执行 public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSms"); } public synchronized void call(){ System.out.println("call"); } //这里没有锁!不是同步方法,不受锁的影响 public void hello(){ System.out.println("hello"); } }
package lin.lock8; import java.util.concurrent.TimeUnit; /* * 8锁:就是关于锁的8个问题 * 5:增加两个静态的同步方法,只有一个对象,先打印谁? ---->先sendSms,再call * 6:两个对象!增加两个静态的同步方法,只有一个对象,先打印谁? ---->先sendSms,再call * */ public class Test3 { public static void main(String[] args) { //两个对象Class类模板只有一个,static锁的是Class Phone3 phone3 = new Phone3(); Phone3 phone3_1 = new Phone3(); //锁的存在 new Thread(()->{ phone3.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone3_1.call(); },"B").start(); } } //只有唯一的一个Class对象 class Phone3{ //Synchronized 锁的对象是方法的调用者 //static 静态方法 //类一加载就有了!锁的是Class public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSms"); } public static synchronized void call(){ System.out.println("call"); } }
package lin.lock8; import java.util.concurrent.TimeUnit; /* * 8锁:就是关于锁的8个问题 * 7:一个对象,一个静态同步方法,一个普通同步方法,先打印哪个? --->先call,在sendSms * 8: 两个对象,一个静态同步方法,一个普通同步方法,先打印哪个? --->先call,在sendSms * */ public class Test4 { public static void main(String[] args) { //两个对象Class类模板只有一个,static锁的是Class Phone4 phone4 = new Phone4(); Phone4 phone4_1 = new Phone4(); //锁的存在 new Thread(()->{ phone4.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone4_1.call(); },"B").start(); } } //只有唯一的一个Class对象 class Phone4{ //Synchronized 锁的对象是方法的调用者 //static 静态方法 //类一加载就有了!锁的是Class //静态同步方法,锁的是Class模板 public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSms"); } //普通同步方法,锁的是调用者 public synchronized void call(){ System.out.println("call"); } }
小结
new this 具体的一个手机
static Class 唯一的一个模板
5、集合类不安全
List不安全
package lin.unsafe; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; //java.util.ConcurrentModificationException 并发修改异常 public class ListTest { public static void main(String[] args) { // List<String> list = Arrays.asList("1","2","3"); // list.forEach(System.out::println); //并发下 ArrayList 不安全的 /* * 解决方案: * 1:List<String> list1 = new Vector<>(); * 2:List<String> list1 = Collections.synchronizedList(new ArrayList<>()); 把ArrayList变得安全 * 3:List<String> list1 = new CopyOnWriteArrayList<>(); * * CopyOnWriteArrayList<>(); 比 Vector<>(); 牛逼在哪里? * */ //CopyOnWrite 写入时复制 简称COW 计算机程序领域的一种优化策略 //多个线程调用的时候,list,读取的时候是固定的,写入(存在覆盖操作) //在写入的时候避免覆盖,避免造成数据问题 // List<String> list1 = new Vector<>(); // List<String> list1 = new ArrayList<>(); // List<String> list1 = Collections.synchronizedList(new ArrayList<>()); List<String> list1 = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list1.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list1); },String.valueOf(i)).start(); } } }
Set不安全
package lin.unsafe; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; /* * 同理可证:ConcurrentModificationException * */ public class SetTest { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); // 1、Set<String> set = Collections.synchronizedSet(new HashSet<>()); // CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>(); for (int i = 0; i < 30; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(set); },String.valueOf(i)).start(); } } }
6、Callable
eg:
1、可以有返回值
2、可以抛出异常
3、方法不同,Runnable重写run()方法,Callable重写call()方法
代码测试
Callable想要启动线程,不能直接启动。
package lin.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { // new Thread(new Runnable()).start(); // new Thread(new FutureTask<V>()).start(); // new Thread(new FutureTask<V>( Callable )).start(); // new Thread().start(); //怎么启动Callable MyThread thread = new MyThread(); //适配类FutureTask<V>() FutureTask futureTask = new FutureTask<>(thread); new Thread(futureTask,"A").start(); Integer o = (Integer) futureTask.get();//获取Callable的返回结果 System.out.println(o); } } //class MyThread implements Runnable{ // @Override // public void run() { // } //} class MyThread implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("call()"); return 1024; } }
细节:
1、有缓存的
2、结果可能需要等待,会阻塞
package lin.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { // new Thread(new Runnable()).start(); // new Thread(new FutureTask<V>()).start(); // new Thread(new FutureTask<V>( Callable )).start(); // new Thread().start(); //怎么启动Callable MyThread thread = new MyThread(); //适配类FutureTask<V>() FutureTask futureTask = new FutureTask<>(thread); new Thread(futureTask,"A").start(); new Thread(futureTask,"B").start(); //两个线程一起跑,会打印几个call() --->一个call(),结果会被缓存,效率高 Integer o = (Integer) futureTask.get();//获取Callable的返回结果,这个get方法可能会产生异常!把它放到最后 //或者使用异步通信 System.out.println(o); } } //class MyThread implements Runnable{ // @Override // public void run() { // } //} class MyThread implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("call()"); return 1024; } }
7、JUC 常用的辅助类(三个必须会)
7.1、CountDownLatch(减法计数器)
package lin.add; import java.util.concurrent.CountDownLatch; //计数器 public class CountDownLatchDemo { 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()+" GO OUT"); countDownLatch.countDown();//-1 },String.valueOf(i)).start(); } countDownLatch.await(); //等待计数器归零,然后再向下执行 System.out.println("close door"); } }
原理:
countDownLatch.countDown();//-1
countDownLatch.await(); //等待计数器归零,然后再向下执行
每次有线程调用countDown()方法,数量就-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行
7.2、CyclicBarrier(加法计数器)
加法计数器
package lin.add; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; //加法计数器 public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("龙珠收集成功,召唤神龙!"); }); for (int i = 1; i <= 7; i++) { final int temp=i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"拿到了第"+temp+"颗龙珠"); try { cyclicBarrier.await(); //等待 } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }
7.3、Semaphore(并发常用)
Semaphore:信号量
eg:抢车位:6个车就三个停车位
package lin.add; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class SemaphoreDemo { public static void main(String[] args) { //默认线程数量:停车位! 限流的时候用 Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6; i++) { new Thread(()->{ //acquire()得到 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 { //release()释放 semaphore.release(); } },String.valueOf(i)).start(); } } }
原理:
semaphore.acquire(); 获取,假设如果已经满了,就等待,等待被释放为止!
semaphore.release(); 释放,会将当前的信号量释放+1,然后唤醒等待的线程!
作用: 多个共享资源互斥使用!并发限流,控制最大的线程数!
8、读写锁
ReadWriteLock
package lin.readwritelock; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /* * ReadWriteLock * 独占锁(写锁):一次只能被一个线程占有 * 共享锁(读锁):多个线程可以同时占有 * */ public class ReadWriteLockDemo { public static void main(String[] args) { // MyCache myCache = new MyCache(); MyCacheLock myCacheLock = new MyCacheLock(); //写操作 for (int i = 1; i <= 5; i++) { final int temo=i; new Thread(()->{ myCacheLock.put(temo+"",temo+""); },String.valueOf(i)).start(); } //读操作 for (int i = 1; i <= 5; i++) { final int temo=i; new Thread(()->{ myCacheLock.get(temo+""); },String.valueOf(i)).start(); } } } /* * 自定义缓存 * */ class MyCache{ private volatile Map<String,Object> map = new HashMap<>(); //存,写的过程 public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"写入"+key); 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 Map<String,Object> map = new HashMap<>(); //读写锁:更加细粒度的控制 private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //存,写的过程 写入的时候只希望同时只有一个线程写 public void put(String key,Object value){ //写锁 readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"写入"+key); map.put(key, value); System.out.println(Thread.currentThread().getName()+"写入完成"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); } } //取,读的过程 所有人都可以去读 public void get(String key){ //读锁 readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取结束"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } } }
9、阻塞队列
阻塞队列:
BlockingQueue<E> 不是新的东西
什么情况下我们会使用阻塞队列?:多线程并发处理,线程池!
学会使用队列:添加、移除
有四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞 等待 | 超时等待(重点) |
---|---|---|---|---|
添加 | add | offer() | put | offer(E e, long timeout, TimeUnit unit) eg:offer("D",3, TimeUnit.SECONDS) |
移除 | remove | poll() | take | poll(long timeout, TimeUnit unit) eg:poll(3, TimeUnit.SECONDS) |
判断(队首) | element | peek |
/* * 抛出异常 * */ public static void test1(){ // <>报错因为()中没有填入队列的大小 ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); System.out.println(arrayBlockingQueue.add("A")); System.out.println(arrayBlockingQueue.add("B")); System.out.println(arrayBlockingQueue.add("C")); //IllegalStateException: Queue full 抛出异常!(超过定义队列的大小时) // System.out.println(arrayBlockingQueue.add("D")); //查看队首元素 System.out.println(arrayBlockingQueue.element()); System.out.println("------------------------------------------"); //先进先出的原理(FIFO) System.out.println(arrayBlockingQueue.remove()); System.out.println(arrayBlockingQueue.remove()); System.out.println(arrayBlockingQueue.remove()); //NoSuchElementException 抛出异常! 没有元素的! // System.out.println(arrayBlockingQueue.remove()); }
/* * 有返回值,不抛出异常 * */ public static void test2(){ //队列大小 ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); System.out.println(arrayBlockingQueue.offer("A")); System.out.println(arrayBlockingQueue.offer("B")); System.out.println(arrayBlockingQueue.offer("C")); //超过队列的大小时,输出false,不抛出异常! System.out.println(arrayBlockingQueue.offer("D")); //检测队首元素(也是查看第一个元素) System.out.println(arrayBlockingQueue.peek()); System.out.println("------------------------------------------"); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); //返回null,也不抛出异常! System.out.println(arrayBlockingQueue.poll()); }
/* * 等待 阻塞(一直) * */ 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("出现该消息则表示没有阻塞,则所有的put完成"); System.out.println("------------------------------------------"); 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); arrayBlockingQueue.offer("A"); arrayBlockingQueue.offer("B"); arrayBlockingQueue.offer("C"); //等待3秒队列中还没有位置添加D,那就就超时退出这个添加操作 arrayBlockingQueue.offer("D",3, TimeUnit.SECONDS); System.out.println("------------------------------------------"); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); //等待3秒队列中还没有元素可以取出,那就就超时退出这个取出操作 System.out.println(arrayBlockingQueue.poll(3, TimeUnit.SECONDS)); }
SynchronousQueue同步队列(没有容量)
没有容量:进去一个元素,必须等待取出来以后,才能再往里面放一个元素!
put、take
package lin.blockQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; /* * 同步队列 * 和其他的BlockingQueue不一样,SynchronousQueue 不存储元素 * * */ public class SynchronousQueueDemo { public static void main(String[] args) { SynchronousQueue<String> synchronousQueue = new SynchronousQueue<String>(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+"put 1"); synchronousQueue.put("1"); System.out.println(Thread.currentThread().getName()+"put 2"); synchronousQueue.put("2"); System.out.println(Thread.currentThread().getName()+"put 3"); synchronousQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } },"T1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+" take "+synchronousQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+" take "+synchronousQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+" take "+synchronousQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"T2").start(); } }
10、线程池(重点)
线程池:三大方法,7大参数,4种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用!======>>>>池化技术
线程池,连接池,内存池,对象池....(创建,销毁都是十分消耗资源)
池化技术:事先准备好一些资源,有人要用就来我这里拿,用完之后还我,重复
线程池的好处:
1、降低资源的消耗
2、提高响应的速度
3、方便管理
总结:线程可以复用,控制线程最大并发数,管理线程
线程池:三大方法
package lin.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //Executors 工具类、3大方法 //使用了线程池之后,要使用线程池来创建线程 public class Demo01 { public static void main(String[] args) { // ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程 // ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小 ExecutorService threadPool = Executors.newCachedThreadPool();//可以伸缩, try { for (int i = 0; i < 10; i++) { //使用池化技术去创建线程池 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { //线程池用完,程序结束,关闭线程池 threadPool.shutdown(); } } }
7大参数
源码分析:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } 本质:ThreadPoolExecutor() public ThreadPoolExecutor(int corePoolSize, //核心线程池大小 int maximumPoolSize, //最大核心线程池大小 long keepAliveTime, //超时了没有人调用就会释放 TimeUnit unit, //超时单位 BlockingQueue<Runnable> workQueue, //阻塞队列 ThreadFactory threadFactory, //线程工厂,创建线程的,一般不用动 RejectedExecutionHandler handler //拒绝策略 ) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
手动创建一个线程池
package lin.pool; import java.util.concurrent.*; //Executors 工具类、3大方法 //使用了线程池之后,要使用线程池来创建线程 public class Demo01 { public static void main(String[] args) { //自定义线程池 ExecutorService threadPool = new ThreadPoolExecutor( 2, //核心线程池大小 5,//最大核心线程池大小 3,//超时了没有人调用就会释放 TimeUnit.SECONDS,//超时单位 new LinkedBlockingQueue<>(3),//阻塞队列 Executors.defaultThreadFactory(),//线程工厂,创建线程的,一般不用动 // new ThreadPoolExecutor.AbortPolicy()); //队列满了还有人进来,就不处理这个人的,并且抛出异常 // new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里! // new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常 new ThreadPoolExecutor.DiscardOldestPolicy() //拒绝策略 //队列满了,尝试和最早的竞争,不会抛出异常 ); try { //最大承载=Deque + max //RejectedExecutionException 超过最大承载就会抛出这个异常(可以控制自定义的拒绝策略) for (int i = 1; i <= 10; i++) { //使用池化技术去创建线程池 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { //线程池用完,程序结束,关闭线程池 threadPool.shutdown(); } } }
四种拒绝策略
/* * 4种拒绝策略: * 1、new ThreadPoolExecutor.AbortPolicy() 队列满了还有人进来,就不处理这个人的,并且抛出异常 * 2、new ThreadPoolExecutor.CallerRunsPolicy() 哪来的去哪里! * 3、new ThreadPoolExecutor.DiscardPolicy() 队列满了,丢掉任务,不会抛出异常 * 4、new ThreadPoolExecutor.DiscardOldestPolicy() 队列满了,尝试和最早的竞争,不会抛出异常 * */
小结和拓展
线程池的最大的大小到底该如何定义?(调优)
1、CPU 密集型 4核的电脑最多同时执行4条线程(几核的CPU就最多同时执行几条线程),----->一般几核CPU最大线程池就定义为几,可以保持CPU的效率最高
2、IO 密集型 > 有15个大型任务,IO十分占用资源(判断程序中十分耗IO的线程,然后定义的时候大于这个数)
11、四大函数式接口(必须掌握)---重点
必须要掌握的:lambda表达式,链式编程,函数式接口,Stream流式计算
函数式接口:只有一个方法的接口
@FunctionalInterface public interface Runnable { public abstract void run(); } // 超级多FunctionalInterface // 简化编程模型,在新版本的框架底层中大量应用 // foreach()
.
代码测试:
Function函数式接口
package lin.function; import java.util.function.Function; /* * Function 函数型接口,有一个输入参数,有一个输出 * 只要是 函数式接口 可以用lambda表达式简化 * */ public class Demo01 { public static void main(String[] args) { //工具类:输出输入的值 Function<String, String> function = new Function<String, String>() { @Override public String apply(String str) { return str; } }; //函数式接口的lambda表达式的简化 Function<String,String> function1 = (str)->{ return str; }; System.out.println(function.apply("abc")); System.out.println(function1.apply("cba")); } }
Predicate 断定型接口:有一个参数,返回值只能是布尔值!
package lin.function; import java.util.function.Predicate; /* * 断定型接口:有一个输入参数,返回只能是布尔值! * */ public class Demo02 { public static void main(String[] args) { //判断字符串是否为空 Predicate<String> predicate = new Predicate<String>(){ @Override public boolean test(String str) { if (str.isEmpty()) { return true; } return false; } }; //lambda表达式简化 Predicate<String> predicate1 = (str)->{return str.isEmpty();}; System.out.println(predicate.test("abc")); System.out.println(predicate1.test("abc")); } }
Consumer 消费型接口:
.
package lin.function; import java.util.function.Consumer; /* * Consumer 消费型接口:只有输入,没有返回值 * */ public class Demo03 { public static void main(String[] args) { Consumer<String> consumer = new Consumer<String>() { @Override public void accept(String str) { System.out.println(str); } }; //lambda表达式简化 Consumer<String> consumer1 =(str)->{ System.out.println(str); }; consumer.accept("abc"); consumer1.accept("cba"); } }
Supplier 供给型接口
.
package lin.function; import java.util.function.Supplier; /* * Supplier 供给型接口:没有参数,只有返回值 * */ public class Demo04_Supplier { public static void main(String[] args) { Supplier<String> supplier = new Supplier<String>() { @Override public String get() { System.out.println("supplier_get()"); return "abc"; } }; //lambda表达式 Supplier<String> supplier1 =()->{ System.out.println("supplier1_get()"); return "cba"; }; System.out.println(supplier.get()); System.out.println(supplier1.get()); } }
12、Stream流式计算
什么是Stream流式计算
大数据=存储 + 计算
集合、MySQL本质就是存储东西的;
计算都应该交给流操作!
创建Stream
Arrays.stream()
在日常编程中面对的是一个数组,也可以使用Arrays.stream()方法来使用Stream
Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96}; long count = Arrays.stream(array).filter(i->i>20).count();
Stream.of()
当面对数组时除了可以使用Arrays.stream()方法外,还可以使用Stream将需要的数组转成Stream。这个方法不但支持传入数组,将数组转成Stream,也支持传入多个参数,将参数最终转成Stream
Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96}; long count = Stream.of(array).filter(i->i>20).count(); long sum = Stream.of(12,77,59,3,654).filter(i->i>20).mapToInt(Integer::intValue).sum(); System.out.println("count:"+count+",sum:"+sum);
Collection.stream()
这个就是最常见的Stream了。因为Collection是Java中集合接口的父接口,Java中的集合都继承或实现了此接口。所以Java中的集合都可以使用此方法来创建一个Stream;
List<Integer> numbers = new ArrayList<>(); numbers.add(3); numbers.add(4); numbers.add(8); numbers.add(16); numbers.stream().forEach(number->{ System.out.println(number); });
package lin.stream; import java.util.Arrays; import java.util.List; import java.util.Locale; /* * 题目要求:只能用一行代码实现 * 现在有5个用户!筛选: * 1、ID必须是偶数 * 2、年龄必须大于23岁 * 3、用户名转为大写字母 * 4、用户名字母倒着排序 * 5、只输出一个用户! * */ public class Test { public static void main(String[] args) { User u1 = new User(1,"a",21); User u2 = new User(2,"b",22); User u3 = new User(3,"c",23); User u4 = new User(4,"d",24); User u5 = new User(6,"e",25); //集合就是存储 List<User> userList = Arrays.asList(u1, u2, u3, u4, u5); //计算交给流 //链式编程 userList.stream() .filter(u->{return u.getId()%2==0;}) .filter(u->{return u.getAge()>23;}) .map(u->{return u.getName().toUpperCase();}) .sorted((uu1,uu2)->{return uu2.compareTo(uu1);}) .limit(1) .forEach(System.out::println); } }
Stream 不是一个数据结构,不存储数据,它相当于一个管道用于传递数据。
Stream 对数据操作,不会修改源数据。
Stream 整体流水线包括很多操作,按阶段划分为三个阶段:
1、生成流:eg:userList.stream(),eg:Stream.of(strs) ---String[] strs = new String[]{"hello","lin","ke","xiang"}; 就是生成一个Stream流对象
2、中间操作:中间操作有很多,每个操作处理完成之后将结果交给下一个操作(中间操作具有惰性)
3、终结操作:一个流只有一个终结操作,只要执行终结操作就触发整个流的执行(不执行后边的foreach()遍历,前面的操作就不执行) -----forEach()
输出流程
跟踪日志
1、整个Stream流计算数据只遍历了一次
2、每个数据元素的处理分别从流水线的三个阶段(filter->map->forEach)依次执行。每遍历一个数据时,都会依次执行操作判断,若有一个不符合,就不执行接下来的操作。然后再遍历下一个数据。
eg :hello字符串,经历过滤,转大写,遍历输出三个操作
eg :lin字符串,只经历过滤,因为过滤条件不满足,所以后边的操作不执行
3、当前一个阶段的操作不满足条件,后边的操作不执行
Stream Api的应用
package lin.stream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.stream.Stream; /* * Stream Api 应用 * */ public class StreamTestApi1 { public static void main(String[] args) { test1(); System.out.println("------------------------"); test2(); System.out.println("------------------------"); test3(); System.out.println("------------------------"); test4(); } //基于collection集合生成流 public static void test1(){ List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); //调用Stream方法 ->并对流进行一个遍历 list.stream() // .forEach(item->{ // System.out.println(item); // }; .forEach(System.out::println); //简写lambda表达式而已 } //基于Map集合生成流 public static void test2(){ HashMap<String, User> map = new HashMap<>(); map.put("1",new User(1,"zhangsan",12)); map.put("2",new User(2,"lisi",18)); map.put("3",new User(3,"wangwu",28)); //基于Map生成流对象,可以对keySet、values生成流对象 map.keySet().stream() //对map的key对象生成一个流对象,可以通过这个key去寻找map对应的value值 .filter(key->{ return map.get(key).getAge()>18; }) .forEach(key->{ System.out.println(map.get(key)); }); map.values().stream() //对map的values对象生成一个流对象 .filter(x->{ return x.getAge()>18; }) .forEach(System.out::println); } //基于数组集合生成流 public static void test3(){ String[] strs = new String[]{"1","2","3","4"}; Arrays.stream(strs) .forEach(System.out::println); // Stream.of(strs) //这两个是相同的=====Arrays.stream(strs) // .forEach(System.out::println); //基于整型生成流 --->遍历之后输出为:[I@5fd0d5ae 有问题,需要一个流的转换 // int[] array_1 = new int[]{1,2,3,4}; // Stream.of(array_1) // .forEach(System.out::println); } public static void test4(){ List<User> userList1 = new ArrayList<>(); userList1.add(new User(1,"zhangsan",12)); userList1.add(new User(2,"lisi",18)); userList1.add(new User(3,"wangwu",28)); List<User> userList2 = new ArrayList<>(); userList2.add(new User(4,"zhangsan1",12)); userList2.add(new User(5,"lisi1",18)); userList2.add(new User(6,"wangwu1",28)); // userList.stream() // .map(x->{ // HashMap<Integer, User> hashMap = new HashMap<>(); // hashMap.put(x.getId(),x); // return hashMap; // }) // .forEach(x->{ // x.forEach((key,value)->{ // System.out.println(value); // }); // }); List<List<User>> lists = new ArrayList<>(); lists.add(userList1); lists.add(userList2); //要求:年龄大于15的名字后面加一个* //最开始的思路 /*lists.stream() .map(x->{ x.stream().map(u->{ HashMap<Integer, User> hashMap = new HashMap<>(); hashMap.put(u.getId(),u); if (u.getAge()>15){ u.setName(u.getName()+"*"); } return hashMap; }).forEach(U->{ U.forEach((key,value)->{ System.out.println(value); }); }); return x; }) .forEach(System.out::println);*/ //修改过后的思路 /*lists.stream() .map(x->x.stream().map(u->{ HashMap<Integer, User> hashMap = new HashMap<>(); hashMap.put(u.getId(),u); if (u.getAge()>15){ u.setName(u.getName()+"*"); } return hashMap; }) ) .forEach(i-> i.forEach(System.out::println));*/ //最优解 lists.stream().map(s->s.stream().map(item->{ if (item.getAge()>15) item.setName(item.getName()+"*"); return item; })).forEach(x->{ x.forEach(System.out::println); }); } }
13、ForkJoin
什么是ForkJoin
ForkJoin 在JDK1.7,并行执行任务!提高效率,大数据量!
大数据: Map Reduce(大任务拆成小任务)
ForkJoin特点:工作切取
这里面维护的都是双端队列
.B执行完之后会切取A的任务来执行,提高效率
ForkJoin操作
package lin.forkJoin; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; /* * 求和计算的任务! * 3() 6(ForkJoin) 9(Stream并行流) * * 如何使用 forkjoin * 1、forkjoinPool 通过它来执行 * 2、计算任务 forkjoinPoll。execute(ForkJoinTask<?> task) * 3、计算类要继承ForkJoinTask * */ public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; private Long end; //临界值 private Long temo = 10000L; public ForkJoinDemo(Long start, Long end) { this.start = start; this.end = end; } //计算方法 @Override protected Long compute() { if ((end-start)<temo){ Long sum = 0l; for (Long i = start; i <= end; i++) { sum +=i; } return sum; }else { //forkjoin 分支计算 递归 long middle = (start + end) / 2; //中间值 ForkJoinDemo forkJoinDemo1 = new ForkJoinDemo(start,middle); forkJoinDemo1.fork(); //拆分任务,把任务压入线程队列 ForkJoinDemo forkJoinDemo2 = new ForkJoinDemo(middle+1,end); forkJoinDemo2.fork(); //拆分任务,把任务压入线程队列 return forkJoinDemo1.join() + forkJoinDemo2.join(); } } }
package lin.forkJoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.LongStream; public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { // test1();//时间:10949 System.out.println("-----------------"); // test2(); //时间:9584 System.out.println("-----------------"); test3(); //时间:330 } //正常求和 public static void test1(){ Long sum = 0l; long start = System.currentTimeMillis(); for (Long i = 1l; i <= 10_0000_0000l; i++) { sum+=i; } long end = System.currentTimeMillis(); System.out.println("sum="+sum+" 时间:"+(end-start)); } //会使用ForkJoin求和 public static void test2() throws ExecutionException, InterruptedException { long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinDemo(1l,10_0000_0000l); ForkJoinTask<Long> submit = forkJoinPool.submit(task);//异步,提交任务 // forkJoinPool.execute(task);//同步,执行任务 submit.get(); long end = System.currentTimeMillis(); System.out.println("sum="+submit.get()+" 时间:"+(end-start)); } //使用Stream并行流 public static void test3(){ long start = System.currentTimeMillis(); long reduce = LongStream.rangeClosed(1l, 10_0000_0000l).parallel().reduce(0, Long::sum); long end = System.currentTimeMillis(); System.out.println("sum="+reduce+" 时间:"+(end-start)); } }