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

相关内容

热门资讯

提振消费,如何增强供需适配性 云南大理白族自治州,游客(左)与摄影师一起挑选照片。 北京朝阳区,一名智能柜补货员在卸货。 以上图...
贝仕达克:预计2025年度净利... 每经AI快讯,贝仕达克1月30日晚间发布业绩预告,预计2025年归属于上市公司股东的净利润860万元...
英媒:随着就业市场降温,美国大... 来源:格隆汇APP 格隆汇1月30日|据英国金融时报,本周,美国的一些大型企业公布了裁员计划,预计将...
终结5连败!德约3-2逆转辛纳... 北京时间1月30日,2026赛季网球大满贯澳大利亚公开赛继续进行,在男单下半区的半决赛中,塞尔维亚天...
去年辽宁非金融企业债务融资达6... 1月30日,人民银行辽宁省分行召开2026年一季度新闻发布会,介绍2025年度辽宁省金融运行主要情况...
“大V带货”遭监管重拳:基金销... 记者 洪小棠 1月29日,证监会证券基金机构监管司发布了新一期《机构监管情况通报》(下称《通报》),...
ST宁科完成组织架构重大调整 ... 来源:新浪财经-鹰眼工作室 【财经网讯】宁夏中科生物科技股份有限公司(证券代码:600165,股票简...
原创 i... 很多人看到苹果这份“史上最强”季度成绩单时,第一反应都是:这销量也太夸张了吧? 尤其是大中华区 ...
Cloudflare入驻B站和... IT之家 1 月 30 日消息,Cloudflare 宣布入驻B站和小红书,认证显示为“Cloudf...
首日涨超160% 智能制造装备... 上证报中国证券网讯(记者 张雪)1月30日,美德乐正式登陆北交所。截至当日收盘,公司股价报109.5...
特朗普提名下一任美联储主席 据新华社消息,美国总统特朗普30日提名美联储前理事凯文·沃什为下任美联储主席,这一提名还需获得参议院...
由盈转亏、业绩下滑超85%!2... 面对每天上千份上市公司公告该看哪些?重大事项公告动辄几十页几百页重点是啥?公告里一堆专业术语不知道算...
原创 华... 金价的上涨和美元的下跌已经让整个依赖美西方货币体系和金融体系获利的人感受到了巨大的威胁。 在美国财政...
康佳集团原董事长周彬、原副总裁... 老牌家电巨头康佳集团(000016)在经历控制权变更与管理层换血的震荡期后,迎来了更为剧烈的“余震”...
安诚财险2025年揽收保费52... (图片来源:视觉中国) 蓝鲸新闻1月30日讯(记者 陈晓娟)日前,安诚财产保险股份有限公司(下称“安...
国际金价、银价,暴跌! 据新华社1月30日消息,国际黄金和白银价格1月29日上演“过山车”行情,双双站上高位后又暴跌,市场剧...
A股115家半导体公司2025... 近期,A股半导体行业上市公司陆续披露半年度业绩预告。据集微网统计,截至2026年1月30日,在已披露...
一图读懂服务消费新政:涉及交通... 红星资本局1月30日消息,为优化和扩大服务供给,聚焦重点领域、潜力领域,加快培育服务消费新增长点,促...
沪农商行:着力于稳健运营、控制... 证券日报网1月30日讯 ,沪农商行在接受调研者提问时表示,投资交易策略方面,公司将基于对2026年宏...
实力“圈粉”全球客:去年上海离... 记者从市税务局获悉,2025年境外旅客在沪办理退税申请单数量同比增长3倍,退税商品销售额和退税额均增...