5.多线程学习
创始人
2025-06-01 19:38:17
0

作者:爱塔居

专栏:JavaEE

作者简介:大三学生,喜欢总结与分享~

文章目录

目录

文章目录

章节回顾

一、wait 和notify

二、设计模式

2.1 单例模式


章节回顾

线程安全

1.一个线程不安全的案例(两个线程各自自增5w次,结果不是10w)

2.线程不安全的原因

(1)抢占式执行,随机调度。线程中的代码执行到任意一行,都随意可能被切换出去。

(2)多个线程同时修改同一个变量。

(3)修改操作不是原子的。

(4)内存可见性(volatile)编译器可能会对我们的代码进行优化。

一个线程频繁读,一个线程修改

(5)指令重排序。

除了这些原因,还有其他原因。

3.解决方案  加锁

在线程1加锁的过程中,线程2无法把自己的指令插入到线程1的修改过程中。

synchronized指定一个“锁对象”

4.volatile

关于volatile和内存可见性补充

内存可见性:

t1频繁读取主内存,效率比较低,就被优化成直接读自己的工作内存。

t2修改了主内存的结果,由于t1没有读主内存,导致修改不能被识别到。

工作内存=>CPU寄存器

主内存=>内存

工作内存和主内存都是由英文work memory和main memory翻译来的。所以,工作内存不一定非要是内存,可以是记忆,存储区,不一定是特指“内存条”。

这一套说法,也称为JMM(java memory model)

java是跨平台的。

1.兼容多种操作系统

2.兼容多种硬件设备

不同的硬件,其实差别很大。cpu和cpu之间,差别就会比较大。

像以前的cpu,上面只有寄存器。现在的cpu上面还有缓存。

而且有的cpu缓存还有好几个,L1,L2,L3,(现在常见的cpu都是3级缓存)

工作内存准确来说,代表cpu寄存器+缓存(CPU内部存储数据的空间)

 cpu读储存器速度比读内存快3-4个数量级。

 缓存读取速度介于寄存器和内存之间。

L1最快,空间最小(仍然比寄存器慢)

L3最快,空间最大(仍然比内存快很多)

实际上cpu尝试读一个内存数据:

1.先看寄存器里有没有

2.没有,看L1有没有

3.没有,看L2有没有

4.没有,看L3有没有

5.没有,看内存有没有

具体缓存的大小,对于程序效率的影响,也看实际的应用场景。


一、wait 和notify

线程的调度是无序的,随机的。但是,也是有一定的需求场景的,希望线程有序执行。

join是一种控制顺序的方式,但是功效有限。

wait就是让某个线程先暂停下来,等一等。

wait主要做三件事:

1.解锁

2.阻塞等待

3.当收到通知的时候,就唤醒,同时尝试重新获取锁。

notify就是把该线程唤醒,能够继续执行。

wait和notify是Object的方法

只要你是个类对象(不是内置类型/基本数据类型),都是可以使用wait和notify。

public class test {static int i;public static void main(String argv[]) throws InterruptedException{Object locker=new Object();Thread t1=new Thread(()->{while (true){try {System.out.println("wait 开始");synchronized (locker){locker.wait();}System.out.println("wait 结束");}catch (InterruptedException e){e.printStackTrace();}}});t1.start();Thread.sleep(1000);Thread t2=new Thread(()->{synchronized (locker){System.out.println("notify 开始");locker.notify();System.out.println("notify 结束");}});t2.start();}
}

 使用外套,阻塞等待会让线程进入WAITING状态。wait也提供了一个带参数的版本,参数指定的是最大等待时间。不带参数的wait是死等,带参数的wait就会等最大时间之后,还没有人通知,就自己唤醒自己。

wait会导致阻塞,竞争锁也会导致阻塞,两种不同的进入阻塞的方式。wait的初心就是为了实现阻塞的效果。

join只能是让t2的线程先执行完,再继续执行t1,一定是串行的

wait、notify,可以让t2执行完一部分,再让t1执行一部分,再让t2去执行,再……

