先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
并发(多线程操作同一个资源)
- CPU 一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替并行(多个人一起行走)
并行:多个人一起走
- CPU多核,多个线程可以同时执行,线程池
public class Test1{
public static void main(String[] args){
//获取CPU的核数
//CPU密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用CPU的资源
线程有几个状态
public enum State {
//运行
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait/sleep 区别
1、 来自不同的类
wait => Object
sleep => Thread
一般企业中使用的休眠是:
TimeUnit.DAYS.sleep(1);//休眠一天
TimeUnit.SECONDS.sleep(1);//休眠1s
2、 关于锁的释放
wait 会释放锁
sleep 睡觉了,不会释放!
3、 使用的范围是不同的
wait必须使用在代码块中
.
sleep 可以再任何地方睡
4、是否需要捕获异常
wait 不需要捕获异常
sleep 必须要捕获异常
3、Lock锁(重点)
传统 Synchronized
/**
* 真正的多线程开发
* 线程就是一个单独的资源类,没有任何的附属操作!
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
//多线程操作
//并发:多线程操作同一个资源类,把资源类丢入线程
Ticket ticket = new Ticket();
//@FunctionalInterface 函数式接口 jdk1.8之后 lambda表达式
new Thread(()->{
for(int i=0;i<40;i++){
ticket.sale();
}
},"A").start();
new Thread(()->{
for(int i=0;i<40;i++){
ticket.sale();
}
},"B").start();
new Thread(()->{
for(int i=0;i<40;i++){
ticket.sale();
}
},"C").start();
}
}
//资源类
//属性+方法
//oop
class Ticket{
private int number=50;
//卖票的方式
// synchronized 本质:队列,锁
public synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+" 卖出了第"+number+" 张票,剩余:"+number+" 张票");
number--;
}
}
}
Lock 接口
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队 (默认为非公平锁)
Synchronized 和 Lock 区别
-
Synchronized 内置的Java关键字, Lock 是一个Java类
-
Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
-
Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
-
Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去;
-
Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以自己设置);
-
Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
锁是什么,如何判断锁的是谁!
4、生产者和消费者问题
面试的:单例模式、排序算法、生产者和消费者、死锁
生产者和消费者问题 Synchronized 版
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"B").start();
}
}
class Data{
//数字 资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if(number!=0){
//等待操作
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if(number==0){
//等待操作
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我-1完毕了
this.notifyAll();
}
}
问题存在,A B C D 4 个线程! 虚假唤醒
**解决方案:**if 改为 while 判断
/**
* 判断等待,业务,通知
*/
class Data2{//资源类 数字
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await();//等待
//condition.signalAll();//唤醒全部
//+1
public void increment() throws InterruptedException {
try {
lock.lock();
//业务代码
while (number !=0){
//等待操作
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我+1完毕了
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
//-1
public void decrement() throws InterruptedException {
try {
lock.lock();
//业务代码
while (number == 0){
//等待操作
condition.await();
}
number --;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我-1完毕了
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
JUC版的生产者和消费者问题
通过Lock 找到 Condition
代码实现:
/**
* 判断等待,业务,通知
*/
class Data2{//资源类 数字
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await();//等待
//condition.signalAll();//唤醒全部
//+1
public void increment() throws InterruptedException {
try {
lock.lock();
//业务代码
while (number !=0){
//等待操作
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我+1完毕了
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
//-1
public void decrement() throws InterruptedException {
try {
lock.lock();
//业务代码
while (number == 0){
//等待操作
condition.await();
}
number --;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我-1完毕了
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
5、8锁现象
如何判断锁的是谁!
锁会锁住:对象、Class
深刻理解我们的锁:
问题一:标准情况下,两个线程先打印 发短信 还是 打电话
结果是:
那么问题来了,为什么是这种顺序来打印的,是按顺序执行的吗?答案显然是错误的!
问题二:我们让打短信延迟4s
现在结果是什么情况呢?
结果依然是:发短信 打电话!
原因: synchronized 锁的对象是方法的调用者!两个方法使用的是同一把锁,谁先拿到,谁先执行!
问题三:添加一个普通方法,结果先执行哪一个呢?
结果:先执行hello()
在打印发短信!原因是hello()
是一个普通方法,并不是同步方法,不受Synchronized
锁的影响,如果把发短信里的TimeUnit.SECONDS.sleep(4)
去掉,那么就会顺序执行,限制性发短信再执行hello()
。原因方法sendSms()
和hello()
两者并不会产生影响,在sendSms()
中加入延时时,在线程开始时,就会等待4s
再执行,去掉之后,两个方法并不会有谁等待谁的关系,就会按照顺序进行执行!(个人理解)
问题四:如果使用两个对象,分别调用发短信 和 打电话 那么顺序是什么呢?
结果: 打电话 发短信。
原因: 在发短信中 延时了4s
,再加上Synchronized
锁的对象是方法的调用者,如果有两把锁,就会根据执行时间来决定打印顺序!
问题 5、6 如果在方法上加上
static
变成静态方法!结果又该如何?
结果: 发短信 打电话
原因: 对于static
静态方法来说,对于整个类Class
来说只有一份,对于不同的对象使用的是同一份方法,相当于这个方法是属于这个类的,如果静态方法static
使用了Synchronized
锁定,那么这个Synchronized
锁会锁住整个对象!不管多少个对象,对于静态的锁都只有一把锁,谁先拿到这个锁谁先执行!其他进程都需要等待!
问题七:如果把两个方法设置为一个静态方法、一个同步方法,结果又将如何?
结果: 打电话 发短信
原因: 因为一个锁的是Class
类模板,一个锁的是对象的调用者,call()
不需要等待发短信,直接运行!
问题八:一个静态方法、一个同步方法,使用两个对象进行分别调用,顺序是什么呢?
结果: 打电话 发短信
原因: 因为两个对象,一样的原因,两把锁锁的不是同一个东西,所以后面的第二个对象不需要等待第一个对象去执行!
6、集合类不安全
List不安全
//java.util.ConcurrentModificationException 并发修改异常
public class ListTest{
public static void main(String[] args){
List<String> list = new ArrayList<>();
for(int i = 1;i<=30;++i){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
},String.valueOf(i)).start();
}
}
}
执行结果:
结果:ArrayList
在并发情况下是不安全的!
解决方案:
- 切换成
Vector
就是线程安全的!
public class ListTest{
public static void main(String[] args){
List<String> list = new Vector<>();
for(int i = 1;i<=30;++i){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
},String.valueOf(i)).start();
}
}
}
- 使用
Collections.synchronizedList(new ArrayList<>());
public class ListTest{
public static void main(String[] args){
List<String> list = Collections.synchronizedList(new ArrayList<>());
for(int i = 1;i<=30;++i){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
},String.valueOf(i)).start();
}
}
}
- 使用
JUC
中的包:List<String> list = new CopyOnWriteArrayList<>();
public class ListTest{
public static void main(String[] args){
List<String> list = new CopyOnWriteArrayList<>();
for(int i = 1;i<=30;++i){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
},String.valueOf(i)).start();
}
}
}
CopyOnWriteArrayList
:写入时复制!COW计算机程序设计领域的一种优化策略
多个线程调用的时候,list,读取的时候,固定的,写入(覆盖);在写入的时候避免覆盖,造成数据的问题!
CopyOnWriteArrayList 比 Vector 厉害在哪里?
Vector
的addElement()
方法使用的是Synchronized
,一般使用Synchronized
效率较低
-
CopyOnWriteArrayList
的add
方法使用的是lock
锁
Set不安全
和List
属于同级,由于List
在并发情况下不安全,则Set
也是不安全的
解决方案:
-
使用
Collections
工具类的synchronized
包装的Set
类 -
使用
CopyOnWriteArraySet
写入复制的JUC
解决方案
//java.util.ConcurrentModificationException
public class SetTest {
public static void main(String[] args) {
Set<String> set1 = new HashSet<>();
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 100; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
HashSet底层是什么?
通过底层来看,HashSet
的底层其实就是一个HashMap
public HashSet() {
map = new HashMap<>();
}
//add 本质其实就是一个map的key,map的key是无法重复的,所以使用的就是map存储
//hashSet就是使用了hashmap key不能重复的原理
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//PRESENT是什么? 是一个常量 不会改变的常量 无用的占位
private static final Object PRESENT = new Object();
Map不安全
//map是这样用的吗? 不是,工作中不使用HashMap
//默认等价于什么? new HashMap<>(16,0.75);
//Map<String, String> map = new HashMap<>();
HashMap
默认加载因子是0.75
,默认的初始容量是16
同样的HashMap
基础类也存在并发修改异常!
public class HashMapTest {
public static void main(String[] args) {
//map是这样用的吗? 不是,工作中不使用HashMap
//默认等价于什么? new HashMap<>(16,0.75);
//Map<String, String> map = new HashMap<>();
// Collections.synchronizedMap()
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
解决方案:
-
使用
Collections.synchronizedMap(new HashMap<>());
处理 -
使用
new ConcurrentHashMap<>()
进行并发处理
7、Callable(简单)
-
可以有返回值
-
可以抛出异常
-
方法不同
run()/call()
代码测试
public class CallableTest{
public static void main(String[] args){
for(int i = 1;i<10;++i){
new Thread(new MyThread()).start;
}
}
}
class MyThread implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
使用Callable进行多线程操作:
Callable
泛型T
就是Call运行方法的返回值类型
Callable如何放入到Thread里面呢?
对于Thread
运行,只能传入Runnable
类型的参数
FutureTask
中可以接受Callable
参数
这样我们就可以先把Callable
放入FutureTask
中,如何再把FutureTask
放入到Thread
就可以了!
public class CallableTest {
public static void main(String[] args)throws Exception {
//new Thread(new Runnable()).start();
//new Thread(new FutuerTask<V>()).start
//构造器
//new Thread(new FutuerTask<V>( Callable )).start
for (int i = 1; i <= 10; i++) {
MyThread thread = new MyThread();
//适配类:FutureTask
FutureTask<String> futureTask = new FutureTask<>(thread);
//放入Thread使用
new Thread(futureTask,String.valueOf(i)).start();
//获取返回值
String s = futureTask.get();
System.out.println("返回值"+s);
}
}
}
class MyThread implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("Call:"+Thread.currentThread().getName());
return "String"+Thread.currentThread().getName();
}
}
这样我们就可以使用Callable
来进行多线程编程了。并且我们发现可以有返回值 了,并且可以抛出异常
8、常用的辅助类(必须会!)
8.1 CountDownLatch
其实就是一个减法计数器,对于计数器归零之后在进行后面的操作,这是一个计数器
//计数器
public class CountDownLatchDemo {
public static void main(String[] args) {
//总数是6 必须要执行的任何的时候,再使用!
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"走了");
countDownLatch.countDown();// 数量-1
},String.valueOf(i)).start();
}
try {
countDownLatch.await();//等待计数器归 0 ,然后再向下执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("关门");
}
}
主要方法:
-
countDown
减一操作 -
await
等待计数器归零
await
等待计数器为0,就唤醒,再继续向下执行。
8.2 CyclickBarrier
其实就是一个加法计数器
public class CyclicBarrierDemo {
public static void main(String[] args) {
/*
*集齐七颗龙珠召唤神龙
* */
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功!");
});
for (int i = 1; i <= 7 ; i++) {
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集了"+temp+"颗龙珠");
try {
cyclicBarrier.await();//等待
} catch(Exception e){
e.printStackTrace();
}
}).start();
}
}
}
8.3 Semaphore
Semaphore
:信号量
代码模拟抢车位
public class SemaphoreDemo {
public static void main(String[] args) {
/**
* 参数:线程数量 应用场景:限流!
*/
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 3; i++) {
new Thread(()->{
//acquire() 得到
try {
semaphore.acquire();//抢到车位
System.out.println(Thread.currentThread().getName()+"抢到了车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//release() 释放
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}
原理:
semaphore.acquire()
获取资源,如果资源已经使用完了,就等待资源释放后再进行使用!
semaphore.release()
释放,会将当前的信号量释放+1,然后唤醒等待的线程!
应用场景:
-
多个共享资源互斥的使用
-
并发限流,控制最大的线程数
9、阻塞队列
BlockingQueue
什么情况下会使用阻塞队列?
-
多线程并发处理
线程A调用线程B,则A必须等待线程B执行完之后才能执行
-
线程池
学会使用队列
| 方式 | 抛出异常 | 不会抛出异常,有返回值 | 阻塞 等待 | 超时等待 |
| — | — | — | — | — |
| 添加 | add() | offer() | put() | offer(,) |
| 移除 | remove() | poll() | take() | poll(,) |
| 检测队首元素 | element() | peek() | - | - |
/**
* 抛出异常
*/
public static void test1(){
//参数:队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
// java.lang.IllegalStateException: Queue full 队列满
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
System.out.println("========================");
// java.util.NoSuchElementException
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
}
/**
* 不抛出异常
*/
public static void test2(){
//队列的大小
ArrayBlockingQueue<Object> 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"));//false 不抛出异常!
System.out.println("-=======================");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());//null 不抛出异常!
}
/*
* 等待,阻塞(一直阻塞)
* */
public static void test3() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");//队列没有位置了,一直阻塞
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
//System.out.println(blockingQueue.take()); 没有这个元素,一直等待
}
/*
* 等待,阻塞(等待超时)
* */
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> 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", 3, TimeUnit.SECONDS));//超时2秒
System.out.println("==========================");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
}
SynchronousQueue 同步队列
/**
* 同步队列
* 和其他的BlockingQueue 不一样,SynchronousQueue 不存储元素
* put了一个元素,必须从里面先take取出来,否则不能在put进去值!
*/
public class SynchronousQueueTest {
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 (Exception e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+ synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
} catch (Exception e) {
e.printStackTrace();
}
},"T2").start();
}
}
10、线程池(重点)
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用!=> 池化技术
线程池、连接池、对象池、内存池///… 创建、销毁十分浪费资源
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。
线程池的好处:
-
降低资源的消耗
-
提高响应的速度
-
方便管理
线程复用,可以控制最大并发数,管理线程
线程池:三大方法
/**
* @function Executors 工具类、3大方法
* @author 派 大 星
* @date 2022/3/31 22:49
*/
public class Demo01 {
public static void main(String[] args) {
// 单个线程
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 创建一个固定的线程池的大小
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 可伸缩的,遇强则强,遇弱则弱
ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 1; i <= 100; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 线程池用完,程序执行完关闭线程池
threadPool.shutdown();
}
}
}
7大参数
源码分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE ,// 约21亿 oom溢出
60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
// 本质:ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,// 最大核心线程池大小
long keepAliveTime,// 超时了没人调用就会释放
TimeUnit unit,// 超时单位
BlockingQueue<Runnable> workQueue,// 阻塞队列
ThreadFactory threadFactory,// 线程工厂,创建线程的,一般不用动
RejectedExecutionHandler handler// 拒绝策略) {
if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
手动创建一个线程池
/**
* @author 派 大 星
* @function
* @date 2022/4/1 7:12
* 四种拒绝策略
*/
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
try {
for (int i = 1; i <= 9; i++) {
//使用自定义线程池创建线程
//最大承载:Deque + max(双端队列 + 最大线程池数)
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程
threadPoolExecutor.shutdown();
}
}
}
四种拒绝策略
/**
* new ThreadPoolExecutor.AbortPolicy() 银行满了,还有人进来,不处理这个人,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() 哪来的去哪里!
* new ThreadPoolExecutor.DiscardPolicy() 队列满了,丢掉任务,不会抛出异常!
* new ThreadPoolExecutor.DiscardOldestPolicy() 队列满了。尝试去和最早的竞争(竞争成功,执行,竞争不成功,则丢掉任务),也不会抛出异常!
*/
小结和拓展
了解:IO密集型、CPU密集型
// 最大线程到底如何定义?
// 1. CPU 密集型 CPU几核,就是几,可以保持CPU的效率最高!Runtime.getRuntime().availableProcessors()
// 2. IO 密集型 > 判断你程序中十分消耗资IO的线程
// 程序 15个大型任务,io十分占用资源
11、四大函数式接口(必须掌握)
新时代程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//简化编程模型,在新版本框架底层大量应用
//foreach(消费者类型的函数式接口)
Function
函数式接口
/**
* @author 派 大 星
* @function
* @date 2022/4/1 8:05
* Function 函数型接口,有一个输入参数,有一个输出
* 只要是函数型接口,可以用lambda表达式简化
*
*/
public class Demo01 {
public static void main(String[] args) {
// 工具类:输出输入的值
//
// Function<String, String> function = new Function<String, String>() {
// @Override
// public String apply(String s) {
// return s;
// }
// };
// lambda表达式简化
Function<String, String> function = (str)->{return str;};
System.out.println(function.apply("123"));
}
}
Predicate
断定型接口:有一个输入参数,返回值只能是 布尔值!
/**
* @author 派 大 星
* @function
* @date 2022/4/1 19:42
* 断定性接口:有一个输入参数,返回值只能是 布尔值!
*/
public class Demo02 {
public static void main(String[] args) {
//判断字符串是否为空
// Predicate<String> predicate = new Predicate<String>() {
// @Override
// public boolean test(String s) {
// return s.isEmpty();
// }
// };
Predicate<String> predicate = (str)->{
return str.isEmpty();
};
System.out.println(predicate.test(""));
}
}
Consumer
消费型接口
/**
* @author 派 大 星
* @function
* @date 2022/4/1 19:48
* Consumer 消费型接口:只有输入,没有返回值!
*/
public class Demo03 {
public static void main(String[] args) {
// Consumer<String> consumer = new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// };
Consumer<String> consumer =(str)->{
System.out.println(str);
};
consumer.accept("dadada");
}
}
Supplier
供给型接口
/**
* @author 派 大 星
* @function
* @date 2022/4/1 19:52
* Supplier 消费型接口:没有参数,只有返回值!
*/
public class Demo04 {
public static void main(String[] args) {
// Supplier<String> supplier = new Supplier<String>() {
// @Override
// public String get() {
// return "null";
// }
// };
Supplier<String> supplier =()->{return "xxxx";};
System.out.println(supplier.get());
}
}
12、Stream流式计算
什么是Stream流式计算?
大数据:存储+计算
集合、Mysql 本质就是存储东西!
/**
* @author 派 大 星
* @function
* @date 2022/4/1 20:14
* 题目要求:一分钟内完成此题,只能用一行代码实现!
* 现有5个用户!筛选
* 1. ID必须是偶数
* 2. 年龄必须大于23
* 3. 用户名转为大写字母
* 4. 用户名字母倒着排序
* 5. 只输出一个用户
*/
public class Test {
public static void main(String[] args) {
User user1 = new User(1,"a",21);
User user2 = new User(2,"b",22);
User user3 = new User(3,"c",23);
User user4 = new User(4,"d",24);
User user5 = new User(6,"e",25);
List<User> users = Arrays.asList(user1, user2, user3, user4, user5);
//集合就是存储
users.stream()
// 过滤 filter(Predicate<? super T> predicate);
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
// map(Function<? super T, ? extends R> mapper);
.map(u->{return u.getName().toUpperCase();})
//sorted(Comparator<? super T> comparator);
.sorted((u1,u2)->{return u2.compareTo(u1);})
.limit(1)
.forEach(System.out::println);
}
}
13、ForkJoin
什么是ForkJoin
ForkJoin在JDK1.7 ,并行执行任务!提高效率,大数据量!
ForkJoin特点:工作窃取
这个里面维护的都是双端队列
如何理解工作窃取
:现有两个线程A、B,假设B提前执行结束,但是B不能一直等待A执行结束,所以B会窃取
A的任务进行执行
ForkJoin如何使用
/**
* @author 派 大 星
* @function
* @date 2022/4/1 21:04
* 求和计算任务
* 如何使用ForkJoin
* 1. ForkJoinPool 通过它来执行
* 2. 计算任务 ForkJoinPool.execute(ForkJoinTask task)
* 3. 计算类必须继承 ForkJOinTask
*/
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;
}
public void test(){
}
//计算方法
@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 task1 = new ForkJoinDemo(start, middle);
//拆分任务,把任务压入线程队列
task1.fork();
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
//拆分任务,把任务压入线程队列
task2.fork();
return task1.join() + task2.join();
}
}
}
测试:
/**
* @author 派 大 星
* @function
* @date 2022/4/1 21:35
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test3();
}
public static void test1(){
//3724
Long sum = 0L;
long start = System.currentTimeMillis();
for (int i = 1; i <= 10_0000_0000; i++) {
sum+=i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
# 惊喜
最后还准备了一套上面资料对应的面试题(有答案哦)和面试时的高频面试算法题(如果面试准备时间不够,那么集中把这些算法题做完即可,命中率高达85%+)
![image.png](https://img-blog.csdnimg.cn/img_convert/f0cd12f0f183937b0caf59073b72a94d.webp?x-oss-process=image/format,png)
![image.png](https://img-blog.csdnimg.cn/img_convert/a09394b5b2a8680c4eea484291bf2d7c.webp?x-oss-process=image/format,png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)**
![img](https://img-blog.csdnimg.cn/img_convert/74a9f8d24668d6dbbf08a45d58d1ae8c.jpeg)
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rS-IOWkpyDmmJ8u,size_12,color_FFFFFF,t_70,g_se,x_16)
如何理解`工作窃取`:现有两个线程A、B,假设B提前执行结束,但是B不能一直等待A执行结束,所以B会`窃取`A的任务进行执行
> ForkJoin如何使用
![在这里插入图片描述](https://img-blog.csdnimg.cn/b8425b8145dc448b9e1a61e6c9d117b9.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/5fbf306b114a495b9560d3911463d830.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rS-IOWkpyDmmJ8u,size_18,color_FFFFFF,t_70,g_se,x_16)
/**
-
@author 派 大 星
-
@function
-
@date 2022/4/1 21:04
-
求和计算任务
-
如何使用ForkJoin
-
- ForkJoinPool 通过它来执行
-
- 计算任务 ForkJoinPool.execute(ForkJoinTask task)
-
- 计算类必须继承 ForkJOinTask
*/
public class ForkJoinDemo extends RecursiveTask {
private Long start;
private Long end;
//临界值
private Long temp = 10000L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
public void test(){
}
//计算方法
@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 task1 = new ForkJoinDemo(start, middle);
//拆分任务,把任务压入线程队列
task1.fork();
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
//拆分任务,把任务压入线程队列
task2.fork();
return task1.join() + task2.join();
}
}
}
**测试:**
/**
-
@author 派 大 星
-
@function
-
@date 2022/4/1 21:35
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test3();
}
public static void test1(){
//3724
Long sum = 0L;
long start = System.currentTimeMillis();
for (int i = 1; i <= 10_0000_0000; i++) {
sum+=i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+"时间:"+(end-start));
惊喜
最后还准备了一套上面资料对应的面试题(有答案哦)和面试时的高频面试算法题(如果面试准备时间不够,那么集中把这些算法题做完即可,命中率高达85%+)
[外链图片转存中…(img-LBO43jJP-1713421854249)]
[外链图片转存中…(img-ECjAXqAE-1713421854249)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-ZaQEA1cS-1713421854249)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!