Java多线程
参考文章(https://baijiahao.baidu.com/s?id=1685762284067131135&wfr=spider&for=pc)
java多线程:
线程类都要实现runable或者callable接口
runable:无返回值
callable:有返回值
Executors能创建四种线程池:
Executors.newCachedThreadPool:可变长度线程池,线程可无限增加(oom)
Executors.newFixedThreadPool: 定长线程池,线程池大小可限定,超过限定不再新增线程,队列是无堵塞队列(无限增加线程任务会oom)
Executors.newSingleThreadExecutor: 单例线程池,单线程依次执行线程任务,队列是无堵塞队列(无限增加线程任务会oom)。
Executors.newScheduledThreadPool:定时任务线程池,线程可无限增加(oom)
ThreadPoolExecutor pool1 = new ThreadPoolExecutor()(推荐线程池创建方式,但是得自己设置参数,比如核心线程数,最大线程数,空闲线程超时时间等)
关键:
synchronized:锁
分为对象锁和类锁。
对象锁:指锁住某个对象,有多个线程调用该对象时,需要依次调用,不能并发调用,因为锁一次只能有一个线程获取。
类锁:指整个类都被锁住了,多个线程调用该类时,只能依次调用。(仅用于静态方法)举例:看截图
也可以不使用同步锁关键字 直接使用java封装的互斥锁 Lock lock = new ReentrantLock();
加锁 lock.lock();
释放锁: lock.unlock();
让线程进入wait : lock.wait()
唤醒线程:lock.notify() lock.notifyall();
synchronized有锁升级的过程
无锁:最开始对象没有锁住线程的状态
偏向锁:线程将线程ID给予锁对象的对象头中,下次线程来就直接锁住。
自旋锁:多个线程互相竞争锁。
重量锁:多个对象互相竞争超过一定数量和一段时间后升级为重量级锁。
锁降级只发生在GC过程中。
每一次锁的资源都是向CPU申请的,而CPU锁的资源是有限的,如果申请完了就会进入申请等待的过程,很耗时间。
ReentrantLock分为公平锁和非公平锁
new ReentrantLock(true)公平锁:线程竞争依次竞争
new ReentrantLock(false)非公平锁:出现插队行为
对象在堆中的对象头
Class TestCallAble implements CallAble{
run(){
synchronized(this) {
sout("test");
}
}
}
main(){
//像这种情况 锁就没用 因为创建了线程任务对象a1 线程任务对象a2 且分配两个线程分别执行a1,a2任务 不会有两个同时执行一个任务
//除非多个线程执行同一个线程任务才会去争锁类似:pool.submit(a1);pool.submit(a1);两个线程执行同一个任务
//对象锁也是两个 a1,a2都分别有自己的对象锁
TestCallAble a1 = new TestCallAble ();
TestCallAble a2 = new TestCallAble ();
pool.submit(a1);
pool.submit(a2);
}
//换个方式:将锁放在静态方法上就变成了类锁,虽然同样是两个线程任务对象,但是由于类的静态方法是在对象创建前加载的,因此该类的所有实例调用静态方法都要争锁。
现在拿个例子来详细说明:
让两个线程轮流打印0~100之间的数字。
AtomicInteger:线程可见性,每次更改所有线程可见。
见代码 由于是新建了两个任务对象,且锁是加在lock对象上的,如果不从外部传入一个共用对象,则锁就是加在了两个任务对象实例各自的lock属性对象上,线程只会执行各自的线程任务,无法去争同一个线程任务的锁。
同样的由于原子变量也要从外部传入,不然也是各自任务对象的原子变量;(举例:当第一个线程进入任务线程1的 run()方法,原子变量是内部初始化的1,第二个线程进入任务线程2的 run()方法,原子变量也是任务线程2的对象属性,也是初始化的1,
但是通过外部引入,则线程任务1和线程任务2的原子变量的引入地址就是同一个原子变量(外部的原子变量flag的地址))lock对象同理,因为两个线程任务对象的lock指向同一个lock对象(外部对象),因此两个线程执行各自线程任务对象的run方法时,则需要去争锁,因为lock对象被锁住了,一次只能有一个线程拿到锁。
介绍完上面:讲代码执行流程:
新建两个线程任务,然后线程池分配两个线程分别执行任务1和任务2,假设线程1先执行(因为线程执行无序),进入 run()方法,执行代码,发现lock对象有锁,于是率先抢到锁,线程2同时也进入了线程任务2的run(),但是发现锁已经被线程1拿到了,线程2只能等待,线程1发现flag==1因此打印且将flag 设置为2,然后调用notify ()方法,唤醒该对象锁wait的一个线程(如果要唤醒多个用notifyall()),然后调用wait()方法,让执行该代码的线程(线程1执行该代码)进入 wait状态且释放锁。
然后锁被线程2拿到,线程2由于flag==2,因此执行打印,然后调用notify()将其他线程进入唤醒状态(线程1是wait状态变成唤醒状态),然后调用wait()方法,线程2进入wait状态然后释放锁。
然后锁被线程1拿到。。。。循环上述步骤。即实现了线程1和线程2轮流打印。
如果要实现
线程1打印0
线程2打印1
线程1打印2
线程2打印3
。。。。
将上述循环改成50次,然后再新增一个原子变量,每次++,然后打印即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix