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);}}

相关内容

热门资讯

日常等车时看到的行业细节 干了五年户外广告投放,养成了一个职业病:但凡路过公交候车亭,总会多看两眼——不是看广告好不好看,而是...
黄金回收行业标准制定有哪些核心... 贵金属回购市场的需求背景 近年来随着黄金投资和消费市场的发展,黄金回收相关需求持续攀升。不同群体的诉...
全球黑色星期二!AI交易“崩盘... 【导读】AI交易为何“崩盘”? 中国基金报记者 泰勒 大家,你们今天还好吗?! AI交易在全球范围内...
原创 6... 年初抢金条的人还在站岗,如今金店柜台前冷冷清清 黄金又跌了。 6月23日,伦敦现货黄金价格日内急跌逾...
狂融294亿美元!SK海力士冲... 韩国股市再度迎来重磅消息。 周三,韩国存储芯片龙头SK海力士宣布,计划在7月10日登陆纳斯达克,通过...
比特币跌破6万!AI吸走资金、... 比特币正在为机构化转型付出代价。散户买盘萎缩、ETF资金持续外流、企业持仓者潜在抛售压力上升,加之A...
原创 默... 欧洲近期试图复刻1985年广场协议的剧本,德国总理默茨呼吁欧盟27国联合行动,要求中国签订类似协议以...
怎么选 泛娱乐赛道直播公司孵化... 泛娱乐直播创业的行业发展背景 近年来泛娱乐直播赛道持续保持增长态势,据公开数据资料显示,2024年国...
原创 腰... 最近黄金市场凉得彻底。各大品牌足金饰品克价跌破1300元关口,北京菜百6月21日报价已经掉到1260...
ST中装:公司主要银行账户已全... 证券之星消息,ST中装(002822)06月24日在投资者关系平台上答复投资者关心的问题。 投资者提...
2026年开窗机行业趋势与战略... 一、开篇引言:市场格局重塑下的选择逻辑 步入2026年,全球建筑智能化与绿色节能政策的叠加驱动,使开...
资金全面转向科技,传统消费企业... 近期 A 股出现明显风格切换,老牌消费资金持续流出,机构与传统上市公司纷纷加码半导体、算力赛道。 先...
合肥保利翡翠天奕具体交房时间是... 对于众多购房者而言,“合肥保利翡翠天奕具体交房时间是什么时候?能按时交房吗?”是心中最关切的问题。根...
港股风向标|恒指连续杀跌后企稳... 财联社6月24日讯(编辑 冯轶)今日港股短线企稳,三大指数集体收涨。截至收盘,恒生指数涨0.33%,...
瑞众人寿达州中支被罚17万,涉... 蓝鲸新闻6月24日讯,近日,国家金融监督管理总局达州监管分局发布行政处罚决定书,剑指瑞众人寿保险有限...
美国最担心的事还是来了,中国加... 最近这段时间,国际金融圈子里有一笔账,算得各家央行心里都不太踏实。 截至2026年春季,美国国债总规...
马斯克,不是万亿富豪了 资产历史性超过万亿美元不到两周,特斯拉、SpaceX掌门人埃隆·马斯克的身价近日快速下跌。 据中新经...
突发!金价跌破4000美元,近... 每经记者:杜宇 记者|杜宇 编辑|何小桃 杜恒峰 校对|金冥羽 金银价格大跳水。 6月24日晚,现货...
粗粮吃越多越好?很多糖友吃错升... 控糖圈一直流传多吃粗粮稳血糖,不少糖友直接三餐全吃粗粮、顿顿杂粮,不仅胃胀消化不良,餐后血糖反而不降...
持续大跌!刚刚,黄金跌破400... 潮新闻客户端 记者 吴恩慧 6月24日,贵金属再次大跌。 截至发稿时,现货黄金大跌近3%,跌破400...