JUC并发编程
什么是JUC
java.util工具包,包,分类
业务:普通的线程代码 Thread
Runnable 没有返回值,效率相对比 Callable
2.线程和进程
线程,进程,如果不能用一句话说出来的技术,不扎实!
进程:一个程序,QQ.exe Music.exe 程序的集合
一个进程往往包含多个线程至少包含一个!
java,默认有几个线程?两个 mian,和gc
线程:开了一个进程Typora,写字,自动保存(线程负责的)
对于java而言:Thread,Runnble,Callable
java真的可以开启线程吗? 开启不了的(他是通过本地方法调用,调用了底层的c++,java无法操作硬件)
并发,并行
并发编程,并发,并行
并发(多个线程操作同一个资源)
- cpu一核,模拟出多条线程,天下武功为快不破,快速交替
并行(多个人一起行走)
- cpu多核,多个线程可以同时执行(线程池)
package com;
public class Test078 {
public static void main(String[] args) {
//获取cup的核数.
//cpu 密集型 IO 密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用Cpu的资源
线程有几个状态
new 线程新生
Runnable 运行
Blocked阻塞
Waitng等待,死死的等
Timed Watiing //超时等待
Terminated//终止
wait和sleep的区别
1.来自不同的类
wait => Object
sleep=>Thread
关于锁的释放
wait会释放锁,sleep不会释放锁,抱着锁睡觉,不会释放
使用的范围是不同的
wait:必须在同步代码块的时候使用
sleep 可以再任何地方使用
是否要捕获异常
sleep 需要捕获异常
wait 不需要捕获异常
Lock锁(重点)
公平锁:顾名思义,十分公平,必须按照顺序执行
非公平锁:不公平,可以插队(默认)
package com;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Dome99{
public static void main(String[] args) {
//并发多个线程操作同一个资源,把资源丢进线程里
//并发多个线程操作同一个资源,把资源丢进线程里
Tick tick = new Tick();
new Thread(()->{ for (int i = 0; i < 60; i++) tick.sale(); },"A").start();
new Thread(()->{ for (int i = 0; i < 60; i++) tick.sale(); },"b").start();
new Thread(()->{ for (int i = 0; i < 60; i++) tick.sale(); },"c").start();
}
}
/**
* lock锁
* 1.new ReentrantLock(); 创建可重复锁
* 2.try 加锁 lock.lock();
* 3.finally
*/
class Tick1{
Lock lock = new ReentrantLock();
private int number = 50;
//麦票的方法
public void sale(){
lock.lock();//加锁
try {
//业务代码
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,还剩"+number+"张");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁
}
}
}
Synchronized 和 Lock 区别
- Synchronized 内置的java关键字 lock是一个java类
- Synchronized 无法判断锁的状态, lock可以判断是否获取到了锁
- Synchronized 会自动释放锁,lock必须要手动释放锁,如果不释放锁就是形成死锁
- Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等),loc就不一定会等待下去
- Synchronized 可重入锁,不可以中单的,非公平锁,lock,可重入锁,可以判断锁,非公平(可以自己设置);
- Synchronized 适合少量的代码同步问题,lock 适合锁大量的同步代码
面试的:单列模式,排序算法,生产者和消费者,死锁
生产者和消费者问题Synchronized 版本
package com.pc;
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"B").start();
}
}
class Data{//数字资源类
private int number = 0;
public synchronized void increment(){//增值
if (number != 0 ){
//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
//通知其他线程我-1完毕了
this.notifyAll();
System.out.println(Thread.currentThread().getName()+">"+number);
}
public synchronized void decrement(){//减值
if (number == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
//通知其他线程我-1完毕了
this.notifyAll();
System.out.println(Thread.currentThread().getName()+">"+number);
}
}
问题存在 Abcd 4个线程,会存在虚假唤醒
package com.pc;
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"c").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"d").start();
}
}
class Data{//数字资源类
private int number = 0;
public synchronized void increment(){//增值
while (number != 0 ){
//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
//通知其他线程我-1完毕了
this.notifyAll();
System.out.println(Thread.currentThread().getName()+">"+number);
}
public synchronized void decrement(){//减值
while (number == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
//通知其他线程我-1完毕了
this.notifyAll();
System.out.println(Thread.currentThread().getName()+">"+number);
}
}
将if改为While判断
因为线程唤醒后会从waic的地方继续执行,使用if的话,唤醒之后不会进行判断(因为wait之前已经判断过了)使用循环的话唤醒后悔继续判断
通过Lock找到Condition
代码实现:
package com.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class D{
public static void main(String[] args) throws Exception{
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"c").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"d").start();
}
}
class Data3{//数字资源类
private int number = 0;
// Lock lock = new ReentrantLock();//重新进入锁
// Condition condition = lock.newCondition();
// condition.await();//等待
// condition.signalAll();//唤醒全部对象
Lock lock = new ReentrantLock();//创建可重入锁
Condition condition = lock.newCondition();//通过lock创建Condition对象,通过这个对象调用方法
public void increment() {//增值
lock.lock();
while (number != 0 ){
//等待
try {
condition.await();//让对象等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
//通知其他线程我-1完毕了
condition.signalAll();//唤醒对象
System.out.println(Thread.currentThread().getName()+">"+number);
}
public void decrement(){//减值
lock.lock();
while (number == 0){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
//通知其他线程我-1完毕了
condition.signalAll();
System.out.println(Thread.currentThread().getName()+">"+number);
}
}
任何一个新的技术,绝对不仅仅是覆盖了原来的技术,优势和补充
Condition 精准的通知和唤醒线程
代码实现
package com.pc;
import java.security.PublicKey;
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 Q {
public static void main(String[] args) {
Data5 date = new Data5();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.pintA();
}
},"A").start();
new Thread(()->{ for (int i = 0; i < 10; i++) {
date.pintB();
}},"B").start();
new Thread(()->{ for (int i = 0; i < 10; i++) {
date.pintC();
}},"C").start();
}
}
class Data5{
private Lock lock = new ReentrantLock();//创建可重新进入锁
private Condition condition1 = lock.newCondition();//创建监视器
private int number = 1;//创建标志位
public void pintA(){
lock.lock();
//业务 判断 执行 通知
try {
while (number !=1){//如果number不等于1
condition1.await();//condition1暂停
}
System.out.println(Thread.currentThread().getName()+"AAAAAAA");
number = 2;
condition1.signal();//唤醒单个线程
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void pintB(){
lock.lock();
try {
while (number != 2){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"BBBBBBB");
number = 3;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void pintC(){
lock.lock();
try {
while (number != 3){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"CCCCC");
condition1.signal();
number = 1;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
什么是锁
8锁现象
如何判断锁的是谁!永远知道什么锁,锁到底锁的谁
对象,Class
package com.pc;
import java.util.concurrent.TimeUnit;
/*
*8锁 就是关于锁的八个问题
* 1.两条线程同时执行,哪个先打印? 先打印发短信,然后打电话
* 2.sendSms方法延迟4秒,那个先打印?先打印发短信,然后打电话
*/
public class Test01 {
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{//电话
/**
* 1.synchronized 锁的对象是方法的调用者
* 2.两个方法用同一个锁,谁先调用,谁先执行
*/
public synchronized void sendSms(){//发送短信
System.out.println("sendSms 发短信");
}
public synchronized void call(){//打电话
System.out.println("call 打电话");
}
}
package com.pq;
/**
* 增加了一个普通方法后!先执行发短信还是Hello?先执行普通方法
* 两个对象,两个同步方法,发短信还是打电话?//打电话
*/
import java.util.concurrent.TimeUnit;
public class Test01 {
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.hello();
},"B").start();
}
}
class Phone{//电话
/**
* 1.synchronized 锁的对象是方法的调用者
* 2.两个方法用同一个锁,谁先调用,谁先执行
*/
public synchronized void sendSms(){//发送短信
try {
TimeUnit.SECONDS.sleep(3);
} 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 com.pq;import java.util.concurrent.TimeUnit;/** * 5.增加两个静态同步的方法,只有一个对象,先打印发短信还是打电话? * 6.两个对象.增加两个静态同步的方法,只有一个对象,先打印发短信还是打电话? */public class Test03 { public static void main(String[] args) { //两个对象的类模板只有一个,static 锁的是class Phone1 phone = new Phone1(); Phone1 phone1 = new Phone1(); new Thread(()->{ phone.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1);//线程睡眠一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone1.call(); },"B").start(); }}class Phone1{//电话 /** * 1.synchronized 锁的对象是方法的调用者 * 2.两个方法用同一个锁,谁先调用,谁先执行 */ public static synchronized void sendSms(){//发送短信 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSms 发短信"); } public static synchronized void call(){//打电话 System.out.println("call 打电话"); }}
package com.pq;import java.util.concurrent.TimeUnit;/** * 1.一个静态的同步方法一个,普通的同步方法,一个对象,先打印哪个? * 2.一个静态的同步方法一个,普通的同步方法,两个个对象,先打印哪个? */public class Test04 { public static void main(String[] args) { //两个对象的类模板只有一个,static 锁的是class Phone4 phone = new Phone4(); Phone4 phone1 = new Phone4(); new Thread(()->{ phone.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1);//线程睡眠一秒 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone1.call(); },"B").start(); }}class Phone4{//电话 /** * 1.synchronized 锁的对象是方法的调用者 * 2.两个方法用同一个锁,谁先调用,谁先执行 */ public static synchronized void sendSms(){//发送短信 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSms 发短信"); } public synchronized void call(){//打电话 System.out.println("call 打电话"); }}
小结
new this 具体的一个手机
static Class 唯一的一个模板
集合不安全
package com.pc;import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;//public class ListTest { public static void main(String[] args) { /** * java.util.ConcurrentModificationException 并发修改异常 * 解决方案. * 1.List<String> list = new Vector<>(); * 2.List<String> list = Collections.synchronizedList(new ArrayList<>()); * 3.List<String> list = new CopyOnWriteArrayList<>(); * CopyOnWrite写入时复制 COW 计算机程序设计领域的一种优化策略 * 多个线程调用的时候list 读取的时候,固定的,写入(覆盖) * 在写入的时候避免覆盖,造成数据问题! * * 读写分离 */ List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },String.valueOf(i)).start(); } }}
小狂神的学习方法推荐:1.先会用,2.货比三家,寻找更好的解决方法,3.看源码
HashSet异常
package com.pc;import java.awt.datatransfer.ClipboardOwner;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;import java.util.concurrent.CopyOnWriteArraySet;/** * 同理可证 ConcurrentModificationException 并发修改异常 * 解决方法 * 1.Set<Object> set = Collections.synchronizedSet(new HashSet<>()); * 2. Set<Object> set = new CopyOnWriteArraySet<>(); */public class SetTest { public static void main(String[] args) { Set<Object> set = new CopyOnWriteArraySet<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(set); },String.valueOf(i)).start(); } }}
hashSet底层是什么?
public HashSet() { map = new HashMap<>();}//底层还是HashMap
package com.pc;import java.util.Collections;import java.util.HashMap;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArrayList;public class MapTest { public static void main(String[]args){ //解决方法 Map<String, String> map = new ConcurrentHashMap<>(); Map<String, String> map = new ConcurrentHashMap<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5)); System.out.println(map); },String.valueOf(i)).start(); } }}
Callable(简单)
- 可以有返回值
- 可以抛出异常
- 方法不同,run/call
代码测试
细节
- 有缓存
- 结果可能需要等待,会阻塞
package com.pc;
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 {
MyCallable a = new MyCallable();
FutureTask futureTask = new FutureTask(a);//创建适配类
new Thread(futureTask,"s").start();
new Thread(futureTask,"a").start();//会缓冲,优化代码
Integer o = (Integer)futureTask.get();//查找返回值
System.out.println(o);//有可能会阻塞
}
}
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("我是王刚");
return null;
}
}
Semaphore
semaphore:信号量
package com.Add;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class semaphoreDome { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 6; i++) { new Thread(()->{ try { semaphore.acquire();//取得线程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(); } }}
原理:
Semaphore.acquire();//获得,假设已经满了,那就等待释放为止
Semaphore.release();//释放,会将当前的信号释放,唤醒等待的线程
作用:多个共享资源,互斥的使用!并发限流,控制最大的限流
读写锁
ReadWriteLock
package com.Add;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * 独占锁(写锁) 一次只能被一个线程占有 * 共享锁(读锁) 多个线程可以同时占有 * ReadWriteLock * 读 - 读 可以共存 * 读 - 写 不能共存 * 写 - 写 不能共存 */public class ReadWriterLockDome { public static void main(String[] args) { MyCacheLock myCacheLock = new MyCacheLock(); for (int i = 0; i < 6; i++) { final int temp = i; new Thread(()->{ myCacheLock.put },String.valueOf(i)).start(); } }}class MyCacheLock{ private volatile Map<String,Object> map = new HashMap<>(); private ReadWriteLock Re = new ReentrantReadWriteLock();//读写锁 private void put(String key,Object vlue){ Re.writeLock().lock();//加锁 try { System.out.println(Thread.currentThread().getName()+"写入"+key); put(key,vlue); System.out.println(Thread.currentThread().getName()+"写入成功"); } catch (Exception e) { e.printStackTrace(); } finally { Re.writeLock().unlock();//解锁 } } private void get(String key){ Re.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取"+key); } catch (Exception e) { e.printStackTrace(); } finally { Re.readLock().unlock(); } }}
阻塞队列
BlockingQueue 不是新东西
什么情况下我们会使用阻塞队列:多线程并发处理,线程池
学会使用队列
添加,移除
四种Api
| 方式 | 抛出异常 | 有返回值 | 阻塞等待 | 超时等待 |
|---|---|---|---|---|
| 添加 | add | offer | put | offer(;😉 |
| 移除 | remove | poll | take | poll(,) |
| 判断队列首 | element | peek | --- | --- |
- 会抛出异常
- 不会抛出异常
- 阻塞等待
- 超时等待
package com.Add;import java.util.concurrent.ArrayBlockingQueue;public class Test { public static void main(String[] args) {//队列的大小 ArrayBlockingQueue<Object> q = new ArrayBlockingQueue<>(3); System.out.println(q.add(3)); System.out.println(q.add(4)); System.out.println(q.add(5)); //IllegalStateException System.out.println(q.remove()); System.out.println(q.remove()); System.out.println(q.remove()); }}
package com.Add;import java.util.concurrent.ArrayBlockingQueue;public class Test { public static void main(String[] args) { ArrayBlockingQueue<Object> q = new ArrayBlockingQueue<>(3); System.out.println(q.offer(3)); System.out.println(q.offer(4)); System.out.println(q.offer(5)); System.out.println(q.offer(5));//false不抛出异常 //IllegalStateException System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll());//null不抛出异常 }}
package com.Add;import java.sql.Time;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.TimeUnit;public class Test { public static void main(String[] args) throws InterruptedException { ArrayBlockingQueue<Object> q = new ArrayBlockingQueue<>(3); //等待阻塞 q.offer(9); q.offer(6); q.offer(9); q.offer(9, 1,TimeUnit.SECONDS); //IllegalStateException System.out.println("======="); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll(2,TimeUnit.SECONDS));//没有这个元素一种阻塞 }}
SynchronousQueue同步队列
没有容量
进去一个元素,必须等待取出来之后,才能再往里面放一个元素
put,take
学科package com.Add;import java.util.concurrent.SynchronousQueue;import java.util.concurrent.TimeUnit;/** * 同步队列 * 和其他的BlockingQueue不一样,SynchronosuQueue不存储元素 *put了一个元素,必须从里面先take取出来,否则不能在put进去值! */public class SynchronousQueueDemo { public static void main(String[] args) { SynchronousQueue<Object> a = new SynchronousQueue<>(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+"Th01"); a.put("1"); System.out.println(Thread.currentThread().getName()+"Th02"); a.put("2"); System.out.println(Thread.currentThread().getName()+"Th03"); a.put("6"); } catch (InterruptedException e) { e.printStackTrace(); } },"T1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"=="+a.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"=="+a.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"=="+a.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"T2").start(); }}
学了技术不会用,看的少
11.线程池(重点)
线程池:三大方法,七大参数,四种拒接策略
池化技术
程序的运行,本质上就是会占用系统的资源,可以通过池化技术来,优化资源的使用
线程池,连接池,内存池,对象池///创建和销毁,都是十分的浪费资源
池化技术:事先准备好一些资源,有人要用,就来我这里那,用完之后还给我
线程池的好处:
- 降低资源的消耗
- 提高响应速度
- 方便管理
线程复用,可以控制最大并发数,管理线程
线程三大方法
package com.Pool;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;//Executor三大方法,工具类public class Dome01 { public static void main(String[] args) { // ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单一线程池 // ExecutorService threadPool =Executors.newFixedThreadPool(6);//创建固定线程池子 ExecutorService threadPool =Executors.newCachedThreadPool();//创建智能线程池(根据线程的多少来指定线程的数量) try { for (int i = 0; i < 100; i++) { //创建了线程池就要用线程池去调用方法,不要用new Thread().start();了 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"ok"); });//execute是执行的意思 } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown();//线程使用完成必须关闭线程池 } }}
七大参数
源码分析
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 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.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler;
手动创建线程池
package com.Pool;import java.util.concurrent.*;//拒绝策略new ThreadPoolExecutor.CallerRunsPolicy() //哪里来的灰哪里// new ThreadPoolExecutor.AbortPolicy() //抛异常// new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了会和最老的竞争,也不会抛出异常// new ThreadPoolExecutor.DiscardPolicy() //队列满了不会抛出异常,丢掉数据public class Dome01 { public static void main(String[] args) { //自定义线程池,new ThreadPoolExecutor Executors.newSingleThreadExecutor(); ExecutorService threadPool =new ThreadPoolExecutor(2,//核心线程数 5,//最大线程数 3, TimeUnit.SECONDS,//超时等待的事件 new LinkedBlockingDeque<>(3),//线程队列 Executors.defaultThreadFactory(),//线程工程 new ThreadPoolExecutor.DiscardPolicy() //拒绝策略 );//创建单一线程池 try { //最大值,就是队列+max的值 for (int i = 1; i <= 1000000; i++) { //创建了线程池就要用线程池去调用方法,不要用new Thread().start();了 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"ok"); });//execute是执行的意思 } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown();//线程使用完成必须关闭线程池 } }}
四种拒绝策略
拒绝策略
//拒绝策略new ThreadPoolExecutor.CallerRunsPolicy() //哪里来的灰哪里// new ThreadPoolExecutor.AbortPolicy() //抛异常// new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了会和最老的竞争,也不会抛出异常// new ThreadPoolExecutor.DiscardPolicy() //队列满了不会抛出异常,丢掉数据
小结和扩展
池的大小该如何去设置
了解IO密集型,cpU密集型(调优的)
- 最大线程到底该如何定义
- CPU 密集型,几核,就是几,可以保证cpu的效率最高
- IO 密集型 > 判断你程序中十分耗IO的线程
四大函数式接口(必须掌握)
新时代的程序员必须会:lambda,链式编程,函数式接口,Stream流式计算
函数式接口:只有一个方法的接口
@FunctionalInterfacepublic interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); //超级多FunctionalInterface //简化编程满血,在新版本的框架最大量引用 //foreach(消费者的函数式接口)
代码测试
Function函数式接口
package com.Pool;import java.util.concurrent.*;/** * Function 函数型接口,有个输入参数,有一个输出 * 只要是函数式接口就可以用lambad表达式来进行简化 */import java.util.function.Function;public class Dome02 { public static void main(String[] args) { Function<String,Object> function = (a)->{ return a; }; System.out.println(function.apply("3")); }}
段定形接口
package com.Pool;import java.util.concurrent.*;import java.util.function.Function;import java.util.function.Predicate;public class Dome02 { public static void main(String[] args) { Predicate<String> predicate = (str)->str.isEmpty(); System.out.println(predicate.equals(0)); }}
Consumer 消费型接口
package com.Pool;import java.util.function.Consumer;public class Dome02 { /** * Consumer 消费型接口,只有输入没有返回值 * */ public static void main(String[] args) { Consumer<String> consumer = (str)->{ System.out.println(str); }; consumer.accept("aaa"); }}
Supplier供给型接口
package com.Pool;import java.util.function.Consumer;import java.util.function.Supplier;public class Dome02 { /** * Consumer 消费型接口,只有输入没有返回值 * */ public static void main(String[] args) { Supplier supplier = ()-> 1024; System.out.println(supplier.get()); }}
Stream流式计算
什么是流式计算
大数据= 储存+计算
集合 , MySQL本质就是存储东西的;
计算都应该交给流来操作
package com.Pool;
import java.sql.Time;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Dome02 {
/**
*1.年龄必须是偶数
* 2.年龄必须大于23岁
* 3.用户名转换为大写字母
* 4.用户名字母倒着排序
* 5.只能输出一个用户
*
*/
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(5,"e",25);
User u6 = new User(6,"f",26);
//集合就是储存
List<User> list =Arrays.asList(u1,u2,u3,u4,u5,u6);
//
list.stream()
.filter(u->{return u.getID()%2 ==0;})//选择偶数
.filter(u->{return u.getAge() > 23;})//选择23以上的岁数
.map(u -> u.getName().toUpperCase() )//打印大写名字
.sorted((uu1,uu2)->uu2.compareTo(uu1))//倒着输出用户
.limit(1)//限制一个输出
.forEach(System.out::println);//打印
}
}
ForkJoin
什么是ForkJoin
ForkJoin在jdk1.7,并行执行任务!提高效率,大数据量;
大数据:Map Reduce(把大任务拆分成小任务)
FoekJoin特点:工作窃取
这个里面维护的都是双端队列
package com.Add;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class test02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//dome01();//10秒
dome03();//
}
public static void dome01(){
Long sum =0L;
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
sum+=i;
}
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public static void dome02() throws ExecutionException, InterruptedException {//会使用forJoin
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkloinDemo(0L, 1000000000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println(end-start);
} public static void dome03(){
long start = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L,1000000000L).parallel().reduce(0,Long::sum);//使用并行流
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
测试
package com.Add;import java.util.concurrent.RecursiveTask;/** * 求和计算的任务 * 3000 6000(Forkjoin) 9000(stream并行流) * 如何使用forkJoin * 计算类要继承 ForkjoinTask */public class ForkloinDemo extends RecursiveTask<Long> { private Long start; private Long end; //临界值 private Long temp = 10000L; public ForkloinDemo() { } public ForkloinDemo(Long start, Long end) { this.start = start; this.end = end; }//计算方法 @Override protected Long compute() { if ((end - start)<temp){ Long sum = 0L; for (Long i = start; i < end; i++) { sum+=1; } return sum; }else {//forkjoim方法实现 long middle = (end+start)/2;//中间值 ForkloinDemo task1 = new ForkloinDemo(start, middle); task1.fork();//拆分任务吧任务压入线程 ForkloinDemo task2 = new ForkloinDemo(middle+1,end ); task2.fork();//拆分任务把任务压入线程 return task1.join()+task2.join(); } }}
异步回调
Futur 设计的初衷:对将来的某个事物的结果进行建模
package com.Add;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;/** * 异步调用:completableFuture * 异步执行 * 成功回调 * 失败回调 */public class Dome09 { //没有返回值的异步回调 public static void main(String[] args) throws ExecutionException, InterruptedException {// CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{// try {// TimeUnit.SECONDS.sleep(2);//// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()+"runAsgnc");// });// System.out.println("1111");// completableFuture.get();//获得阻塞 CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{ int a = 10/0; System.out.println(Thread.currentThread().getName()+"Integer"); return 1024; }); System.out.println(completableFuture.whenComplete((t,u)->{ System.out.println("t="+t);//先存入的数是1024 System.out.println("u="+u);//错误信息java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero }).exceptionally((e)->{ System.out.println(e.getMessage()); return 233;//可以获取到错误的返回结果 }).get()); }}
Volatile
请你谈谈你对Volatile的理解
Volatile是java虚拟机提供轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
什么是JMM
JMM:java内存模型,不存在的东西,是一个概念,事宜约定
关于JMM的一些同步的约定:
- 线程解锁前,必须吧共享变量立刻刷回主存
- 线程加锁前,必须读取主存中的最新值到工作内存中
- 加锁和解锁是同一把锁
8种操作
MM(Java内存模型)本身是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。总结:就是规范,抽象的模型。
*
1)内存:共享的数据
2)工作空间:工作内存,基本数据类型,直接分配到工作内存,引用类型的数据将引用的地址存放在工作内存,引用的对象存放堆中。
3)工作方式:
A 线程修改私用数据,直接在工作空间修改。
B 线程修改共享数据,把数据复制到工作空间中去,在工作空间修改,修改完毕以后,再刷新内存中的数据。
2.JMM用来干什么?
JMM是Java内存模型,既然是模型,必定是能提供一种规范指导产出一种产品的东西。那么JMM指导的是什么呢?JVM就是根据JMM在物理内存划分出的结果。Java内存模型的作用是规范内存数据和工作空间数据的交互 。
3.计算机与JMM
先看看计算机的硬件内存架构:
*
计算机硬件架构存在的问题:
多个CPU并行处理缓存数据时,并发处理的不同步,会产生数据一致性问题。
解决方案:
Ⅰ. 总线加锁:CPU核执行一个线程去访问数据做操作的时候,向总线上发送一个LOCK信号,通过把内存和CPU之间的通信锁住,把并行化的操作变成了串行。虽然能解决一致性问题,但是会导致很严重的性能问题。
Ⅱ. 缓存的一致性协议(MESI):当CPU在Cache中操作数据时,如果该数据是共享变量,数据在Cache读到寄存器中,进行新修改,并更新内存数据Cache Line置无效。其他的CPU拿取数据,若Cache Line为无效会从内存中拿取。
JMM与计算机的硬件内存架构非常的相似。所以会遇到相似的问题和对应问题的解决方案。并发会有三个重要的特征,Java对这些特征有对应的保证方式:
1、原子性:不可分割,像变量的赋值x=1便是原子性,但y=x没有原子性,因为先是从x读到工作空间(原子性),把x的值写到y(原子性)。同理x++,z=z+1都没有原子性。多个原子性的操作合并没有原子性。(Java的保证操作:Synchronized 、JUC Lock的lock)
2、可见性:线程只能操作自己工作空间中的数据(Java的保证操作:Volatile、Synchronized、JUC Lock的lock)
3、有序性:程序中的顺序不一定就是执行的顺序,因为涉及编译时编译重排序、指令重排序来提高效率(Java的保证操作: Volatile、Synchronized)
问题:程序中的值已经不知道主内存中的值被修改过了
Volatile
保证可见性
package com.Add;import java.util.concurrent.TimeUnit;public class TestJMM { /** * 不加 volatile会死循环 * 加上volatile可以保证可见性 */ private volatile static int num = 0; public static void main(String[] args) throws InterruptedException { new Thread(()->{ while(num==0){//线程一是不知道程序的变化的 } }).start(); TimeUnit.SECONDS.sleep(2); num = 2; System.out.println(num); }}
package com.Add;public class VDome { //Volatilc不保证原子性 private volatile static int num = 0; private static void add(){ num++; } public static void main(String[] args) { for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int i1 = 0; i1 < 1000; i1++) { add(); } }).start(); } while (Thread.activeCount()>2){//如果线程活跃数量超过2 Thread.yield();//礼让线程 } System.out.println(Thread.currentThread().getName()+" "+num); }}
如果不加lock锁和synchronized,怎么保证原子性
使用原子类解决,原子性问题
package com.Add;
import java.util.concurrent.atomic.AtomicInteger;
public class VDome {
//Volatilc不保证原子性
//可以用AtomicInteger
private volatile static AtomicInteger num = new AtomicInteger(1);
private static void add(){
num.getAndIncrement();//num+1方法 ,gas
}
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//如果线程活跃数量超过2
Thread.yield();//礼让线程
}
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
这些类的底层都直接和操作系统挂钩!在内存中修改值!,unsafe类是一个很特殊的存在
指令重排
什么是指令重排:你写的程序, 计算机并不是按照你写的那样去执行的.
源代码->编译器优化的重排->指令并行也可能会被重排->内存系统也会重排->执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性
指令重排可能造成影响的家国:abxy这四个默认值都是0;
| 线程a | 线程b |
|---|---|
| x=a | y=b |
| b=1 | a=2 |
正常的结果:x=0,Y=0但是可能由于指令重排
| 线程a | 线程b |
|---|---|
| b=1 | a=2 |
| x=a | y=b |
指令重排导致的诡异结果:x=2;y=1;
Volatile可以避免指令重排
内存屏障,cpu指令.作用:
- 保证特点的操作的执行顺序
- 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)
Volatile 是保持可见性,不保证原子性,可以避免指令重排的现象产生
彻底玩耍单列模式
饿汉式,DCL懒汉式,深究!

浙公网安备 33010602011771号