JUC 并发编程

学习方法,学习新知识 要结合以前得旧知识 再结合实战 组成新知识 体系知识

文章目录

  • JUC 并发编程
    • 1.什么是juc
    • 2.线程和进程与锁
      • 线程有几个状态
      • wait和sleep区别
      • lock锁(重点)
      • lock锁与synchronized锁区别
      • 什么是锁 如何判断锁是谁
      • 生产者与消费者问题
      • JUC版本生产者消费者关系
      • **8锁概念**
        • 静态方法 锁得概念
    • 集合类不安全
      • COPYONWRITEARRAYLIST(LIST不安全)
      • SET不安全
      • Map(不安全)
      • Callable(easy)
    • 三大常用辅助类
      • countDownLatch
      • cyclicBarrier
      • semaphore
    • 读写锁
    • 阻塞队列BlockingQueue
      • synchronizedquery同步队列
    • 线程池
      • 7大参数
      • 最大线程数定义(调优)
        • IO密集型
        • cpu密集型
      • 四大函数式接口(新时代必会)
    • stream流式计算
    • ForkJoin
    • 异步回调
    • JMM
      • 什么是Volatile
      • 什么是jmm
        • 关于jmm的一些同步约定
      • jmm8种规定
      • Volatile
      • 指令重排
    • 彻底玩转单例模式
      • 饿汉单例模式 会造成资源浪费
      • DCL懒汉式
      • 静态内部类单例模式
    • CAS深究
      • 原子引用(对应是思想 乐观锁)
    • 各个锁的理解
      • 可重入锁 概念 只有进了一个锁 里面就等于都获取 就等于 进大门 等于卧室门也能进入
      • 公平锁, 非公平锁
      • ==synchronized==
      • ==lock版 可重入锁==
      • 自旋锁 SpinLock
      • 死锁排查解决

1.什么是juc

源码+官方文档 学习方法

java.util 工具类、包、分类

thread 普通线程代码

runnable 效率低 callable效率高 有返回值

2.线程和进程与锁

进程:一个程序,qq.exe music.exe 程序集合;

一个进程往往包含多个线程,至少包含一个!

java默认有几个线程?2个 main GC

线程:开了一个进程typora 比如我在写入 (自动保存)其实就是一个个线程在处理;

java能开启线程嘛

答:不可以得 因为java是一个虚拟机程序 不能去操作硬件 实际了 start方法调用本地native方法调用c++来开启线程

   private native void start0();

并发、并行

并发(多线程操作一个资源)

  • 单核cpu操作多个线程 其实是虚拟得 天下武功,唯快不破 其实就是快速交替

并行(多个人以前行走)

  • cpu多核操作多个线程 线程池
//查看cpu核数
public class Thread01 {public static void main(String[] args) {System.out.println(Runtime.getRuntime().availableProcessors());}

并发编程核心:充分利用cpu资源

所有公司都很看重

企业-挣钱-提高效率-裁员 =一个厉害得人>=三个废物

线程有几个状态

 public enum State {//新生NEW,//运行RUNNABLE,//阻塞BLOCKED,//等待 死死得等WAITING,//超时等待 一定时间TIMED_WAITING,//终止TERMINATED;}

wait和sleep区别

1.来自不同的类

wait->object

sleep->thread

2.关于锁的释放

wait会释放锁 等待 会主动释放

sleep不会释放锁 抱着锁睡

3.使用的范围不同

wait 只能在同步代码块使用 配置notify 使用

sleep 可以在任何地方使用 企业用法 timeunit

TimeUnit.DAYS.sleep(1);

4.是否需要捕获异常

sleep 需要捕获异常

wait 不需要捕获异常

lock锁(重点)

传统 synchronized 隐似同步锁
 Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }

公平锁:十分公平 先来后到 如果3s 3h 3h先到就要一直等待 才能执行3s的

非公平锁:十分不公平,可以插队 (默认)

LOCK锁代码

package com.hy.lock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** 买票案例* 企业级开发 降低耦合性 单一资源类* 多线程操作同一资源类*/
public class SaleTicketDemo02 {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();}
}
class Ticket {/*** oop 写法* 属性* 方法*/private int number = 50;  //总票数Lock lock=new ReentrantLock();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();}}
}

lock锁与synchronized锁区别

  1. synchronized 内置的java关键字,lock是一个java类
  2. synchronized 无法判断获取锁的状态,lock可判断是否获取锁
  3. synchronized 会自动释放锁 lock必须手动释放 如果不释放就 deadlock
  4. synchronized 线程1(获取锁,阻塞)、线程2(等待 一直等);lock锁就不一定一直等待
  5. synchronized 可重入锁,不可以中断,非公平;lock 可重入锁,可以判断锁,非公平(可以设置)自由
  6. synchronized 适合锁少量代码同步问题,lock适合锁大量代码

什么是锁 如何判断锁是谁

synchronized 同步 就是锁 如果加在方法上锁得就是 调用者 如果锁得是静态的 就是锁类模板

lock 默认锁得就是对象

生产者与消费者问题

