5.多线程学习

作者:爱塔居

专栏: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);
    }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2976.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch 之 基于经典网络架构训练图像分类模型

文章目录一、 模块简单介绍1. 数据预处理部分2. 网络模块设置3. 网络模型保存与测试二、数据读取与预处理操作1. 制作数据源2. 读取标签对应的实际名字3. 展示数据三、模型构建与实现1. 加载 models 中提供的模型,并且直接用训练的好权重当做初始化参数2. 参考 pyto…

【Linux】基于阻塞队列的生产者消费者模型

​🌠 作者:阿亮joy. 🎆专栏:《学会Linux》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉为何要使用…

谈谈低代码的安全问题,一文全给你解决喽

低代码是一种软件开发方法,通过使用图形化用户界面和可视化建模工具,以及自动生成代码的技术,使得开发人员可以更快速地构建和发布应用程序。 作为近些年软件开发市场热门之一,市面上也涌现了许多低代码产品,诸如简道云…

SpringCloud:统一网关Gateway

目录 1、网关介绍 2、搭建网关服务 3、路由断言工厂 4、路由过滤器 5、全局过滤器GlobalFilter 6、过滤器执行顺序 7、跨域问题处理 1、网关介绍 网关(Gateway)又称网间连接器、协议转换器。网关在网络层以上实现网络互连,是复杂的网络互 连设备&#xff0…

常见背包问题

一.前言若你想学习或正在学习动态规划,背包问题一定是你需要了解的一种题型,并且大多数人最初都是从背包问题入坑进而打开动态规划这一大门。背包问题分为多种,你可以先掌握最常见的主要是三类:01背包、完全背包、多重背包二.分析…

C语言--动态内存管理1

目录前言动态内存函数介绍mallocfreecallocrealloc常见的动态内存错误对NULL指针的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一块动态开辟内存的一部分对同一块动态内存多次释放动态开辟内存忘记释放(内存泄漏)对通讯…

TCP和UDP协议的区别?

是否面向连接: TCP 是面向连接的传输,UDP 是面向无连接的传输。 是否是可靠传输:TCP是可靠的传输服务,在传递数据之前,会有三次握手来建立连接;在数据传递时,有确认、窗口、重传、拥塞控制机制…

Linux编辑器-vim

一、vim简述1)vi/vim2)检查vim是否安装2)如何用vim打开文件3)vim的几种模式命令模式插入模式末行模式可视化模式二、vim的基本操作1)进入vim(命令行模式)2)[命令行模式]切换至[插入模式]3)[插入模式]切换至[命令行模式]4)[命令行模…

【C语言进阶】动态内存管理

真正的人生,只有在经过艰难卓绝的斗争之后才能实现。 ——塞涅卡 目录 一.为什么存在动态内存分配? 二.动态内存管理的函数 1.malloc函数 2.free函数 ​3.calloc函数 4.realloc函数 三.常见的动态内存错误 1.对N…

python编程:使用pyecharts绘制拟合曲线图

pyecharts库是python下实现的echarts图表绘制库,接下来,我们使用pyecharts来绘制一条曲线,来体验一下pyecharts的基本使用效果。 1、首先,我们要安装下pyecharts库,在pycharm终端输入安装命令: pip install…

pytorch实现深度神经网络与训练

目录 1. 随机梯度下降算法 2.优化器 3. 损失函数 3.1 均方误差损失 3.2 交叉熵损失 4.防止过拟合 4.1 过拟合的概念 4.2 防止过拟合的方法 5. 网络参数初始化 5.1 网络参数初始化方法 5.2 参数初始化方法应用实例 1.针对某一层的权重进行初始化 2.针对一个网络的权…

基于ESP32做低功耗墨水屏时钟

基于ESP32做低功耗墨水屏时钟电子墨水屏概述ESP32实验低功耗电子时钟功能描述接线开发实验结果电子墨水屏 概述 电子墨水是一种革新信息显示的新方法和技术。和传统纸差异是电子墨水在通电时改变颜色,并且可以显示变化的图象,像计算器或手机那样的显示。…

使用ArcGIS为科研论文制作正确、美观、详细的插图

科研论文中的插图,如果图中包含地理信息,那么首先需要在图中标明指北针、比例尺、图例,然后在此基础上再对作的图进一步的美化和修改。 来源:https://doi.org/10.1016/j.uclim.2022.101326 这种就是属于是最常见的研究区概况图&a…

(只需五步)注册谷歌账号详细步骤,解决“此电话号码无法验证”问题

目录 第一步:打开google浏览器 第二步:设置语言为英语(美国) 第三步:点击重新启动,重启浏览器 第四步:开始注册 第五步,成功登录google账号! 如果出现这样的原因&…

java多线程之线程安全(重点,难点)

线程安全1. 线程不安全的原因:1.1 抢占式执行1.2 多个线程修改同一个变量1.3 修改操作不是原子的锁(synchronized)1.一个锁对应一个锁对象.2.多个锁对应一个锁对象.2.多个锁对应多个锁对象.4. 找出代码错误5. 锁的另一种用法1.4 内存可见性解决内存可见性引发的线程安全问题(vo…

乐观锁和悲观锁 面试题

Mysql的乐观锁和悲观锁 实现方式加锁时机常见的调用方式优势不足适用场景乐观锁开发自定义更新数据的时候sql语句中进行version的判断高并发容易出现不一致的问题高并发读,少写悲观锁Mysql内置查询数据的开始select * for update保证一致性低并发互联网高并发场景极…

linux实验之shell编程基础

这世间,青山灼灼,星光杳杳,秋风渐渐,晚风慢慢 shell编程基础熟悉shell编程的有关机制,如标准流。学习Linux环境变量设置文件及其内容/etc/profile/etc/bashrc/etc/environment~/.profile~/.bashrc熟悉编程有关基础命令…

JVM类加载机制

文章目录定义类加载过程加载链接验证准备解析初始化类加载器双亲委派模型定义 Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这个过程被称为虚拟机的类…

有手就行 -- 搭建图床(PicGo+腾讯云)

🍳作者:贤蛋大眼萌,一名很普通但不想普通的程序媛\color{#FF0000}{贤蛋 大眼萌 ,一名很普通但不想普通的程序媛}贤蛋大眼萌,一名很普通但不想普通的程序媛🤳 🙊语录:多一些不为什么的…

2023最新最详细【接口测试总结】

序章 ​ 说起接口测试,网上有很多例子,但是当初做为新手的我来说,看了不不知道他们说的什么,觉得接口测试,好高大上。认为学会了接口测试就能屌丝逆袭,走上人生巅峰,迎娶白富美。因此学了点开发…