唤醒操作,还有一个notifyAll。可以有多个线程,等待同一个对象。

比如在t1,t2,t3中都调用object.wait。此时在main中调用了object.notify 会随机唤醒上述的一个线程。(另外两个仍然会是waiting状态)

如果是调用了object.notifyAll,此时就会把上述三个线程都唤醒。伺候这三个线程就会重新竞争锁,然后依次执行。

总结:

1.wait需要搭配synchronized使用,sleep不需要。

2.wait是Object的方法,sleep是Thread的静态方法。

wait和sleep都是可以提前唤醒的。

他们最大的区别在于初心不同。

wait解决的是线程之间的顺序控制

sleep单纯是让当前线程休眠一会。

二、设计模式

设计模式,就是软件开发中的棋谱。大佬们针对一些常见场景,总结出来的代码的编写套路。设计模式有很多种。

在校招阶段,主要考察两个设计模式。

1.单例模式

2.工厂模式

设计模式需要大家有一定的开发经验的积累,才好理解。

2.1 单例模式

单例指的是单个实例(instance)对象。类的实例,就是对象。Java中的单例模式,借助java语法,保证某个类,只能够创建出一个实例,而不能new多次。

有些场景,本身就是要求某个概念是单例的。

//把这个类设定为单例
class Singleton{//唯一的实例的实体
private static Singleton instance=new Singleton();
//被static修饰,该属性是类的属性。JVM中,每个类的类对象只有唯一一份,类对象里的这个成员自然也是唯一一份了。
//获取到实例的方法public static Singleton getInstance(){return instance;}//禁止外部new实例private Singleton(){};
}
public class test {public static void main(String[] args) {Singleton s1=Singleton.getInstance();Singleton s2=Singleton.getInstance();}}

java中实现单例模式是有多种写法的。

主要说两种:

1.饿汉模式(急迫)

2.懒汉模式(从容)

A吃完饭立马洗碗(饿汉行为)

B吃完饭,不洗碗,等下一顿要用碗的时候,再洗碗。(懒汉行为)

通常认为,懒汉模式更好,效率更高。(非必要,不洗碗)

举一个计算机的例子:

打开一个硬盘上的文件,读取文件内容,并显示出来。

饿汉:把文件所有内容都读到内存中,并显示出来

懒汉:只把文件读一小部分,把当前的屏幕填充上,如果用户翻页,再读其他文件内容。

当文件特别大的时候,就可以看出懒汉模式的优势了。

饿汉模式:

//把这个类设定为单例,饿汉
class Singleton{//唯一的实例的实体
private static Singleton instance=new Singleton();
//被static修饰,该属性是类的属性。JVM中,每个类的类对象只有唯一一份,类对象里的这个成员自然也是唯一一份了。
//获取到实例的方法public static Singleton getInstance(){return instance;}//禁止外部new实例private Singleton(){};
}
public class test {public static void main(String[] args) {Singleton s1=Singleton.getInstance();Singleton s2=Singleton.getInstance();}}

懒汉模式:

//把这个类设定为单例模式中的懒汉模式。
class SingletonLazy{
private static SingletonLazy instance=null;public static SingletonLazy getInstance(){if(instance==null){instance=new SingletonLazy();}return instance;}private SingletonLazy(){};
}
public class test {public static void main(String[] args) {SingletonLazy s1=SingletonLazy.getInstance();SingletonLazy s2=SingletonLazy.getInstance();System.out.println(s1==s2);}}

 饿汉模式一开始就把实例创建好了,而懒汉模式是非必要不创建实例。

上述两个代码,是否是线程安全的?多个线程下调用getInstance,是否会出现问题?

饿汉模式,认为是线程安全的,只是读数据。

而在多线程下,懒汉模式可能无法保证创建对象的唯一性。

比如以下情况:

 如何解决上诉线程安全问题?

进行加锁,保证判定和new操作是原子性的。