synchronized 版  生产者与消费者
/*** 生产者消费者 通信 关系* 线程 A B 同时操作一个资源* A ++* B --*/
public class SynchronizedCommunication {public static void main(String[] args) {Data data=new Data();//线程代码new Thread(()-> {for (int i = 0; i < 10; i++) {try {data.incrment();} catch (InterruptedException e) {e.printStackTrace();}}},"A").start();new Thread(()-> {for (int i = 0; i < 10; i++) {try {data.decrment();} catch (InterruptedException e) {e.printStackTrace();}}},"B").start();}
}/*** 资源类* 判断等待 业务 通知*/
class Data{private  int number=0;public synchronized  void incrment() throws InterruptedException {if (number!=0){System.out.println(Thread.currentThread().getName()+"线程=>"+number);//释放锁 等待this.wait();}number++;//通知this.notifyAll();}public synchronized  void decrment() throws InterruptedException {if (number==0){System.out.println(Thread.currentThread().getName()+"线程=>"+number);//释放锁 等待this.wait();}number--;//通知this.notifyAll();}

如果有 A B C D四条现场这样写就有问题存在 虚假唤醒

因为if 只判断一次 所以改用while 会解决该问题

JUC版本生产者消费者关系

通过新版得lock锁 实现 可以控制顺序执行线程 消费者生产者完美执行标准

/*** lock 可以解决排序 问题 可以准确通知 下一个谁来执行*/
public class Block2 {public static void main(String[] args) {Data3 data3 = new Data3();new Thread(()-> { {try {for (int i = 0; i < 10; i++) {data3.A();}} catch (InterruptedException e) {e.printStackTrace();}}},"A").start();new Thread(()-> { {try {for (int i = 0; i < 10; i++) {data3.B();}} catch (InterruptedException e) {e.printStackTrace();}}},"B").start();new Thread(()-> { {try {for (int i = 0; i < 10; i++) {data3.C();}} catch (InterruptedException e) {e.printStackTrace();}}},"C").start();new Thread(()-> { {try {for (int i = 0; i < 10; i++) {data3.D();}} catch (InterruptedException e) {e.printStackTrace();}}},"D").start();}
}
class Data3{private Lock lock=new ReentrantLock();Condition condition1 = lock.newCondition();Condition condition2 = lock.newCondition();Condition condition3 = lock.newCondition();Condition condition4 = lock.newCondition();private int number=1;  //等于一个flag 标志位public void A() throws InterruptedException {try {lock.lock();while (number!=1){condition1.await();}number=2;  //标志位1System.out.println(Thread.currentThread().getName()+"AAAAAA");//通知1 执行condition2.signal();} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}public void B() throws InterruptedException {try {lock.lock();while (number!=2){condition2.await();}number=3;  //标志位1System.out.println(Thread.currentThread().getName()+"BBBBBB");//通知1 执行condition3.signal();} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}public void C() throws InterruptedException {try {lock.lock();while (number!=3){condition3.await();}number=4;  //标志位1System.out.println(Thread.currentThread().getName()+"CCCCCC");//通知1 执行condition4.signal();} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}public void D() throws InterruptedException {try {lock.lock();while (number!=4){condition4.await();}number=1;  //标志位1System.out.println(Thread.currentThread().getName()+"DDDDDD");//通知1 执行condition1.signal();} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}

8锁概念

package com.hy.lock;import java.util.concurrent.TimeUnit;/*** 8 kind of lock concept锁作为 关键字在方法上 默认锁得是 this 如果作用在代码块 锁得就是对象*/
public class CPhone {public static void main(String[] args) {//这里锁得就是check 锁得方法调用者  所以那个先调用 就那个先加锁Check check =new Check();new Thread(() ->{try {check.send();TimeUnit.SECONDS.sleep(1);}catch (Exception e){e.printStackTrace();}},"A").start();new Thread(() ->{ check.hello();},"B").start();}
}/*** 资源类  符合 oop思想*/
class Check{public synchronized void  send(){try {System.out.println("发短信");TimeUnit.SECONDS.sleep(4);} catch (Exception e) {e.printStackTrace();}}public synchronized void  call(){System.out.println("打电话");}public synchronized void hello(){System.out.println("hello");}
}
//这里先执行得就会是 发短信
/*** 模拟 锁得 调用 静态 和 非静态区别*/
//这个时候执行得 打电话 因为锁得默认是对象调用者
public class CPhone {public static void main(String[] args) {//其实就是两把锁//phone 被阻塞 但是 对象phone1也是调用者 所以也可以执行 所以就会直接执行Phone phone=new Phone();Phone phone1=new Phone();new Thread(() ->{try {phone.send();TimeUnit.SECONDS.sleep(1);}catch (Exception e){e.printStackTrace();}},"A").start();new Thread(() ->{phone1.call();{}},"B").start();}
}
class Phone{public synchronized void send(){try {TimeUnit.SECONDS.sleep(5);System.out.println("发短信");} catch (Exception e) {e.printStackTrace();}}public synchronized void call(){System.out.println("打电话");}
}

静态方法 锁得概念

静态方法 多个对象 这个时候锁 锁得是类模板 class文件 默认只有一个 所以 先调用得对象 就会得到模板锁 其余得就不能调用啦 所以这里就会依旧是 A先执行

/*** 模拟 锁得 调用 静态 和 非静态区别* 默认锁得是类模板  所以A线程先去调用 虽然有两个对象 但是模板只有一个 所以A调用了 等于模板加锁 所以要等A* 执行完毕 才能执行B*/
public class DPhone {public static void main(String[] args) {CD cd=new CD();CD cd1=new CD();new Thread(() ->{try {cd1.call();TimeUnit.SECONDS.sleep(1);}catch (Exception e){e.printStackTrace();}},"A").start();new Thread(() ->{cd.send();{}},"B").start();}
}class CD{public static synchronized void call(){try {TimeUnit.SECONDS.sleep(5);System.out.println("打电话");} catch (Exception e) {e.printStackTrace();}}public static synchronized void send(){System.out.println("发短信");}
}

静态锁锁得是CLASS 和普通锁锁得是this 不是一个锁

import java.util.concurrent.TimeUnit;
/*** 模拟 锁得 调用 静态 和 非静态区别* 一个锁的是模板 一个锁得是调用者 等于两把锁 所以下面b会执行*/
public class DPhone {public static void main(String[] args) {CD cd=new CD();CD cd1=new CD();new Thread(() ->{try {cd1.call();TimeUnit.SECONDS.sleep(1);}catch (Exception e){e.printStackTrace();}},"A").start();new Thread(() ->{cd.send();{}},"B").start();}
}class CD{public static synchronized void call(){try {TimeUnit.SECONDS.sleep(5);System.out.println("打电话");} catch (Exception e) {e.printStackTrace();}}public  synchronized void send(){System.out.println("发短信");}
}

集合类不安全

COPYONWRITEARRAYLIST(LIST不安全)

   public static void main(String[] args) {/*** 这种就是不安全 解决方案* 1.直接用线程安全得 Vector* 2.可以使用 集合帮助类 调用synchronizedList 进行集合加锁得方法解决*/// List list=new ArrayList();//方法1 List list=new Vector();//方法2  List list= Collections.synchronizedList(new ArrayList<>());/*** 为什么要用CopyOnWriteArrayList  写入时复制* 避免 覆盖数据 在写入得时候 复制一个 写完后在插入* Vector 和 copyarraylist区别 * Vector一个是 synchronized 所以性能方面 没有copyarraylist好* copyarraylist 用的是lock锁 所以性能要好很多*/List list=new CopyOnWriteArrayList();for (int i = 1; i < 10; i++) {new Thread(() ->{{list.add(UUID.randomUUID().toString().substring(0,5));System.out.println(list);}},String.valueOf(i)).start();}}

SET不安全

和LIST 同理

public class SetCur {/*** 和 list 同理* 不过set 只有两个解决方案* 1 使用帮助类 加锁* 2 CopyOnWriteArraySet* @param args*/public static void main(String[] args) {// Set set = new HashSet<>();‘// Set<String> set = Collections.synchronizedSet(new HashSet<String>()Set set = new CopyOnWriteArraySet();for (int i = 1; i < 50; i++) {new Thread(() -> {{set.add(UUID.randomUUID().toString().substring(0, 10));System.out.println(set);}}, String.valueOf(i)).start();}}

hashSet 底层是什么

 public HashSet() {map = new HashMap<>();  //底层就是hashmap}
//利用l map得key 因为key是不可重复得public boolean add(E e) {  //添加方法  return map.put(e, PRESENT)==null;}

Map(不安全)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5O3t95TL-1619055586305)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20210323204236320.png)]

  • 内存一致性效果:与其他并发集合一样,在将对象放入ConcurrentMap作为键或happen-before之前的线程中的操作,在另一个线程中从ConcurrentMap访问或删除该对象之后

主要是保证了原子性

public class MapCur {public static void main(String[] args) {// Map<String, Object> map =new HashMap<>();Map<String, Object> map =new ConcurrentHashMap<>();for (int i = 0; i <50 ; i++) {new Thread(()->{map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(1,5));System.out.println(map);},String.valueOf(i)).start();}}

ConcurrentHashMap

  1. 将当前 Segment 中的 table 通过 key 的 hashcode 定位到 HashEntry。
  2. 遍历该 HashEntry,如果不为空则判断传入的 key 和当前遍历的 key 是否相等,相等则覆盖旧的 value。
  3. 不为空则需要新建一个 HashEntry 并加入到 Segment 中,同时会先判断是否需要扩容。
  4. 最后会解除在 1 中所获取当前 Segment 的锁。
  5. 根据 key 计算出 hashcode 。
  6. 判断是否需要进行初始化。
  7. f 即为当前 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋保证成功。
  8. 如果当前位置的 hashcode == MOVED == -1,则需要进行扩容。
  9. 如果都不满足,则利用 synchronized 锁写入数据。
  10. 如果数量大于 TREEIFY_THRESHOLD 则要转换为红黑树。

ConcurrentHashMap put源码

public V put(K key, V value) {Segment<K,V> s;if (value == null)throw new NullPointerException();int hash = hash(key);int j = (hash >>> segmentShift) & segmentMask;if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck(segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegments = ensureSegment(j);return s.put(key, hash, value, false);
}

ConcurrentHashMap get源码

public V get(Object key) {Segment<K,V> s; // manually integrate access methods to reduce overheadHashEntry<K,V>[] tab;int h = hash(key);long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&(tab = s.table) != null) {for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);e != null; e = e.next) {K k;if ((k = e.key) == key || (e.hash == h && key.equals(k)))return e.value;}}return null;
}

ConcurrentHashMap 1.8以后锁得是node 锁的粒度更低

因为1.8后 改用数组 链表 达到阈值后使用红黑树 放弃了分段锁而是用了Node锁,减低锁的粒度,提高性能,并使用CAS操作来确保Node的一些操作的原子性,取代了锁。

JDK1.7中,ConcurrentHashMap从过二次hash的方式(Segment -> HashEntry)能够快速的找到查找的元素。在1.8中通过链表加红黑树的形式弥补了put、get时的性能差距。

hashtable 与 ConcurrentHashMap区别

  • 底层 数组和链表 1.8 到达最大阈值 可以设置位红黑树

  • hashtable put get每次都会进行同步锁

  • ConcurrentHashMap 里面是用的lock锁 所以比较高效 get乐观锁

  • ConcurrentHashMap get 只需要将 Key 通过 Hash 之后定位到具体的 Segment ,再通过一次 Hash 定位到具体的元素上。

    由于 HashEntry 中的 value 属性是用 volatile 关键词修饰的,保证了内存可见性,所以每次获取时都是最新值。

    ConcurrentHashMap 的 get 方法是非常高效的,因为整个过程都不需要加锁

ConcurrentHashMap 采用了分段锁技术,其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。

Callable(easy)

callable 怎么启动 原理探究

runnable 的实现类 futuretask

futuretask 里面可以方callable 所以就实现啦 runnable和callable 连接 然后就可以间接使用 thread跑线程

Constructor and Description
FutureTask(Callable<V> callable)创建一个 FutureTask ,它将在运行时执行给定的 Callable
FutureTask(Runnable runnable, V result)创建一个 FutureTask ,将在运行时执行给定的 Runnable ,并安排 get将在成功完成后返回给定的结果。

最后就形成了完美解决方案 流程图

代码附上

public class CallableTest {public static void main(String[] args) throws ExecutionException, InterruptedException {// new Runnable()//new Thread().start();  //线程启动方式只有者一中/*** 用到 runnable线程实现类 FutureTask 间接和thread关联*/MyThread myThread=new MyThread();FutureTask futureTask=new FutureTask<>(myThread);new Thread(futureTask).start();Integer o = (Integer) futureTask.get();System.out.println(o);}
}
class MyThread implements Callable<Integer>{@Overridepublic Integer call() throws Exception {System.out.println("call()");return 1024;}
}

细节:有缓存 还会阻塞

三大常用辅助类

countDownLatch

放代码。。。

概念 类似于计数器 统计线程 线程跑完 关闭

{//必须要执行任务的时候 在使用CountDownLatch countDownLatch=new CountDownLatch(6); //初始化 6个for (int i = 1; i <=6 ; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "god dd");countDownLatch.countDown(); //每次-1}, String.valueOf(i)).start();}countDownLatch.await();  //线程等待结束 往下执行 结束System.out.println("god bye");}countDownLatch.countDown()  //每次执行减一countDownLatch.await();  //相当于计数器 当对于0 就唤醒向下执行结束

cyclicBarrier

概念 ++计数器 每次+1 直到得到设置结果 执行完毕

/*** ++计数器* 加到一定量 就开启新的线程 执行结束*/
public class Cyclicbarrier {public static void main(String[] args) throws BrokenBarrierException, InterruptedException {CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()-> {System.out.println(Thread.currentThread().getName() + "集齐龙珠召唤神龙");});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();}};

semaphore

程序计数器 使用场景 共享互斥 限流

*** 程序计数器  限流 、* 停车场案例*/
public class semaphoreTest {public static void main(String[] args) {Semaphore semaphore=new Semaphore(5);  //设置默认能进入的数量for (int i = 1; i <10 ; i++) {new Thread(() -> {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();  //释放资源 或者通知}},String.valueOf(i)).start();}}

semaphore.acquire() 获取资源

semaphore.release() 释放资源

读写锁

ReadWriteLock

  • ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。 read lock可以由多个阅读器线程同时进行,只要没有作者。 write lock是独家的。

独占锁(就是写锁)

共享锁(就是读锁)

上代码。。

/*** 读写锁 案例  比如缓存 读取 和 写入 写只能一个线程 而读可以多线程* 读 写 (不能共存)* 读 读 (可以共存)* 写 写 (不能共存)*/public class ReadWriteLockTest {//创建线程public static void main(String[] args) {MyCache myCache=new MyCache();for (int i = 1; i <10 ; i++) {final int temp = i;new Thread(()->{myCache.put(temp+"",temp+"");},String.valueOf(i)).start();}for (int i = 1; i <10 ; i++) {final int temp = i;new Thread(()->{myCache.get(temp+"");},String.valueOf(i)).start();}}}//缓存cache资源类class MyCache{private  volatile Map<String, Object> map =new HashMap<>();private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();public void put(String key,Object value){reentrantReadWriteLock.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 {reentrantReadWriteLock.writeLock().unlock();}}public void get(String key){reentrantReadWriteLock.readLock().lock();try {System.out.println(Thread.currentThread().getName()+"读取"+key);map.get(key);System.out.println(Thread.currentThread().getName()+"读取结束");} catch (Exception e) {e.printStackTrace();} finally {reentrantReadWriteLock.readLock().lock();}}

阻塞队列BlockingQueue

BlockingQueue 不是新东西

队列概念 父子级别

使用场景 多线程 线程池

四组Api

方式 抛出异常 不抛异常有返回值 阻塞等待 超时等待
添加 add offer put offer
移除 remove poll take poll
判断队列首 element element
public static void test1(){BlockingQueue<String> blockingQueue=new LinkedBlockingQueue<>(3);System.out.println(blockingQueue.add("a"));System.out.println(blockingQueue.add("b"));System.out.println(blockingQueue.add("c"));//System.out.println(blockingQueue.add("d"));System.out.println(blockingQueue.element());  //判断队列首System.out.println("-----------------------------------");System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());}
public static void test2(){BlockingQueue<String> blockingQueue=new LinkedBlockingQueue<>(3);//如果超出了 就会返回false 不会抛出异常System.out.println(blockingQueue.offer("a"));System.out.println(blockingQueue.offer("b"));System.out.println(blockingQueue.offer("c"));System.out.println(blockingQueue.offer("d"));//System.out.println(blockingQueue.element());System.out.println("-----------------------------------");System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());}
public static void test3() throws InterruptedException {BlockingQueue<String> blockingQueue=new LinkedBlockingQueue<>(3);//阻塞 死死得等 如果没有 就一直等待blockingQueue.put("a");blockingQueue.put("a");blockingQueue.put("a");System.out.println(blockingQueue);//System.out.println(blockingQueue.element());System.out.println("-----------------------------------");System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());}
public static void test4() throws InterruptedException {BlockingQueue<String> blockingQueue=new LinkedBlockingQueue<>(3);//超时等待  等到指定时间才会结束System.out.println(blockingQueue.offer("b"));System.out.println(blockingQueue.offer("c"));System.out.println(blockingQueue.offer("d"));System.out.println(blockingQueue.offer("a",5, TimeUnit.SECONDS));//System.out.println(blockingQueue.element());System.out.println("-----------------------------------");System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());}

synchronizedquery同步队列

模拟代码

public static void main(String[] args) {BlockingQueue<String> blockingQueue=new SynchronousQueue<>();new Thread(()->{try {System.out.println(Thread.currentThread().getName()+"PUT1");blockingQueue.put("1");System.out.println(Thread.currentThread().getName()+"PUT2");blockingQueue.put("2");System.out.println(Thread.currentThread().getName()+"PUT3");blockingQueue.put("3");} catch (InterruptedException e) {e.printStackTrace();}},"T1").start();new Thread(()->{try {TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+blockingQueue.take());TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+blockingQueue.take());TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+blockingQueue.take());} catch (InterruptedException e) {e.printStackTrace();}},"T2").start();
}

线程池

池化技术=》 优化资源使用

线程池 连接池 对象池 常量池 都是为了 解决创建 销毁资源浪费

池化技术 事先准备好资源 有人要用就来那 用完还给我

线程池好处

  1. 降低资源得消耗
  2. 提高响应得速度
  3. 方便管理

线程复用 可以控制最大并发数 方便管理

//不过推荐 还是用这个创建线程次池
//这是Executors 的原生手段
public ThreadPoolExecutor(int corePoolSize, //默认大小int maximumPoolSize, //最大线程long keepAliveTime,  //超时等待时间TimeUnit unit,  //单位BlockingQueue<Runnable> workQueue) {  //阻塞队列this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);
}
/*** 线程池得 三大方法* Executors 工具类*/
public class ThreadPool {public static void main(String[] args) {//ExecutorService service = Executors.newSingleThreadExecutor();//单一线程// ExecutorService service = Executors.newFixedThreadPool(5); //参数线程 默认5 最大5ExecutorService service =  Executors.newCachedThreadPool();  //缓存线程 伸缩版 哈哈哈try {for (int i = 0; i < 10; i++) {service.execute(() -> {System.out.println(Thread.currentThread().getName() + "A");});}} catch (Exception e) {e.printStackTrace();} finally {service.shutdown();}}

7大参数

/*** AbortPolicy 拒绝策略 如果超出线程和队列数 抛出异常* CallerRunsPolicy 哪来的回哪去 如果超出了 就回到来的线程 执行* DiscardPolicy  不抛出异常 直接丢弃*DiscardOldestPolicy 尝试和第一条竞争 失败照样给我丢弃啦* 承载最大 线程数+队列数* @param args*/
public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 3,TimeUnit.SECONDS, new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());try {for (int i = 0; i < 9; i++) {executor.execute(() -> {System.out.println(Thread.currentThread().getName() + "ok");});}} catch (Exception e) {e.printStackTrace();} finally {executor.shutdown();}
}
corePoolSize #初始线程数
maximumPoolSize #最大线程数
keepAliveTime   #超时策略
TimeUnit        #单位
BlockingQueue<Runnable> #队列
threadFactory #线程工厂 默认 用工具类调用
RejectedExecutionHandler #拒绝策略

最大线程数定义(调优)

IO密集型

比较耗费资源的线程 找到 然后给定线程数>比较耗费资源的线程 一般都是双倍

cpu密集型

如何让资源使用到机制 判断运行时 cpu数量 来设置线程max

 Runtime.getRuntime().availableProcessors() //设置线程最大

四大函数式接口(新时代必会)

lambda 表达式 函数型接口 链式编程 stream流计算
函数式接口 简化 编程
foreaeh (消费者函数接口 )

1.函数式接口 function

源码

public interface Function<T, R> {  //传递一个 t 返回一个R/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);
public class FunctionTest {public static void main(String[] args) {Function stringFunction = new Function<String, String>() {@Overridepublic String apply(String S) {return r;}};//lombra 表达式写法 Function stringFunction = (s)->{return s;};System.out.println(stringFunction.apply("adada"));}

2.Predicate 判断是否为空

public class praticateTest {public static void main(String[] args) {Predicate predicate=new Predicate<String>() {@Overridepublic boolean test(String o) {return o.isEmpty();}};System.out.println(predicate.test(""));}
}

3.consumer 消费者函数型接口

只有输入 没有返回

  public static void main(String[] args) {//        Consumer consumer=new Consumer<String>() {//            @Override
//            public void accept(String o) {//                System.out.println(o);
//
//            }
//        };Consumer consumer= (s)->{System.out.println(s);};consumer.accept("aaaa");}

4.supplier 供给型接口 只有返回值

只有返回 没有输入

public static void main(String[] args) {Supplier supplier=new Supplier<Integer>() {@Overridepublic Integer get() {return 1024;}};System.out.println(supplier.get());
}

stream流式计算

什么是流式计算

存储数据 集合 mysql

计算 stream流

/*** 一分钟完成 只能一行代码* 5个 用户* id 为偶数* 年龄 23* 用户名转大写* 倒叙* 只显示一个用户*/
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> list= Arrays.asList(u1,u2,u3,u4,u5); //数组转换为list对象//链式编程list.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);}

ForkJoin

分支合并

ForkJoin 并行执行任务 提高效率 大数据量

大数据:map reduce(任务拆分 大任务 拆分成一个个小任务)

概念图

ForkJoin特点 工作窃取

/*** forkJoin 拆分操作*/
public class FaskJoinTest extends RecursiveTask<Long> {private Long start ;private Long end ;Long temp = 10_0000_0000L;public FaskJoinTest(Long start, Long end) {this.start = start;this.end = end;}@Overrideprotected  Long compute() {if ((end - start) < temp) {Long sum = 0L;for (int i = 0; i < 1000000000; i++) {sum += i;}return sum;} else {long limit = (end - start) / 2;FaskJoinTest faskJoinTest = new FaskJoinTest(start, limit);faskJoinTest.fork(); //拆分任务压缩到线程队列FaskJoinTest faskJoinTest1 = new FaskJoinTest(limit + 1, end);faskJoinTest1.fork();return faskJoinTest1.join() + faskJoinTest.join();}}
}
 public static void main(String[] args) throws ExecutionException, InterruptedException {tset1();  //传统傻瓜式做法 时间 4174// test2();  //升级版 forkjoin程序员 时间:7556// test3();    //1999999999000000000  时间:384  流式速度最快privte volate Long SUM=2_0000_0000;}//方式1public static void tset1(){Long start=System.currentTimeMillis();Long sum=0L;for (int i = 0; i <20_0000_0000; i++) {sum+=i;}Long end= System.currentTimeMillis();System.out.println("时间:"+(end-start)+"sum"+"/n"+sum);}public static void test2() throws ExecutionException, InterruptedException {Long start=System.currentTimeMillis();ForkJoinPool forkJoinPool=new ForkJoinPool();ForkJoinTask<Long> task = new FaskJoinTest(0L, SUM);ForkJoinTask<Long> submit = forkJoinPool.submit(task);  //提交 有returnLong aLong = submit.get();System.out.println(aLong);Long end=System.currentTimeMillis();System.out.println("时间:"+(end-start));}public static void test3(){// 流式计算 未来的佼佼者Long start=System.currentTimeMillis();long reduce = LongStream.range(0L, 20_0000_0000).parallel().reduce(0, Long::sum);System.out.println(reduce);Long end=System.currentTimeMillis();System.out.println("时间:"+(end-start));}
}

流式计算为什么那么快

重点 因为是 并行执行 将cpu核数 资源使用到极致

并发 并行 串行流 采用的无状态 操作放入 链表 然后在终结任务执行

并行流 基本差不多 但是采用啦 forkjoin 分片处理

异步回调

Future 设计的初衷:对将来的某个事件结果进行建模!

其实就是前端 --> 发送ajax异步请求给后端

但是我们平时都使用CompletableFuture

第一种没有返回值的 异步回调

public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> integerCompletableFuture =CompletableFuture.runAsync(()->{try {TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+"A");} catch (Exception e) {e.printStackTrace();}});System.out.println("11111111111");Void unused = integerCompletableFuture.get();
}

第二种有返回值的 异步回调 成功或失败 类似于 success 200 error 404 500

 CompletableFuture<Integer> integerCompletableFuture =CompletableFuture.supplyAsync(()->{System.out.println(Thread.currentThread().getName());int i=10/0;return 1024;});System.out.println(integerCompletableFuture.whenComplete(//异步 success函数回调(t,s)->{System.out.println("t="+t);System.out.println("s="+s);}).exceptionally((e)->{System.out.println(e.getMessage());return 404;}).get());}
}
//输出
ForkJoinPool.commonPool-worker-1
t=null
s=java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
404

whenComplete: 有两个参数,一个是t 一个是u

T:是代表的 正常返回的结果

U:是代表的 抛出异常的错误信息

如果发生了异常,get可以获取到exceptionally返回的值;

JMM

什么是Volatile

Volatile是java虚拟机提供的轻量级同步机制

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排序

可见性概念

立即将缓存中数据写会到内存中

其他处理器通过嗅探总线上传播过来了数据监测自己缓存的值是不是过期了,如果过期了,就会对应的缓存中的数据置为无效。而当处理器对这个数据进行修改时,会重新从内存中把数据读取到缓存中进行处理。

在这种情况下,不同的CPU之间就可以感知其他CPU对变量的修改,并重新从内存中加载更新后的值,因此可以解决可见性问题。

什么是jmm

jmm: java的内存模型 不存在的东西 概念 思想 约定

关于jmm的一些同步约定

  1. 线程解锁前,必须把共享变量立刻刷新到主存储
  2. 线程加锁前 必须读取主存种的最新值到工作内存中
  3. 加锁和解锁是同一把锁

线程 工作内存 主内存

8种操作

如果这样就会出现一个问题 线程B修改变量为flase 刷新到主存 但是线程A还在执行 flag=true 所以线程A此时获取的就不是最新变量

jmm8种规定

  • lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
  • unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则:

  • 如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作, 如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。但Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。
  • 不允许read和load、store和write操作之一单独出现
  • 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
  • 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

Volatile

验证

保证可见性

public class VolatileTest {private volatile static int num=0;public  static void main(String[] args) {new Thread(()->{while (num==0){}}).start();try {TimeUnit.SECONDS.sleep(3);}catch (Exception e){}num=1;System.out.println(num);}
}

非原子性

原子性 不可分割的

public class Vtest2 {private volatile static  int num=0;public  static void add(){num++;  //本身就不是一个原子性操作  javap -c 项目名 (反编译操作)}public  static void main(String[] args) {for (int i = 1; i <10 ; i++) {new Thread(()->{for (int j = 0; j <1000 ; j++) {add();}}).start();}System.out.println(Thread.activeCount()>2);Thread.yield();System.out.println(Thread.currentThread().getName()+num);}

此时发现值还是会有问题 说明没有保证原子性

可以加锁来保证 只不过那也比较耗费资源

推荐使用原子类来处理

通过原子类 提供原子性

public class Vtest2 {private volatile static AtomicInteger num=new AtomicInteger();public  static void add(){num.incrementAndGet();  //相当于++操作 但是底层是调用啦 cpu的cas//本身就不是一个原子性操作  javap -c 项目名 (反编译操作)}public  static void main(String[] args) {for (int i = 1; i <10 ; i++) {new Thread(()->{for (int j = 0; j <1000 ; j++) {add();}}).start();}System.out.println(Thread.activeCount()>2);Thread.yield();System.out.println(Thread.currentThread().getName()+num);}
}

指令重排

什么是指令重排:你写的程序 并不是按照你写的那也去执行的;

源代码–>编译器优化的重排–>指令并行也可能重排–>内存系统也会重排–>执行

线程A 线程B
x=b y=a
a=1 b=2

正常执行 应该是0

线程A 线程B
a=1 b=3
y=b x=a

指令重排的诡异操作 可能导致答案异常 y=3 x=1

volatile 怎么保证防止指令重排序

内存屏障,cpu指令 作用

保证啦操作系统的执行顺序

可以保证某些变量的内存可见性(利用了这些特性保证啦 volatile可见性)

小结 volatile 是可以保持的 可见性。不能保证原子性,由于内存屏障 可以避免指令重排现象

彻底玩转单例模式

饿汉单例模式 会造成资源浪费

/*** 饿汉式*/
public class Hungry {/*** 可能会浪费空间  因为我们不用的时候 也会初始化这些对象*/private byte[] data1=new byte[1024*1024];private byte[] data2=new byte[1024*1024];private byte[] data3=new byte[1024*1024];private byte[] data4=new byte[1024*1024];public Hungry() {}private final static Hungry hungry=new Hungry();public static Hungry getInstance(){return hungry;}
}

DCL懒汉式

二种防止反射破坏 策略

双重锁检测

通过定义标志位 加锁

public class LazyMan {private static boolean flag=false;public LazyMan(){synchronized (LazyMan.class){if (flag==false){flag=true;}else{throw new RuntimeException("不要试图用反射搞我");}}System.out.println(Thread.currentThread().getName()+"ok");}/*** 1、分配内存空间* 2、执行构造方法,初始化对象* 3、把这个对象指向这个空间*就有可能出现指令重排问题*比如执行的顺序是1 3 2 等*我们就可以添加volatile保证指令重排问题*/private volatile static LazyMan lazyMan;  //防止指令重排 而导致占用了空间 没初始化 返回未初始化对象public static LazyMan getInstance() throws Exception {//双重检测锁模式 简称DCL懒汉式 解决啦 多条线程进入的问题if (lazyMan==null) {synchronized (LazyMan.class) {if (lazyMan == null) {lazyMan = new LazyMan();System.out.println(Thread.currentThread().getName() + "ok");}}}return lazyMan;}public static void main(String[] args) throws Exception {//        for (int i = 0; i <10 ; i++) {//            new Thread(()->{//                LazyMan instance = LazyMan.getInstance();
//            }).start();
//        }//反射改变对象  获取构造器 来破坏单例模式Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);Field flag = LazyMan.class.getDeclaredField("flag");flag.setAccessible(true);declaredConstructor.setAccessible(true); //无视私有构造器LazyMan lazyMan = declaredConstructor.newInstance();LazyMan lazyMan1 = declaredConstructor.newInstance();flag.set(lazyMan1,false);System.out.println(lazyMan);System.out.println(lazyMan1);}
}

静态内部类单例模式

public class Holder {public Holder() {}public static Holder getInstance(){return InnerClass.holder;}private static class InnerClass{private static volatile Holder holder=new Holder();}
}

单例不安全, 因为反射

/*** 枚举本身就是一个 class*/
public enum EnumTest {INSTANCE;public EnumTest getInstance(){return INSTANCE;}public static void main(String[] args) throws Exception {Constructor<EnumTest> declaredConstructor = EnumTest.class.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);EnumTest enumTest = declaredConstructor.newInstance();EnumTest enumTest1 = declaredConstructor.newInstance();System.out.println(enumTest1==enumTest);}
}

此时报了个特别的错误

Exception in thread "main" java.lang.NoSuchMethodException: com.hy.single.EnumTest.<init>()at java.lang.Class.getConstructor0(Class.java:3110)at java.lang.Class.getDeclaredConstructor(Class.java:2206)at com.hy.single.EnumTest.main(EnumTest.java:15)

并没有报enum 官方的防止反射错误 是为什么呢

=原来如此 我们都被idea骗了 通过反编译知道他默认不是无参 而是带有参数的 所以我们反射就会报main错误 真相大白

public static EnumSingle[] values()
{return (EnumSingle[])$VALUES.clone();
}public static EnumSingle valueOf(String name)
{return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
}private EnumSingle(String s, int i)
{super(s, i);
}public EnumSingle getInstance()
{return INSTANCE;
}public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];static
{INSTANCE = new EnumSingle("INSTANCE", 0);$VALUES = (new EnumSingle[] {INSTANCE});
}

最终ok

/*** 枚举本身就是一个 class*/
public enum EnumTest {INSTANCE;public EnumTest getInstance(){return INSTANCE;}public static void main(String[] args) throws Exception {Constructor<EnumTest> declaredConstructor = EnumTest.class.getDeclaredConstructor(String.class,int.class);declaredConstructor.setAccessible(true);EnumTest instance = EnumTest.INSTANCE;//这是原生对象EnumTest enumTest = declaredConstructor.newInstance(); //这是我们破坏的对象System.out.println(instance);System.out.println(enumTest);}
}

CAS深究

修内功 才是本质 网络 系统 原理

unsafe类 主要作用就是调用底层 native c++ 来调用内存

深入源码 探究底层 ++的由来

小结

CAS 如果工作内存的值和主内存的值 是期望的 就会执行更新 否则不执行 内部加了自旋锁 如果不是就一直循环;

CAS 缺点

  • 循环比较耗时
  • 一次性只能保证一个原子性
  • ABA问题

CAS (ABA问题)

假设有两个线程——线程1和线程2,两个线程按照顺序进行以下操作:

(1)线程1读取内存中数据为A;

(2)线程2将该数据修改为B;

(3)线程2将该数据修改为A;

(4)线程1对数据进行CAS操作

在第(4)步中,由于内存中数据仍然为A,因此CAS操作成功,但实际上该数据已经被线程2修改过了。这就是ABA问题。

private static  AtomicInteger atomicInteger=new AtomicInteger(2020);public static void main(String[] args) {//expect 期望  update更新   概念 如果达到期望值就更新 否则 不更新System.out.println(atomicInteger.compareAndSet(2020, 2021));System.out.println(atomicInteger.get());//捣乱修改过的线程System.out.println(atomicInteger.compareAndSet(2021, 2020));System.out.println(atomicInteger.get());System.out.println(atomicInteger.compareAndSet(2020, 6666));System.out.println(atomicInteger.get());}

原子引用(对应是思想 乐观锁)

关键注意点

使用这个解决ABA问题

public static void main(String[] args) {/*** 大坑 如果使用的是包装类型 就要注意引用类型啦* 值过大 开辟新的内存空间 然后 == 比较引用地址值不一致 导致false*/AtomicStampedReference <Integer> atomicStampedReference=new AtomicStampedReference<Integer>(1,1);new Thread(()->{int stamp = atomicStampedReference.getStamp();System.out.println("a1=>"+stamp);try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {e.printStackTrace();}atomicStampedReference.compareAndSet(1,2,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);System.out.println("a2=>"+atomicStampedReference.getStamp());atomicStampedReference.compareAndSet(2,1,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);System.out.println("a3=>"+atomicStampedReference.getStamp());},"A").start();new Thread(()->{int stamp = atomicStampedReference.getStamp();try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {e.printStackTrace();}System.out.println("b1=>"+stamp);System.out.println(atomicStampedReference.compareAndSet(1, 3, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));System.out.println("b2=>"+atomicStampedReference.getStamp());},"B").start();}

各个锁的理解

可重入锁 概念 只有进了一个锁 里面就等于都获取 就等于 进大门 等于卧室门也能进入

公平锁, 非公平锁

public ReentrantLock(boolean fair) {  //FALSE 就是公平sync = fair ? new FairSync() : new NonfairSync();
}

公平锁: 非常公平,先来后到,不允许插队

非公平锁: 非常不公平, 允许插队

synchronized

//实际上是一把锁public static void main(String[] args) {Phone phone=new Phone();new Thread(()->{System.out.println(Thread.currentThread().getName());phone.sms();},"A").start();new Thread(()->{System.out.println(Thread.currentThread().getName());phone.sms();},"B").start();}
}
class Phone{public  synchronized void sms(){System.out.println("发短信");call();}public  synchronized void call(){System.out.println("打电话");}
}

lock版 可重入锁

public static void main(String[] args) {Phone1 phone=new Phone1();new Thread(()->{System.out.println(Thread.currentThread().getName());phone.sms();},"A").start();new Thread(()->{System.out.println(Thread.currentThread().getName());phone.sms();},"B").start();}}
class Phone1{private static Lock lock=new ReentrantLock();public   void sms(){lock.lock();try {System.out.println("发短信");call();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public  void call(){lock.lock();try {System.out.println("打电话");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}

细节 锁必须匹配 否则很容易deadlock

自旋锁 SpinLock

底层

 AtomicReference<Thread> atomicReference=new AtomicReference<Thread>();//自定义自旋锁//加锁
public void myLock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"==>myLock");//自旋锁  底层Caswhile (!atomicReference.compareAndSet(null,thread)){}}
//解锁
public void myUnLock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"==>myUnLock");atomicReference.compareAndSet(thread,null);}
  public static void main(String[] args) {//原始锁
//        ReentrantLock reentrantLock = new ReentrantLock();
//        reentrantLock.lock();
//        reentrantLock.unlock();//用自己的写的自旋锁SpinLockDemo lockDemo = new SpinLockDemo();new Thread(()->{lockDemo.myLock();try {TimeUnit.SECONDS.sleep(5); //这里就会走自旋锁 会一直循环} catch (Exception e) {e.printStackTrace();} finally {lockDemo.myUnLock();}},"t1").start();new Thread(()->{lockDemo.myLock();try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {e.printStackTrace();} finally {lockDemo.myUnLock();}},"t2").start();}

互斥

占有等待

循环等待

不可抢占

   public static void main(String[] args) {String lockA = "lockA";String lockB = "lockB";new Thread(new MyThead(lockA,lockB),"t1").start();new Thread(new MyThead(lockB,lockA),"t2").start();}
}
class MyThead implements Runnable {private String lockA ;private String lockB;public MyThead(String lockA, String lockB) {this.lockA = lockA;this.lockB = lockB;}@Overridepublic void run() {synchronized (lockA) {System.out.println(Thread.currentThread().getName() + "lock" + lockA + "get=>" + lockB);try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockB) {System.out.println(Thread.currentThread().getName() + "lock" + lockB + "get=>" + lockA);}}}
}

死锁排查解决

jps -l 查看当前信息

用jstack + 类编号 查看堆栈

面试问 怎么排查问题 日志log 看堆栈

新手通俗易懂的juc优质文章相关推荐

  1. PostgreSQL 15 新特性解读 | 墨天轮优质文章合集

    5月19日,PostgreSQL 全球开发组宣布 PostgreSQL 15 的第一个 beta 版本,这一新版本在开发者体验.性能表现等方面都有提升.为了帮助大家更快速了解到PostgreSQL 1 ...

  2. 优质文章为什么对网站推广这么重要

    要想网站优化有好的结果,少不了优质文章更新,这既是搜索引擎优化规则要求,也是用户对于优质网站需求.保持网站优质文章的更新,保持网站的活跃度,吸引搜索引擎频繁抓取,吸引更多用户点击浏览.今天与大家分享优 ...

  3. 优质文章汇总,请查收!

    点击上方蓝色"Golang来啦"关注我哟 加个"星标",天天 15 分钟,掌握 Go 语言 你好,我是 Seekload. 这篇文章汇总了近期优质文章,请查收! ...

  4. WanAndroid,一款每日推荐优质文章App

    WanAndroid WanAndroid,一款每日推荐优质文章App,项目涉及API均来自鸿洋大神的玩Android,基于Material Design + MVP + RxJava2 + Retr ...

  5. 整理了我开始分享学习笔记到现在超过250篇优质文章,涵盖数据分析、爬虫、机器学习等方面,别再说不知道该从哪开始,实战哪里找了...

    大家好,我是老表,Python终身学习者,也是开源学习组织Datawhale终身成员,有近两年的Python编程学习经验,热爱爬虫与数据分析,目前是一名数据分析师(实习). 从18年5月起我将自己从0 ...

  6. 编辑器 的保存怎么绑定事件_适合新手用的公众号文章编辑器,你必须知道这一款...

    对很多新手从事公众号运营的人来说,往往是写作两小时,排版一整天,能够选对合适的编辑器就是非常重要的事情了.面对市面上琳琅满目的公众号文章编辑器,适合新手排版用的,我比较推荐96编辑器,功能全面,但是操 ...

  7. 优采云自动采集器,轻松获取优质文章资源!

    在当今信息爆炸的时代,如何快速获取海量的文章资源,成为了很多工作者必须面对的问题.而优采云自动文章采集器,就是一款让人们轻松获取优质文章资源的利器.下面,我们将从各个方面来详细介绍这款软件. 一.软件 ...

  8. 2017下半年掘金日报优质文章合集:前端篇

    在掘金微信群里的小伙伴应该都有看每日小报吧!这是小饼每天为大家精选的优质掘金文章(大家都很爱学习,我已经跟不上了QAQ..)小饼已经被N位群友连续几周催着出小报合集了,2017年马上就要结束了,拖延症 ...

  9. 2017下半年优质文章合集:前端篇

    2017年马上就要结束了,拖延症绝不拖到明年!这就给大家整理出来--2017下半年优质小报合集. CSS/页面布局 border属性的多方位应用和实现自适应三角形 BEM实战之扒一扒淘票票页面 一劳永 ...

最新文章

  1. 网游生命周期在百度指数曲线上呈“M”形分布,各阶段搜索行为呈一定特征
  2. 1组合逻辑电路--多路选择器与多路分解器
  3. 说一下fopen和open
  4. 基于事件驱动架构构建微服务第14部分:查询API
  5. P3714 [BJOI2017]树的难题(点分治/线段树/单调队列)
  6. linux下挂载iso镜像的方法
  7. asp.net5开发中DNX SDK版本的影响
  8. 这4款甘特图绘制工具,都是老原精心整理
  9. 数据结构——栈(链栈)
  10. java 物联网项目_物联网工程综合实践-JAVA WEB开发.ppt
  11. python 视频转图片
  12. 虚拟机设置开机启动自动运行脚本
  13. Codeforces 645D Robot Rapping Results Report【拓扑排序+二分】
  14. php echo eot,(基础篇)php中理解print EOT分界符和echo EOT的用法区别
  15. 会计平台常见问题QA
  16. 【论文翻译 IJCAI-20】Heterogeneous Network Representation Learning 异构网络表示学习
  17. 阿里巴巴矢量图标库全选
  18. 宽带换了新的账号怎么连接服务器地址,宽带换了路由器设置步骤图解
  19. 余弦于相似度cos similarity
  20. 用链表实现对二进制数加1的运算

热门文章

  1. ATRW A Benchmark for Amur Tiger Re-identification in the
  2. 一名优秀的安全主管需要“见人说人话,见鬼说鬼话”
  3. 网络驱动->PHY驱动调试
  4. 谷粒商城-后台管理系统
  5. 【91xcz】笔者分享:Win7系统操作注意事项
  6. Android监测屏幕旋转
  7. OAuth 2.0实战课 09 笔记
  8. MySQL聚簇索引和非聚簇索引的区别是什么?(2)
  9. 联想小新潮7000的全方位解析
  10. 9月29日全球域名商(国际域名)解析量排行榜TOP20