JUC知识点记录
看完狂神的视频 纯属想记录 回顾 狂神哥直链: 遇见狂神说的个人空间_哔哩哔哩_bilibili 没有不会做的事,只有不想做的事。
1、线程和进程
进程是操作系统中的应用程序、是资源分配的基本单位,线程是用来执行具体的任务和功能,是CPU调度和分派的最小单位
一个进程往往可以包含多个线程,至少包含一个
线程
Java默认有几个线程?2个线程! main线程、GC线程
线程的状态
public enum State {<!-- -->
//运行
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
线程的生命周期
新建、就绪、运行、阻塞、死亡
进程
JAVA真的可以开启线程吗? 开不了的!
java是没有权限去开启线程、操作硬件的,这是一个native的一个本地方法,它可以调用底层的c++代码
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调度速度很快假象同时执行的,实则是交替执行,并发编程的本质就是充分的利用CPU资源
并行
并行:多个人行走
-
CPU多核的情况,多个线程同时执行,我们可以使用线程池!
获取CPU的核数
//获取CPU核数 System.out.println(Runtime.getRuntime().availableProcessors()); }
wait 跟sleep的区别
1、父类不同
wait => Object
sleep => Thread
一般企业中使用的休眠都是:
package com.neihan.dome;
import java.util.concurrent.TimeUnit;
public class CpuDome {
public static void main(String[] args) {
TimeUnit.SECONDS.sleep(1); // SECONDS 是休眠的单位
}
}
2、关于锁释放
wait: 会释放锁
sleep:不会释放锁
3、使用的范围是不同的
wait:必须在同步代码块中使用
sleep:可以在任意地方使用
Lock
公平锁:十分公平,必须先来后到,先进入的线程先执行
不公平锁:十分不公平,可以插队,当前面的线程需要执行的时间很长的时候,后面的线程可以插队**(默认不公平锁)**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-umyUkvZy-1636546509443)(C:\Users\Neihan\AppData\Roaming\Typora\typora-user-images\image-20211110143059977.png)]
Synchronized与Lock的区别
1、Synchronized 是java关键字,Lock是类
2、Synchronized 无法获取判断锁的状态,Lock可以判断
3、Synchronized 会自动释放锁,Lock必须手动加锁释放锁
4、Synchronized 线程 A (获得锁->阻塞)线程 B(等待) Lock 就不一定会等下去,Lock有一个trylock去尝试获得锁,不会造成长久的等待。
4、Synchronized 是可重入锁,不可以中断,非公平的,Lock 可重入,可以判断锁,可以自己设置公平锁非公平锁
5、 Synchronized 适合锁少量代码同步问题,Lock适合锁大量的同步代码
生产者消费者的关系
synchronized版本
if 判断有虚假唤醒,处理方法用 while
结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后
package com.neihan.pc;
public class ConsumeAndProduct {
public static void main(String[] args) {
Data data = new Data();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
/*
if 判断有虚假唤醒
处理方法用 while
*/
class Data{
Integer number = 0;
public synchronized void increment() throws InterruptedException {
//判断等待
/* if(0 != number){
this.wait();
}*/
while (0!= number){
this.wait();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//判断
/* if(0 == number){
this.wait();
}*/
while (0== number){
this.wait();
}
//操作
number--;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
this.notifyAll();
}
}
Lock版本
package com.neihan.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockCAP {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() ->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data2{
Integer number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
//加锁
lock.lock();
try {
while (0!= number){
//等待
condition.await();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
public void decrement() throws InterruptedException {
//加锁
lock.lock();
try {
while (0== number){
//等待
condition.await();
}
//操作
number--;
System.out.println(Thread.currentThread().getName()+"==>"+number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
Condition的优势
指定唤醒线程
精准的通知和唤醒的线程!
如果我们要指定通知的下一个进行顺序怎么办呢? 我们可以使用Condition来指定通知进程
package com.neihan.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created with IntelliJ IDEA.
*
* @Author: 内涵
* @Date: 2021/11/02/19:53
* @Description:
*/
public class JUCPC {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data3.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data3.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data3.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
}
}
class Data3{
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2= lock.newCondition();
Condition condition3= lock.newCondition();
Integer number = 1; // 1A 2B 3C
public void printA() throws InterruptedException {
//加锁
lock.lock();
try {
while (number != 1){
//等待
condition1.await();
}
number = 2;
System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAA");
//唤醒 B
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
public void printB() throws InterruptedException {
//加锁
lock.lock();
try {
while (number != 2){
//等待
condition2.await();
}
number =3 ;
//唤醒 C
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBBBBB");
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
public void printC() throws InterruptedException {
//加锁
lock.lock();
try {
while (number != 3){
//等待
condition3.await();
}
number = 1;
//唤醒 A
System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCCCCCCC");
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
8锁现象
synchronized 锁的是当前的对象
package com.neihan.lock8;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/03/18:47
* @Description:
*/
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Send send1 = new Send();
Send send2= new Send();
new Thread(()->{
try {
send1.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
send2.sendEmail();
},"B").start();
}
}
class Send{
/*
synchronized 锁的是同一个对象
*/
public synchronized void sendSms() throws InterruptedException {
//增加延迟 1 s
TimeUnit.SECONDS.sleep(1);
System.out.println("发送短信");
}
public synchronized void sendEmail(){
System.out.println("发送邮箱");
}
}
static synchronized(静态同步函数)
静态同步函数锁的是类的 Class
package com.neihan.lock8;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/03/18:53
* @Description: static synchronized 与 synchronized 的区别
* 锁的对象不一样,static synchronized 锁的是类的Class 并且类Class全局唯一
* synchronized 锁的是当前操作的对象,对象会有多个 俩者互不干扰
*/
public class Test2 {
public static void main(String[] args) {
Send2 send = new Send2();
new Thread(()->{
try {
send.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
send.sendEmail();
},"B").start();
}
}
/**
* 静态 synchronized 锁的是Class模板 全局唯一
*/
class Send2{
/*
synchronized 锁的是同一个对象
*/
public static synchronized void sendSms() throws InterruptedException {
//增加延迟 1 s
TimeUnit.SECONDS.sleep(1);
System.out.println("发送短信");
}
/*
去掉static 锁的对象就不是同一个 所有发送邮箱先执行
*/
public synchronized void sendEmail(){
System.out.println("发送邮箱");
}
}
如果我们使用一个静态同步方法、一个同步方法、一个对象调用顺序是什么?
原因:因为一个锁的是Class类的模板,一个锁的是对象的调用者。所以不存在等待,直接运行。
集合不安全
List(不安全)
不安全的 触发 java.util.ConcurrentModificationException 异常 因为底层没有使用 synchronized
解决方案:
1、使用 new Vector<>()
2、Collections.synchronizedList(new ArrayList<>());
3、CopyOnWriteArrayList<>();
CopyOnWriteArrayList 比 Vector 好在哪?
Vector 和 CopyOnWriteArrayList 的区别
因为 Vector 使用了 synchronized 进行修饰,只要是使用了 synchronized 修饰的效率特别低下。
CopyOnWriteArrayList 没有使用 底层是使用的是 Lock锁 写入时复制 效率会更加高效! COW 计算机程序设计领域的一种优化策略
package com.neihan.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/03/19:39
* @Description: 测试List、是否安全
* 不安全的 触发 java.util.ConcurrentModificationException 异常 因为底层没有使用 synchronized
*/
public class TestList {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
/*
解决方法
1、使用 new Vector<>()
2、Collections.synchronizedList(new ArrayList<>());
3、CopyOnWriteArrayList<>();
Vector 和 CopyOnWriteArrayList 的区别
因为 Vector 使用了 synchronized 进行修饰,只要是使用了 synchronized 修饰的效率特别低下。
CopyOnWriteArrayList 没有使用 底层是使用的是 Lock锁 写入时复制 效率会更加高效! COW 计算机程序设计领域的一种优化策略
*/
for (int i = 1; i <=10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
}).start();
}
}
}
Map(不安全)
默认加载因子是0.75,默认的初始容量是16
new HashMap<>(16,0.75);
package com.neihan.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/04/8:35
* @Description: 测试HashMap 是否线程安全
* 不安全 触发 java.util.ConcurrentModificationException 线程修改异常
*
*/
public class TestMap {
public static void main(String[] args) {
// 默认等价什么?new HashMap<>(16,0.75);
//处理方式 Collections.synchronizedMap()
//处理方式 new ConcurrentHashMap<>();
Map<String, String> map = new ConcurrentHashMap<>();
map.put("A","A");
map.put("S","B");
map.put("B","c");
System.out.println(map);
/* 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();
}*/
}
}
Set(不安全)
HashSet底层是什么?
hashSet底层就是一个HashMap;
public HashSet() {
map = new HashMap<>();
}
package com.neihan.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/03/20:11
* @Description: 测试 Set 是否安全
* java.util.ConcurrentModificationException 并发修改 异常
* Set 是不安全的
*
*/
public class TestSet {
public static void main(String[] args) {
Set<Object> set = new HashSet<>();
/*
解决方法
1、Collections.synchronizedSet(new HashSet<>());
2、new CopyOnWriteArraySet<>();
hashSet 底层是什么?
就是hashMap
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
*/
for (int i = 1; i <=20; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
}).start();
}
}
}
Callable
1、可以有返回值
2、可以抛出异常
3、方法不同 run() / call()
Callable
泛型就是返回值
启动需要 FutureTask 适配器 使得Callable 与 Runnable 有关联
package com.neihan.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/04/9:34
* @Description: 测试 Callable 如何启动 需要什么适配器 进行启动 区别
*/
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
//使用 FutureTask 适配器 使得Callable 与 Runnable 有关联
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask).start();
//输出返回值 可能出现阻塞 可以把获取返回值放到最后一行,或者异步通信, 不阻塞
System.out.println(futureTask.get());
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
三大工具类
CountDownLatch
new CountDownLatch(5);
创建的时候 指定初始值
package com.neihan.add;
import java.util.concurrent.CountDownLatch;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/04/10:07
* @Description: 线程减法计数器
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch count = new CountDownLatch(5);
for (int i = 1; i <=5; i++) {
new Thread(()->{
//减一
count.countDown();
System.out.println(Thread.currentThread().getName());
},String.valueOf(i)).start();
}
//等待归零向下执行
count.await();
System.out.println("close");
}
}
CyclicBarrier
package com.neihan.add;
import java.util.Hashtable;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/04/11:37
* @Description:
*/
public class TestCyclicBarrier {
public static void main(String[] args) {
/**
* @Description: 线程加法计数器
* @Param: 1、需要到的次数,2、到达之后执行的线程
* @return:
* @Author: Neihan
* @Date: 2021/11/4
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("集结龙珠完成,召唤神龙");
});
for (int i = 1; i <=7; i++) {
final Integer temp = i;
new Thread(()->{
System.out.println("第"+temp+"龙珠");
try {
//等待次数到达
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
Semaphore
package com.neihan.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/04/14:48
* @Description: Semaphore
*/
public class TestSemaphore {
public static void main(String[] args) {
/**
* @Description:
* @Param: [args]
* @return:
* @Author: Neihan
* @Date: 2021/11/4
*/
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6; i++) {
new Thread(()->{
try {
semaphore.acquire(); //得到
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2); //休眠 2s
System.out.println(Thread.currentThread().getName()+"离开到车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
原理
semaphore.acquire(); 得到了资源,如果资源已经使用完,就等待资释放后再进行使用
semaphore.release(); 释放,会将当前的信号量释放 +1 然后唤醒等待的线程!
作用: 多个共享资源互斥的使用! 并发限流,控制最大的线程数!
读写锁
所以如果我们不加锁的情况,多线程的读写会造成数据不可靠的问题。
我们也可以采用synchronized这种重量锁和轻量锁 lock去保证数据的可靠。
但是这次我们采用更细粒度的锁:ReadWriteLock 读写锁来保证
package com.neihan.add;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/04/15:25
* @Description: 读写锁
*
*/
public class TestReadWriteLock {
public static void main(String[] args) {
MyCacheLock cache = new MyCacheLock();
for (int i = 1; i <=10; i++) {
final Integer temp = i;
new Thread(()->{
cache.write(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <=10; i++) {
final Integer temp = i;
new Thread(()->{
cache.read(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map<String,String> map = new HashMap<>();
public void write(String key,String value){
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完成");
}
public void read(String key){
System.out.println(Thread.currentThread().getName()+"读取"+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成");
}
}
class MyCacheLock{
private volatile Map<String,String> map = new HashMap<>();
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void write(String key,String 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 read(String key){
//读锁
readWriteLock.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 {
//释放读锁
readWriteLock.readLock().unlock();
}
}
}
阻塞队列
BlockQueue
是 Collection 的一个子类
什么情况下使用阻塞队列
多线程并发、线程池 的情况下使用
BlockQueue 有四组Api
1、抛出异常
2、不抛出异常,有返回值
3、一直阻塞
4、等待 超时阻塞
package com.neihan.queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/04/19:46
* @Description:
*/
public class TestQueue {
public static void main(String[] args) throws InterruptedException {
test04();
}
/**
* @Description: 抛出异常
* java.lang.IllegalStateException: Queue full 异常 超过队列大小
* java.util.NoSuchElementException 异常 队列没有元素
* @Param: []
* @return:
* @Author: Neihan
* @Date: 2021/11/4
*/
public static void test01(){
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// java.lang.IllegalStateException: Queue full 异常
//System.out.println(blockingQueue.add("c"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//java.util.NoSuchElementException 异常
//System.out.println(blockingQueue.remove());
}
/**
* @Description: 不抛出异常,有返回值
* @Param: []
* @return:
* @Author: Neihan
* @Date: 2021/11/4
*/
public static void test02(){
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
}
/**
* @Description: 一直阻塞
* @Param: []
* @return:
* @Author: Neihan
* @Date: 2021/11/4
*/
public static void test03() throws InterruptedException {
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
//一直阻塞不会返回
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
//blockingQueue.put("d");
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
//如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
System.out.println(blockingQueue.take());
}
/**
* @Description: 等待 超时阻塞
* @Param: []
* @return:
* @Author: Neihan
* @Date: 2021/11/4
*/
public static void test04() throws InterruptedException {
//队列需要初始化大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//等待 两秒 如果队列还是满的话就不再等待
System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
//取值,两秒没取到就不再等待
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
}
}
同步队列
同步队列 没有 容量 也可以视为 容量为一的队列;
进去一个元素必须等进去的元素出来其他的元素才能进入
put方法 和 take方法;
SynchronousQueue 的take是使用了lock锁保证线程安全的。
package com.neihan.queue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/8:43
* @Description: 同步队列
*/
public class TestSynchronousQueue {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
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();
}
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"take()");
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"take()");
TimeUnit.SECONDS.sleep(3);
System.out.println(synchronousQueue.take()+"take()");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
线程池
三大方式,七大参数,四种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术
线程池、JDBC的连接池、内存池、对象池 等等。。。。
资源的创建、销毁十分消耗资源
池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。
线程池的好处
1、降低资源的消耗
2、提高响应速度
3、方便管理
实现线程复用、可以控制最大并发数、管理线程
package com.neihan.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/9:02
* @Description: 线程池
*/
public class Dome01 {
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 < 100; i++) {
//使用线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+ " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//使用完线程池一定要关闭
threadPool.shutdown();
}
}
}
线程池的三大方法
- ExecutorService threadPool = Executors.newSingleThreadExecutor(); 单个线程
- ExecutorService threadPool = Executors.newFixedThreadPool(5); //固定多少个线程 或者说最大多少个线程
- ExecutorService threadPool = Executors.newCachedThreadPool(); //遇强则强,遇弱则弱
线程池的七大参数
1、核心线程
2、最大线程
3、阻塞队列
4、线程工厂
5、超时数
6、超时单位
7、拒绝策略
/*
线程池七大参数
int corePoolSize, 核心线程数
int maximumPoolSize, 最大线程池大小
long keepAliveTime, 超时了没人调用就会释放
TimeUnit unit, 超时单位
BlockingQueue<Runnable> workQueue 阻塞队列
ThreadFactory threadFactory, 线程工厂 创建线程的 一般不动
RejectedExecutionHandler handler 拒绝策略 四种拒绝策略
`
*/
拒绝策略
1. new ThreadPoolExecutor.AbortPolicy(): //该拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常
超出最大承载,就会抛出异常:队列容量大小+maxPoolSize
2. new ThreadPoolExecutor.CallerRunsPolicy(): //该拒绝策略为:哪来的去哪里 main线程进行处理
3. new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。
4. new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常
如何设置线程池的大小
1、CPU密集型:电脑的核数是几核就选择几;选择maximunPoolSize的大小
I/O密集型:
在程序中有15个大型任务,io十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间。
四大函数式接口
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
Function 函数型接口
函数式接口 只有一个方法的接口 并带有 @FunctionalInterface
package com.neihan.function;
import java.util.function.Function;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/16:06
* @Description: 函数式接口 只有一个方法的接口 并带有 @FunctionalInterface
*/
public class Demo01 {
public static void main(String[] args) {
/* Function<String, String> stringStringFunction = new Function<String,String>(){
@Override
public String apply(String o) { //传入什么就返回什么
return o;
}
};*/
// lambda 表达式
Function<String,String> function = (str)->{
return str;
};
System.out.println(function.apply("neihan niu bi lambda"));
}
}
断定型接口Predicate
断定型接口 只能传入一个参数,返回值只能是Boolean
package com.neihan.function;
import java.util.function.Predicate;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/16:22
* @Description: 断定型接口 只能传入一个参数,返回值只能是Boolean
*/
public class Dome02 {
public static void main(String[] args) {
/* Predicate<String> stringPredicate = new Predicate<String>(){
@Override
public boolean test(String o) {
return o.isEmpty();
}
};*/
Predicate<String> predicate = (str)->{ return str.isEmpty();};
System.out.println(predicate.test(""));
}
}
Supplier供给型接口
没有参数 只有返回值
package com.neihan.function;
import java.util.function.Supplier;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/16:39
* @Description: 供给型接口 没有参数 只有返回值
*/
public class Demo03 {
public static void main(String[] args) {
/* Supplier<String> stringSupplier = new Supplier<String>(){
@Override
public String get() {
return "NeiHan";
}
};
*/
Supplier supplier = ()->{return "Neihan Niu bi ";};
System.out.println(supplier.get());
}
}
消费性接口Consumer
消费性接口 只有参数没有返回值
package com.neihan.function;
import java.util.function.Consumer;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/17:03
* @Description: 消费性接口 只有参数没有返回值
*/
public class Demo04 {
public static void main(String[] args) {
Consumer<String> stringConsumer = new Consumer<String>(){
@Override
public void accept(String o) {
System.out.println(o);
}
};
stringConsumer.accept("neihan ");
}
}
stream流试计算
package com.neihan.function;
import java.util.Arrays;
import java.util.List;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/17:20
* @Description: Stream 流式计算
*
*
*
* 题目要求 : 用一行代码实现
* 1、ID 必须是偶数
* 2、年龄必须是大于23
* 3、用户名必须转换为大写
* 4、用户名倒序
* 5、只能输出一个用户
*
*/
public class testStream {
public static void main(String[] args) {
Student student1 = new Student(16,"a",1);
Student student2 = new Student(26,"b",2);
Student student3 = new Student(25,"c",6);
Student student4 = new Student(23,"d",4);
List<Student> students = Arrays.asList(student1, student2, student3, student4);
//链式编程 stream流式计算
students.stream()
.filter(student ->{return student.getId() % 2 ==0;})
.filter(student -> {return student.getAge() > 23;} )
.map(student -> {return student.getName().toUpperCase();}) //转换大写
.sorted((s1,s2)->{return s2.compareTo(s1);}) //排序 倒叙输出
.limit(1) // 分页,显示的页数
.forEach(System.out::printf);
}
}
package com.neihan.function;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/06/18:44
* @Description: Stream 案例
* 取出数组中所有的偶数,并拼接成最大的偶数,
*/
public class StreamHome01 {
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(2,3,4,6,60,7);
String collect = integers
.stream()
.map(Object::toString)
.sorted((s1, s2) -> {
return s2.compareTo(s1);
})
.filter((s) -> {
return Integer.parseInt(s) % 2 == 0;
})
.collect(Collectors.joining(""));
String sz [] = new String[collect.length()];
for (int i = 0; i < collect.length(); i++) {
sz[i] = collect.substring(i,i+1);
}
Arrays.stream(sz)
.sorted((i1,i2)->{return i2.compareTo(i1);})
.forEach(System.out::printf);
}
}
ForkJoin
ForkJoin 在JDK1.7,并行执行任务!提高效率~。在大数据量速率会更快!
大数据中:MapReduce 核心思想->把大任务拆分为小任务!
如何使用ForkJoin?
- 1、通过ForkJoinPool来执行 - 2、计算任务 execute(ForkJoinTask<?> task) - 3、计算类要去继承ForkJoinTask; ForkJoin 的计算类
package com.neihan.forkjoin;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/08/14:17
* @Description:
*/
@Data
@NoArgsConstructor
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp =10000L; //临界值
public ForkJoinDemo(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+= i;
}
return sum;
}else{
//使用Forkjoin计算
long middle = (start + end) / 2;
ForkJoinDemo forkJoin1 = new ForkJoinDemo(start,middle);
//拆分任务 把任务压入队列
forkJoin1.fork();
ForkJoinDemo forkJoin2 = new ForkJoinDemo(middle+1,end);
forkJoin2.fork();
return forkJoin1.join() + forkJoin2.join();
}
}
}
测试类
package com.neihan.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/08/14:34
* @Description: ForkJoin求和案例
*
* 3000 6000 90000
*/
public class TestForkjoin {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test01(); 2928
test02(); //4043
//test03(); //198
}
private static void test01(){
long start = System.currentTimeMillis();
long sum = 0L;
for (Long i = 1L; i <= 10_0000_0000L; i++) {
sum+= i;
}
Long end = System.currentTimeMillis();
System.out.println("sum= "+sum+"时间:"+(end-start));
}
public static void test02() throws ExecutionException, InterruptedException {
Long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务
Long sum = submit.get();
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start)/2);
}
/*
stream流计算
*/
public static void test03(){
Long start = System.currentTimeMillis();
Long sum = LongStream.range(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
}
}
异步回调
Future 设计的初衷:对将来的某个事件结果进行建模!
package com.neihan.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/08/16:11
* @Description: CompletableFuture 线程异步回调
*/
public class TestFuture {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test02();
}
public void test01() throws ExecutionException, InterruptedException {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//发起一个异步任务
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("这是一个异步的消息");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
System.out.println("Main线程");
System.out.println("future.get() = " + future.get());
}
/**
* @Description: 有返回值的异步回调
* @Param:
* @return: Int
* @Author: Neihan
* @Date: 2021/11/8
*/
public static void test02() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//发起一个异步任务
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("这是一个异步的消息");
} catch (InterruptedException e) {
e.printStackTrace();
}
int i = 10 /0;
System.out.println(Thread.currentThread().getName());
return 1024; // 成功的返回值
});
System.out.println(future.whenComplete((t, u) -> {
System.out.println(t); // t 是正常返回的结果
System.out.println(u); // u 是错误的信息
}).exceptionally((e -> {
System.out.println("e.getMessage() = " + e.getMessage());
return 500; //返回error 的信息
})).get());
}
}
但是我们平时都使用CompletableFuture
JMM
什么是JMM?
JMM:JAVA内存模型,不存在的东西,是一个概念,也是一个约定!
关于JMM的一些同步的约定:
1、线程解锁前,必须把共享变量立刻刷回主存;
2、线程加锁前,必须读取主存中的最新值到工作内存中;
3、加锁和解锁是同一把锁;
线程中分为 工作内存、主内存
8种操作:
-
Read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用; - load(载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中;
-
Use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令;
-
assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中; -
-
store(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用; - write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中; -
-
lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态;
-
unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
- JMM对这8种操作给了相应的规定*:
- - 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存- 不允许一个线程将没有assign的数据从工作内存同步回主内存- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store操作之前,必须经过assign和load操作- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量- 对一个变量进行unlock操作之前,必须把此变量同步回主内存
遇到问题:程序不知道主存中的值已经被修改过了!
对Volatile 的理解
Volatile是 Java 提供的 轻量级 的同步机制
1、保证可见性
2、不保证原子性
3、禁止指令重排机制
保证可见性
volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据 一直死循环
package com.neihan.jmm;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/9:34
* @Description: volatile
*
* 保证可见性
* 不保证原子性
* 禁止指令重排
*/
public class JMMDemo01 {
private static volatile int sum =1; //volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (sum==1){
}
}).start();
TimeUnit.SECONDS.sleep(1);
sum=0;
System.out.println("main");
}
}
不保证原子性
如果不加lock和synchronized ,怎么样保证原子性?
使用原子类
这些类的底层都直接和操作系统挂钩!是在内存中修改值。
package com.neihan.jmm;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/9:41
* @Description: 如果不加lock和synchronized ,怎么样保证原子性?
* 使用原子类
*/
public class VDemo {
private static volatile AtomicInteger count = new AtomicInteger();
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2 ){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+""+"count="+count);
}
public static void add(){
/* count++; // ++ 不是原子性操作 是两个~3个操作*/
count.incrementAndGet();
}
}
禁止指令重排
什么是指令重排?
我们写的程序,计算机并不是按照我们自己写的那样去执行的
源代码–>编译器优化重排–>指令并行也可能会重排–>内存系统也会重排–>执行
int x=1; //1
int y=2; //2
x=x+5; //3
y=x*x; //4
//我们期望的执行顺序是 1_2_3_4 可能执行的顺序会变成2134 1324
//可不可能是 4123? 不可能的
1234567
可能造成的影响结果:前提:a b x y这四个值 默认都是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
可能在线程A中会出现,先执行b=1,然后再执行x=a;
在B线程中可能会出现,先执行a=2,然后执行y=b;
那么就有可能结果如下:x=2; y=1.
volatile可以避免指令重排:
volatile中会加一道内存的屏障,这个内存屏障可以保证在这个屏障中的指令顺序。
内存屏障:CPU指令。作用:
1、保证特定的操作的执行顺序;
2、可以保证某些变量的内存可见性(利用这些特性,就可以保证volatile实现的可见性)
总结
- volatile可以保证可见性;- 不能保证原子性- 由于内存屏障,可以保证避免指令重排的现象产生
面试官:那么你知道在哪里用这个内存屏障用得最多呢?单例模式
玩转单例模式
饿汉式、DCL懒汉式
饿汉模式
单例模式私有构造
饿汉顾名思义 很饿 什么东西都先创建好
package com.neihan.jmm;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/10:26
* @Description: 饿汉单例模式
*
* 单例模式全部是构造私有
* 饿汉顾名思义 很饿 什么东西都先创建好
*
*/
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 Hungry(){
}
private final static Hungry hungry = new Hungry();
private static Hungry getInstance(){
return hungry;
}
}
DCL懒汉式
package com.neihan.jmm;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/10:29
* @Description: 懒汉单例模式
* 很懒,所有东西不先创建
*/
public class LazyMan {
private static boolean neihan = false; //红绿灯
private LazyMan(){
synchronized (LazyMan.class){
if(neihan == false){
neihan=true;
}else{
throw new RuntimeException("小伙子不要试图使用反射来搞破坏!!!");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static LazyMan lazyMan;
private static LazyMan getInstance(){
if (lazyMan ==null){
synchronized (LazyMan.class){
if(lazyMan==null){ //单线程下是OK 的 但是多线程是不行的 需要加锁 加锁后java中有反射!!!!
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
/* for (int i = 0; i < 10; i++) {
new Thread(()->{
getInstance();
}).start();
}
*/
//反射
//LazyMan instance1 = LazyMan.getInstance();
Field neihan = LazyMan.class.getDeclaredField("neihan");
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); //这个时候无视了私有的构造器,使用反射创建对象
LazyMan instance2 = declaredConstructor.newInstance();
neihan.setAccessible(true);
neihan.set(instance2,false); //把字段的值改变又可以了
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
静态内部类
package com.neihan.jmm;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/11:06
* @Description: 静态内部类
*/
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.holder;
}
public static class InnerClass{
private static final Holder holder = new Holder();
}
}
单例不安全, 因为反射
枚举
package com.neihan.jmm;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/11:09
* @Description: 枚举本身也是一个类
* 在源码中看到Enum 是有无参构造的,原本的异常期望的是Cannot reflectively create enum objects
*
* 但是报的错误是 没有这个构造 java.lang.NoSuchMethodException: com.neihan.jmm.EnumSingle.<init>()
* 通过反编译jad工具看到是 有个String 跟int 的构造,IDEA 骗了我们 给上String.class 跟int.class 之后我们发现到达了预期的报错
* Cannot reflectively create enum objects
* 使用枚举,我们就可以防止反射破坏了。
*
*/
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(enumSingle);
}
}
使用枚举,我们就可以防止反射破坏了。
枚举类型的最终反编译源码:
public final class EnumSingle extends Enum
{<!-- -->
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
});
}
}
深入理解CAS
什么是CAS?
大厂必须深入研究底层!!!!修内功!操作系统、计算机网络原理、组成原理、数据结构
意思就是比较并交换
package com.neihan.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/14:02
* @Description: CAS
* 意思就是比较并交换
* 当期望的值跟实际的值相同的时候就进行更新
* Java 层面无法操作内存,java可以调用c++ native c++ 可以操作内存,java留了个后门 可以 通过 Unsafe 类操作
*
*/
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(1024);
//public final boolean compareAndSet(int expect, int update)
/**
* @Description: CAS
* @Param: 期望值、更新值
* @return: boolean
* @Author: Neihan
* @Date: 2021/11/9
*/
System.out.println(atomicInteger.compareAndSet(1024, 241));
System.out.println(atomicInteger.get());
}
}
Unsafe 类
总结
CAS:比较当前工作内存中的值 和 主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环,使用的是自旋锁。
缺点:
- 循环会耗时;- 一次性只能保证一个共享变量的原子性;- 它会存在ABA问题
CAS : ABA 问题 (狸猫换太子)
线程1:期望值是1,要变成2;
线程2:两个操作:
- 1、期望值是1,变成3- 2、期望是3,变成1
所以对于线程1来说,A的值还是1,所以就出现了问题,骗过了线程1;
public class casDemo {<!-- -->
//CAS : compareAndSet 比较并交换
public static void main(String[] args) {<!-- -->
AtomicInteger atomicInteger = new AtomicInteger(2020);
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
//boolean compareAndSet(int expect, int update)
//期望值、更新值
//如果实际值 和 我的期望值相同,那么就更新
//如果实际值 和 我的期望值不同,那么就不更新
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//因为期望值是2020 实际值却变成了2021 所以会修改失败
//CAS 是CPU的并发原语
//atomicInteger.getAndIncrement(); //++操作
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
原子引用
解决ABA问题,对应的思想:就是使用了乐观锁~
带版本号的 原子操作!
使用包装类会有对象引用的问题
Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
带版本号的原子操作
package com.neihan.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/14:30
* @Description: ABA 问题 狸猫换太子 理论像乐观锁
*
*
*/
public class ABADemo {
public static void main(String[] args) {
//使用包装类会有对象引用的问题
//Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(1,1);
// CAS 比较交换
new Thread(()->{
int stamp = atomicStampedReference.getStamp(); //获得版本号
System.out.println(Thread.currentThread().getName()+"Stamp=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* @Description: CAS 比较 修改
* @Param: 期望的值 修改的值 版本号 修改之后的版本号
* @return: Boolean
* @Author: Neihan
* @Date: 2021/11/9
*/
System.out.println(atomicStampedReference.compareAndSet(
1,
2,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());
//修改完之后再修改回来,另一条线程并不知情,看看能不能修改成功
System.out.println(atomicStampedReference.compareAndSet(
2,
1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());
},"A").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"Stamp"+stamp);
System.out.println(Thread.currentThread().getName()+" "+atomicStampedReference.compareAndSet(1, 6, stamp, atomicStampedReference.getStamp() + 1));
System.out.println(Thread.currentThread().getName()+"Stamp"+atomicStampedReference.getStamp());
},"B").start();
}
}
各种锁的理解
公平锁,非公平锁
1、公平锁:非常公平,不能插队,必须先来后到
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {<!-- -->
sync = new NonfairSync();
}
2、非公平锁:非常不公平,允许插队,可以改变顺序
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {<!-- -->
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁
1、Synchonized 锁
synchronized 版本的可重入锁 自动获得内部锁
package com.neihan.lock;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/15:04
* @Description: synchronized 版本的可重入锁 自动获得内部锁
*/
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"==>sms");
call(); //这里也是一把锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"==>call");
}
}
Lock版本
Lock 版本的重入锁 Lock 锁一定是配对出现,不然就会出现死锁
package com.neihan.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/15:08
* @Description: Lock 版本的重入锁 Lock 锁一定是配对出现,不然就会出现死锁
*/
public class Demo02 {
public static void main(String[] args) {
Phone2 phone2 = new Phone2();
new Thread(()->{
phone2.sms();
},"A").start();
new Thread(()->{
phone2.sms();
},"B").start();
}
}
class Phone2{
Lock lock = new ReentrantLock();
public void sms(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"==>sms");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
call(); //这里也是一把锁
}
public void call(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"==>call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
自旋锁
1、spinlock
public final int getAndAddInt(Object var1, long var2, int var4) {<!-- -->
int var5;
do {<!-- -->
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
2、自定义自旋锁
基于CAS 实现
package com.neihan.lock;
import java.util.concurrent.atomic.AtomicReference;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/15:25
* @Description: 自定义的自旋锁 基于CAS 实现
*/
public class SpinlockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+" myLock ");
//自旋锁
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnlock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===>myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
测试类
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/15:30
* @Description:
*/
public class TestSpinLock {
public static void main(String[] args) {
SpinlockDemo spinlockDemo = new SpinlockDemo();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"A").start();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"B").start();
}
}
死锁
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/16:06
* @Description: 死锁案例 以及排查
* jps -l 查看进程
* jstack 进进程号,进行排查
*
*/
public class Deadlock {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get "+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===> get"+lockA);
}
}
}
}
如何解开死锁
命令:jps -l
1、使用jps定位进程号,jdk的bin目录下: 有一个jps
2、使用jstack
进程进程号 找到死锁信息
一般情况信息在最后:
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnlock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===>myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
**测试类**
```java
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/15:30
* @Description:
*/
public class TestSpinLock {
public static void main(String[] args) {
SpinlockDemo spinlockDemo = new SpinlockDemo();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"A").start();
new Thread(()->{
try {
spinlockDemo.myLock();
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"B").start();
}
}
死锁
package com.neihan.lock;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
*
* @Author: Neihan
* @Date: 2021/11/09/16:06
* @Description: 死锁案例 以及排查
* jps -l 查看进程
* jstack 进进程号,进行排查
*
*/
public class Deadlock {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get "+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===> get"+lockA);
}
}
}
}
如何解开死锁
命令:jps -l
1、使用jps定位进程号,jdk的bin目录下: 有一个jps
2、使用jstack
进程进程号 找到死锁信息
一般情况信息在最后: