目录
锁消除,锁粗化,自旋锁,自适应自旋锁(synchronize知识)
5.CompletableFuture->组合式异步编程(异步编排)
JVM
1.jvm内存结构
方法区(共享)
1.存储构造方法,常量池,静态变量,接口定义. |
栈(独立)
1.存储方法的返回值,方法的参数,方法的变量(局部变量),入栈和出栈的操作, 栈帧(方法的操作进度), 2.栈随着线程的创建而创建,随着线程的消失而消失,每个线程拥有自己的栈. |
本地方法栈
1.用于调用系统级别的一些方法 |
堆(共享)
新生代:所有的对象都"可以"是新生代(每个线程都可能会new一些新对象,而且这些对象可能 用一下就不用了) 区域划分:伊甸园区域,幸存者区域(From区域 To区域) 内存占比 8 : 1 : 1 大部分创建(new)出来的对象都会放在伊甸园区域,如果伊甸园区域满了会触发垃圾回收. 垃圾回收流程:当新生代进行垃圾回收的时候,会将所有的伊甸园区域还在使用的对象放在 From区域,然后将From区域的所有对象全部复制到To区域里,其次将From区域 和伊甸园区域进行清空,之后将To区域变成From区域,把之前被清空的From区域 变成To区域,以上流程存活下来的对象的年龄会进行+1操作. 老年代: a.经历15次垃圾回收->变成老年代. b.如果对象太大,新生代的内存区域装不下这个对象(触发垃圾回收,垃圾回收之后还是装 不下这个对象,则该对象直接晋级老年代) c.一旦新生代对象晋升成老年代,而老年代装不下这个对象的时候,会将老年代中不再使用的 对象进行垃圾清理,如果老年代清理之后还是装不下这个对象则会抛出异常(OOM->Out of memory 内存溢出) 永久代:一直都会被使用的,比如元数据(.class文件) 随着项目越来越大,造成元数据以及.class文件会越来越多,最终会造成永久代可能装不下 这些数据,也会发生OOM 解决策略:1.调大永久代的内存区域;2.取消永久代(提出元空间-->堆外内存). |
程序计数器
1.记录程序运行到哪儿了 |
执行引擎
1.用于执行具体的方法和逻辑(运行代码); 2.负责解释jvm内部命令,翻译给操作系统执行. |
类加载器
1.作用:将.class文件加载到运行时的数据区域,只进行加载,不进行校验(意思你的类里边 可能有一些错误,这个类能不能运行起来加载器是不清楚的). 2.分类: a.虚拟机自带的加载器: 启动类加载器(bootstrapClassLoader): 扩展类加载器(extensionClassLoader): 应用类加载器(APPClassLoader): b.用户自定义加载器:用户可以自定义类的加载方式,写一个类继承java.lang.ClassLoader. 3..class文件的加载顺序: 首先,.class文件丢给类加载器之后,自定义加载器判断这个类是否被加载过,如果没有 加载过,则把该类交给应用类加载器,如果已经加载过则立马结束加载;然后应用 类加载器拿到这个类之后,查看是否加载过,如果已经加载过,则立马结束加载,如果 没有加载过,则把该类交给扩展类加载器;扩展类加载器拿到这个类之后,查看是否加 载过,如果已经加载过,则立马结束加载,如果没有加载过,则交给启动类加载器;启动 类加载器拿到这个类之后,查看是否加载过,如果已经加载过,则立马结束加载,如果没 有加载过,则尝试加载,如果加载成功,立马结束加载,如果加载失败,则交给扩展类加 载器;扩展类拿到该类之后尝试加载,如果加载成功,则立马结束加载,如果加载失败,则 交给应用程序加载器加载;应用程序拿到该类后,尝试加载,如果加载成功,则立马结束 加载,如果加载失败,则交给自定义类加载器;自定义类加载器拿到该类后,尝试加载,如 果加载成功,则立马结束加载,如果没有加载成功,则抛出ClassNoFoundException. 4.双亲委派机制 作用:保证安全,可避免自己编写的类动态替换java的核心类,比如java.lang.String |
2.jvm每块作用是什么
4.jvm常用参数(jvm调优)
Young GC->表示只有新生代回收
Full GC->表示既有新生代回收又有老年代回收
5.垃圾回收--算法
6.垃圾回收--工具(垃圾回收器)
JUC
await搭配while而不是if!!!!!!!!!!
线程:1.如何创建线程.2线程同步.3.线程通讯.
1.程序:是为了完成某个特定的任务,而用某种语言编写的一组指令的集合,即指的 2.进程:是程序的一次执行过程,或是一个正在运行的程序。是一个动态的过程:有它 程序是静态的,进程是动态的。系统在运行时会为每个进程分配不同的内存空间。 3.线程:进程可进一步细化为线程,是一个程序内部的执行的路径。若一个进程同一时间 |
sleep和wait的区别
1.sleep是Thread线程类的一个内部的静态方法; 2.wait是Object类中的一个实现方法,是非静态方法; 3.所有的对象都可以进行wait,但是只有线程对象能进行sleep; 4.无论是wait还是sleep都可以被中断; 5.sleep是在指定的时间内线程属于阻塞状态,当时间到了以后,线程苏醒; 6.wait有两种状态:一个是指定时间,一个是不指定时间,当一个对象wait以后,需要其他对象进行notify(随机唤醒 一个wait中的线程)唤醒/notifyAll(唤醒全部处于wait中的线程); 7.sleep不会释放锁资源,wait会释放锁资源. |
串行,并发,并行的区别
synchronized关键字
1.修饰的对象: a.修饰一个代码块:被修饰的代码块同一时间只能一个人访问; b.修饰一个方法; c.修饰一个静态的方法; d.修饰一个类; 2.情景:一个类中有两个方法a和b,每个方法都加了synchronized关键字.当创建两个线程分别调用这两个方法时, 会不会发生阻塞? 情况一:当a方法是static的synchronized,b方法是static的synchronized--->>会发生阻塞. 情况二:当a方法是static的synchronized,b方法不是static的synchronized.-->不会发生阻塞. 情况三:当a方法不是static的synchronized,b方法也不是static的synchronized.-->会发生阻塞. |
锁膨胀过程(jdk对synchronize关键字的优化)
1.我们在堆中创建的对象,会有一个对象头(MarkWord),里边记录着对象的哈希值、年龄(垃圾回收)、锁(线程 id).锁的底层原理即该对象头中的线程id. 2.锁膨胀,其实就是锁的状态发生变化:无锁->偏向锁->轻量级锁->重量级锁 (锁的级别越来越高,加锁解锁消耗 越大) 3.锁的那几个状态怎么理解? a.无锁:对象没有线程访问的时候 b.偏向锁:好比只有一个线程访问对象(核心思想->在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得,因此为了减少同一个线程获取锁(会涉及到一些CAS操作,很耗时)的代价而引入偏向锁.一旦a线程持有了该对象,标志位修改为1(后边a线程如果连续获得该锁,标志位连续+1),就进入偏向模式,同时会把这个线程的id记录在对象的Mark word中.当a线程再次请求锁时,无需再作任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而提升程序的性能,但对于锁竞争比较激烈的场合,偏向锁就失效了,因为这种场合极有可能每次申请锁的线程都是不同的) c.轻量级锁:多线程交替访问对象,不会发生冲突(不存在并发) d.重量级锁:多线程存在竞争关系(存在并发).注:重量级锁的时候会产生一个对象监视器指针,它作用于 await与notify 注:锁的状态是由竞争升级的,而不是由哪个线程主动选择.而且锁的级别只升不降.当不再有线程进行抢锁的时候,随着事件的推移,会造成锁超时,从而达到锁消失. |
锁消除,锁粗化,自旋锁,自适应自旋锁(synchronize知识)
1.锁消除->取消没有意义的锁 2.锁粗化->没有必要反复的加锁 3.自旋锁 当一个线程拿着锁的时候,其他的线程不知道该线程什么时候会释放锁,所以其他线程会陷入一种状态(不 断的来尝试给该对象加锁,而拿到锁的线程长时间不释放锁,就会导致其他线程一直尝试加锁,导致浪费 CPU). 4.自适应自旋锁 在自旋锁的基础上进行优化->其他线程不再一直尝试加锁,而是尝试相应的次数(比如现在加锁的这个线程 成功加锁前尝试了20次,那么自适应自旋锁就尝试加锁20次,如果都没能加锁,直接进入锁阻塞状态) |
Lock接口
lock接口的方法
public interface Lock {
//加锁: 加锁如果失败会一直尝试,加锁成功还是失败不知道结果
void lock();
//中断锁: 加锁必须要成功为止,没有成功就会一直尝试(中断掉)
void lockInterruptibly() throws InterruptedException;
//加锁: 加锁就一次,可以知道这次加锁是成功还是失败
boolean tryLock();
//加锁: 在指定时间内一直加锁,可以知道这次加锁是成功还是失败
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁:如果加锁成功,不释放锁,导致死锁
void unlock();
//钥匙
Condition newCondition();
}
Lock锁的可重入锁、公平锁
public class SaleTicketLock {
private Integer ticket = 100;
Lock lock = new ReentrantLock();
//Lock lock = new ReentrantLock(true);如果使用这种方式声明锁,则该lock锁为
//公平锁,即每个线程依次获得一下该锁
public void sale(){
try {
lock.lock();
if (ticket>0){
ticket --;
System.out.println(Thread.currentThread().getName()+"购买了一张票,还剩"+ticket+"张票");
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void sendMessage(){
try {
System.out.println(Thread.currentThread().getName()+"is running");
}finally {
}
}
public static void main(String[] args) {
SaleTicketLock saleTicketLock = new SaleTicketLock();
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
//解释->当一个线程拿到lock锁执行a对象的加了锁的b()方法之后,又想去执行
// a对象的加了锁的c()方法,这个是可以的,当该线程把两个方法都执行完了
// 之后就会把锁释放掉(可以记为switch的case穿透性)
saleTicketLock.sale();
saleTicketLock.sendMessage();
}
}).start();
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
saleTicketLock.sale();
saleTicketLock.sendMessage();
}
}).start();
}
}
读写锁->读锁释放之后写锁才能被抢
public class ReadAndWrite {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Object data = null;
public void write(Object data){
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
try {
writeLock.lock();
this.data = data;
System.out.println(Thread.currentThread().getName()+"is writing");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"has writed");
}
finally {
writeLock.unlock();
}
}
public void read(){
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
try {
readLock.lock();
System.out.println(Thread.currentThread().getName()+"is reading");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"get the data which is"+this.data);
}
finally {
readLock.unlock();
}
}
public static void main(String[] args) {
ReadAndWrite readAndWrite = new ReadAndWrite();
new Thread(() -> {
readAndWrite.write("image");
},"徐老师").start();
for (int i = 0; i < 67; i++) {
new Thread(() -> {
readAndWrite.read();
},"小"+i).start();
}
}
}
定制化通信
public class CustomizedCommunication {
Lock lock = new ReentrantLock();
Condition cut = lock.newCondition();
Condition cook = lock.newCondition();
Condition give = lock.newCondition();
private String flag = "cut";
public void cut(){
try {
lock.lock();
while (!flag.equals("cut")){
try {
cut.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"cut");
this.flag = "cook";
cook.signal();
}
finally {
lock.unlock();
}
}
public void cook(){
try {
lock.lock();
while (!flag.equals("cook")){
try {
cook.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"cook");
this.flag = "give";
give.signal();
}
finally {
lock.unlock();
}
}
public void give(){
try {
lock.lock();
while (!flag.equals("give")){
try {
give.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"give");
this.flag = "cut";
cut.signal();
}
finally {
lock.unlock();
}
}
public static void main(String[] args) {
CustomizedCommunication customizedCommunication = new CustomizedCommunication();
new Thread(() -> {
for (int i = 1; i < 11; i++) {
customizedCommunication.cut();
System.out.println(Thread.currentThread().getName()+"正在切第"+i+"到菜");
}
},"cut").start();
new Thread(() -> {
for (int i = 1; i < 11; i++) {
customizedCommunication.cook();
System.out.println(Thread.currentThread().getName()+"正在炒第"+i+"到菜");
}
},"cook").start();
new Thread(() -> {
for (int i = 1; i < 11; i++) {
customizedCommunication.give();
System.out.println(Thread.currentThread().getName()+"正在上第"+i+"到菜");
}
},"give").start();
}
}
中断锁
public class LockInterrupt {
Lock lock = new ReentrantLock();
public void dateing(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"正在约会");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
System.out.println(Thread.currentThread().getName()+"约会完毕");
lock.unlock();
}
}
public void waiting(){
System.out.println(Thread.currentThread().getName()+"正在等待");
try {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName()+"终于等到你");
System.out.println(Thread.currentThread().getName()+"正在约会");
System.out.println(Thread.currentThread().getName()+"约会完毕");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}catch (Exception e){
System.out.println(Thread.currentThread().getName()+"不等了,滚!");
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
LockInterrupt lockInterrupt = new LockInterrupt();
new Thread(() -> {
lockInterrupt.dateing();
},"情敌").start();
Thread bt = new Thread(() -> {
lockInterrupt.waiting();
}, "备胎");
bt.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bt.interrupt();
}
}
lock和synchronized作比较
1.相同点.
都支持独占线程,都支持可重入.
2不同点.
lock系列api用法 | synchronized用法 | |
加锁/解锁 | 手动 | 自动 |
支持共享锁 | √(比如读锁) | × |
支持尝试获取锁 | √ | × |
支持响应中断 | √ | × |
代码风格 | 面向对象 | 面向过程 |
底层原理 | AQS(volatile+CAS+线程的双向链表)=非阻塞同步 | 阻塞同步 |
程序计数器
1.介绍:通过使用CountDownLatch可以使当前线程阻塞,等待其他线程完成给定任务.可以类比旅游团要等待所有的旅客到齐后才能去下一个景点.CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待N个点完成,构造方法里边就传入N,这里所说的N个点,可以使N个线程,也可以是1个线程里的N个执行步骤. 构造器: public CountDownLatch(int count) { 2.注:计数器参数count必须大于等于0,等于零的时候,调用await方法不会阻塞当前线程. 3.当我们调用CountDownLatch的countDown()方法时,N就会减1,CountdownLatch的await()方法会阻塞当前线程,直到N编程0. 4,使用案例:使用多线程下载网络上的图片,下载完成后,提示用户下载完成. |
public class MyCountDownLatch {
public static void main(String[] args) {
System.out.println("等待旅客集合!!");
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
CountDownLatch countDownLatch = new CountDownLatch(67);
for (int i = 1; i < 68; i++) {
new Thread(() -> {
Random random = new Random();
int times = random.nextInt(5);
try {
Thread.sleep(times*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("旅客"+Thread.currentThread().getName()+"到达集合点!");
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
countDownLatch.countDown();
},i+"").start();
}
try {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是导游,人到齐了,我们去下一个景点!");
}
}
循环栅栏
public class MyCyclicBarrier {
public static void main(String[] args) {
//创建7个线程,每个线程执行完毕下边的7会减1,减到0开始执行
//下边构造器里边的Runnable重写方法
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("已经集齐七颗龙珠,出来吧神龙!");
});
for (int i = 1; i < 8; i++) {
int num = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"开始收集龙珠");
Random random = new Random();
int times = random.nextInt(5);
try {
Thread.sleep(times*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"收集到第"+num+"颗龙珠");
try {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},i+"").start();
}
}
}
信号灯
public class MySemaphore {
public static void main(String[] args) {
//创建三个信号灯,
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i < 8; i++) {
new Thread(() -> {
try {
//线程拿到信号灯即可继续执行
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"号车进入车位");
int times = new Random().nextInt(7)+1;
try {
Thread.sleep(1000*times);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"号车驶出车位");
//线程执行完方法归还信号灯
semaphore.release();
},i+"").start();
}
}
}
创建多线程的实现方式
1.继承Thread类
public class First extends Thread {
public First(String threadName){
super(threadName);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"简单");
}
public static void main(String[] args) {
new First("1").start();
}
}
2.实现Runnable接口
public class Second implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"easy");
}
public static void main(String[] args) {
Second second = new Second();
new Thread(second,"2").start();
}
}
3.线程池
public class Third {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(() -> {
System.out.println(Thread.currentThread().getName()+"");
});
}
}
4.Callable+FutureTask
public class Fourth {
public static void main(String[] args) {
FutureTask<BigDecimal> futureTask = new FutureTask<>(() -> {
System.out.println(Thread.currentThread().getName()+"");
return new BigDecimal("4");
});
new Thread(futureTask,"four").start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
5.CompletableFuture->组合式异步编程(异步编排)
CompletableFuture实现了Future接口和CompletionStage接口
future接口提供的方法
get()->调用该方法的线程进行等待,直到等待的任务做完才能执行get()方法以后的代码 cancel()->取消该任务 isCanceled()->判断该任务是否取消,是返回true,不是返回false, isDone()->查看任务是否以及完成,如果完成了返回true,否则返回false get(long time,TimeUnit unit)->只等time的时间,如果任务还没执行完则调用该方法的线程继续执行 只有get方法会抛异常. |
CompletableFuture自己的静态方法
我们使用CompletableFuture可以直接调用的方法 runAsync()->无返回结果 allof()->所有任务执行完才执行该代码下边的代码,需要join()!!! anyOf()->任何任务执行完就可以执行改代码下边的代码需要join()!!! supplyAsync()->有返回结果 |
CompletionStage提供的方法
!!!!!!不带Async的可能由子线程执行,可能由主线程执行 1.thenRunAsync()->无参无返 2.thenAcceptAsync()->有1参无返,参数为上一步的结果 3.thenApplyAsync()->有1参有返,参数为上一步的结果 4.exceptionally()->有1参有返,参数为上一步抛的异常,如果上一步发生异常才会执行该任务,exceptionally()里边只会打印上一步的异常,而不会因为上一步发生异常而结束任务 5.whenCompleteAsync()->两2参无返,无论有没有异常都会执行,第一个参数:上一步的结果;第二个参数:上一步抛的 异常,当whenCompleteAsync()里边的代码执行完毕就会往外抛异常 6.handleAsync()->2参有返,无论有没有异常都会执行,第一个参数为上一步的结果,第二个参数为上一步的异常,不抛异常 |
6.Fork Join
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
public class ForkJoinTest extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long result = 0L;
public ForkJoinTest(Long start, Long end){
this.start = start;
this.end = end;
}
//重写方法
@Override
protected Long compute() {
if (end - start <= 100){
//表示能算
System.out.println("已经达到可计算范围"+start+"=========="+end);
for (Long i = start; i <= end; i++) {
result = result + i;
}
}else {
//开始拆分任务
Long leftStart = start;
Long leftEnd = (start + end)/2;
Long rightStart = leftEnd + 1;
Long rightEnd = end;
//交给新的分组下线
ForkJoinTest leftForkJoin = new ForkJoinTest(leftStart, leftEnd);
ForkJoinTest rightForkJoin = new ForkJoinTest(rightStart, rightEnd);
//启动下线任务
leftForkJoin.fork();
rightForkJoin.fork();
//获得下线结果
Long leftResult = leftForkJoin.join();
Long rightResult = rightForkJoin.join();
//结果
result = leftResult + rightResult;
}
return result;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTest forkJoinTest = new ForkJoinTest(1L, 98456L);
//线程池启动任务
ForkJoinTask<Long> submitResult = forkJoinPool.submit(forkJoinTest);
//获取分组计算的结果
Long result = submitResult.get();
System.out.println("最终结果是"+result);
}
}
7.并行计算
8.Timer定时任务
9.Spring的一部方法调用
线程池解析
自定义线程池
/**
* 商品详情微服务使用的线程池
*/
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8,//核心线程数:
// 文件IO较多的情况->等于逻辑线程数数量;计算较多的情况->等于逻辑线程数数量*2
16,//最大线程数
10,//闲置时间
TimeUnit.SECONDS,//闲置时间单位
new ArrayBlockingQueue<>(100),//阻塞队列
Executors.defaultThreadFactory(),//线程工厂对象:用于创建线程
new ThreadPoolExecutor.AbortPolicy());
return threadPoolExecutor;//拒绝策略
}
/*
* 线程池的膨胀过程:
* 1.在项目启动后,会通过springboot包扫描注解扫描到当前配置类,会完成线程池的初始化,并存储到IOC容器中当作一个Bean
* 线程池中:核心线程数:0个;非核心线程数:0个,阻塞队列中元素:0个
* 2.这时候来了一个任务需要被执行,将任务分配给线程池,此时线程池开始调用线程执行这个任务
* 线程池中:核心线程数:1个;非核心线程数:0个,阻塞队列中元素:0个
* 3.和上面那个任务一起来的还有7个任务,将任务分配给线程池,此时线程池开始调动线程执行这些任务
* 线程池中:核心线程数:8个(满了);非核心线程数:0个,阻塞队列中元素:0个
* 4.与上面8个任务一起来的还有8个任务,将任务分配给线程池,此时线程池开始调用线程执行这些任务
* 线程池中:核心线程数:8个(满了);非核心线程数:0个,阻塞队列中元素:8个
* 5.与上面8个任务一起来的还有92个任务,将任务分配给线程池,此时线程池开始调用线程执行这些任务
* 线程池中:核心线程数:8个(满了);非核心线程数:0个,阻塞队列中元素:100个(满了)
* 6.与上边92个任务一起来的还有12个任务,将任务分配给线程池,此时线程池开始调用线程执行这些任务
* 线程池中:核心线程数:8个(满了);非核心线程数:8个(满了),阻塞队列中元素:100个(满了)
* 7.再来任何数量的任务,都会触发拒绝策略:
* a:ThreadPoolExecutor.AbortPolicy->抛异常,不执行任务
* b:ThreadPoolExecutor.CallerRunsPolicy->执行任务,但是是谁主线程去执行,即提交线程任务的线程
* c.ThreadPoolExecutor.DiscardPolicy->不执行任务,啥也不做,丢弃线程
* d.ThreadPoolExecutor.DiscardOldestPolicy->出列等待时间最长的那一个,然后丢掉,将新任务交给线程次执行
* */
/*
* 线程池的方法shutdown()和shutdownNow()的区别:
* 一般情况下,当我们频繁的使用线程的时候,为了解决资源快速相应需求,我们都会考虑使用线程池,
* 线程池使用完毕之后使用以上两个方法进行关闭,一下列举以上两个方法的区别:
* shutdown()->它是将线程池的状态设置为SHUTDOWN状态,不过正在执行的任务会继续执行下去,没有被执行的则中断(不再执行)
* shutdownNow()->它是将线程池的状态设置为STOP,正在执行的任务会被停止,没有被执行的也进行中断(不再执行)
* */
}
JDK=JRE+java开发辅助工具....硬件系统->操作系统->JVM
JRE=JVM+java程序运行时所需要的类库
1.结构
a.类加载器->加载我们编写的类以及java里边的类到java虚拟机的内存空间,按需加载即第一次使用的时候才加载.只加载不校验正确与否.如果四种加载器都不能加载某个类,则直接ClassNotFindException.
b.内存区->
A:程序计数器->相当于执行计划.让CPU进行安全有序的执行.
B.栈->随着线程的创建而创建,随着线程的消失而消失
存储数据:方法形参变量,方法体内声明的变量,方法返回值变量.
栈帧结构:局部变量表(保存方法中声明的所有变量),操作数栈(方法运行过程中的临时数据),帧数据区(保存访问常量池指针,异常处理表).
父子关系:帧栈通过父子关系可以确定方法的调用关系;子父栈帧形成链表(父帧:指向当前栈针的调用者;子帧:当前栈帧调用的下一个栈帧).
栈溢出错误:栈给当前线程分配的空间被占满后,还要申请更多空间.
栈线程私有:当某一个线程抛出StackOverflowError不会影响其他线程,因为栈会给每个线程单独分配空间.
C.堆->线程共享,最大堆大小->计算机内存的四分之一;初始堆大小->计算机内存的六十四分之一.
D.方法区(永久代)->构造器,方法,static修饰的类变量和常量.线程共享.永久代的内存回收主要针对常量池的回收和类的卸载,因此可回收的对象很少.(常被用来存储常量,静态变量,类信息,类中方法的代码和class文件)
E.本地方法区->为native方法服务,它从本地方法接口拿到方法进行调用
c.本地方法接口->像前端调用我们的接口一样,我们也要调用本地方法
本地方法库->存储本地方法的地方(java中带native的方法)
2.类加载器
a.Bootstrap启动类加载器->加载$JAVA_HOME$/jre/lib/rt.jar里边的类.,如果我们想使用加载器对象.getParent() 这个方法去获取,是获取不到的,因为它是使用c++编写的.
b.Extension扩展类加载器->加载$JAVA_HOME$/jre/lib/ext/classes目录里边的类
c.APPClassLoader应用程序加载器->加载classpath中指定的jar包及目录中的class