//把这个类设定为单例模式中的懒汉模式。
class SingletonLazy{
private static SingletonLazy instance=null;synchronized public static SingletonLazy getInstance(){if(instance==null){instance=new SingletonLazy();}return instance;}private SingletonLazy(){};
}
public class test {public static void main(String[] args) {SingletonLazy s1=SingletonLazy.getInstance();SingletonLazy s2=SingletonLazy.getInstance();System.out.println(s1==s2);}}

相关内容

热门资讯

港金管局: 难以预测外汇基金第... 来源:观点地产网 观点网 香港报道:5月4日,香港金管局总裁余伟文表示,首季中东冲突后,股市、债市都...
原创 1... 哈尔滨的老乡聚会上,20岁的孔令辉第一次看见马苏。那一年,他刚刚摘得悉尼奥运会男单金牌,光芒四射,被...
有一个方子,不仅能拯救“不开心... 导读:今天学习开郁种玉汤相关的医论知识,理解肝郁致不孕的病因病机。 开郁种玉汤 【组成】白芍30克...
915亿元资金已下达!支持这些... 记者从国家发展改革委了解到,今年第二批915亿元超长期特别国债支持设备更新资金已下达。 据了解, 新...
快消品牌的消费者画像,不能再靠... 前段时间,跟一个快消品牌的营销总监交流。 聊到消费者洞察,他拿出一份PPT,第一页写着:核心消费群体...
汇丰控股一季度税前利润93.7... 香港5月5日电 (记者 戴小橦)汇丰控股有限公司(简称“汇丰控股”)5日公布2026年一季度业绩。截...
金饰价格跌破1400元 美东时间5月4日收盘,现货黄金报4521.190美元/盎司,跌2.02%;COMEX黄金期货报453...
标普500指数的11个板块全线... 标普500指数的11个板块全线收涨,原材料板块涨1.67%,信息技术/科技板块涨1.63%,工业板块...
打卡就能领券丨香坊万达“火锅+... 近日, 哈尔滨香坊万达广场“好好吃饭奶茶火锅季”主题消费活动启幕,本次活动聚焦年轻人喜好,政企携手以...
【裕信银行CEO:取得德国商业... 【裕信银行CEO:取得德国商业银行控制权“并非预期情景”】意大利裕信银行CEOAndrea Orce...
前妻带孩子来看我,开口就要50... “陆远,安安该上学了。” 苏晚晴把书包往玄关柜上一放,指尖敲了敲茶几上的购房合同。 “这学区房首付五...
震惊全场!首个提问,来自“AI... AI版“巴菲特”、2.3亿美元回购,伯克希尔股东大会开场不久,亮点纷呈。 北京时间5月2日晚, 20...
原创 当... 当成都一个城市的GDP占到了整个四川省的36%以上,当中西部省份纷纷效仿这种“集全省之力打造单极核心...
孙宇晨香港发声:波场TRON抢... 4月21日,香港Web3嘉年华主会场,座无虚席。 一场聚焦“AI+区块链”技术融合的行业峰会正在进行...
最高190亿元!600673,... 5月5日晚间,东阳光发布公告称,公司近日收到控股子公司东莞东阳光云智算科技有限公司(以下简称“东阳光...
茂名市独立网站建设公司 这是(fsdjwl)整理的信息,希望能帮助到大家 在现代数字化环境中,企业建立独立网站已成为标准配置...
2026揭秘神话铂族金属在未来... 今天分享的是:2026揭秘神话铂族金属在未来氢能的作用白皮书 报告共计:20页 揭秘铂族金属在氢能产...
原创 战... 《孙子兵法》开篇的那句话至今仍令人心潮澎湃:兵者,国之大事,死生之地,存亡之道,不可不察也。用通俗的...
原创 9... #热爆趣创赛#最近AI 概念涨得一个比一个离谱,身边人都在喊“再不上车就踏空了”。 但就在所有人都...
金价,下跌!创史上最大双月跌幅 据新华社消息 中东紧张局势再度升级 金银价格5月4日大跌 北京时间5月5日 现货黄金、现货白银 开盘...