Java多线程~谈谈自己对于synchronized、volatile关键字的理解,线程的状态转换以及创建线程的方式

2023-05-16

目录

创建线程的方式

继承Thread类

实现Runnable接口 

实现Callable接口

线程的状态转换

volatile关键字

synchronized关键字

synchronized关键字的特性

synchronized原理


创建线程的方式

在java中,创建线程就是new一个Thread,有三种方式,第一种是继承Thread类、第二种是实现Runnable接口、第三种是实现Callable接口

继承Thread类

继承Thread类,重写run方法(run方法即定义要执行的任务代码)

public class TestDemo5 {
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("继承Thread类方式创建线程");
        }
    }
    public static void main(String[] args) {
        TestDemo5.MyThread thread = new TestDemo5().new MyThread();
        thread.start();
    }
}

实现Runnable接口 

首先实现Runnable接口,再重写run方法,之后创建线程时传入创建的runnable对象

public class TestDemo5 {
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("实现Runnable接口方式创建线程");
        }
    }

    public static void main(String[] args) {
        TestDemo5.MyRunnable myRunnable = new TestDemo5().new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

实现Callable接口

public class TestDemo6 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        int ret = futureTask.get();
        System.out.println(ret);
    }
}

线程的状态转换

线程的状态有六种:NEW(初始状态)、RUNNABLE(可运行态)、WAITING(等待状态)、TIMED_WAITING(超时等待状态)、BLOCKED(阻塞状态)、TERMINATED(终止状态) 

创建一个线程,线程就进入了NEW状态,当调用start方法时,进入RUNNABLE状态,如果调用了wait、join等方法时就进入WAITING、TIMED_WAITING状态,当多个线程竞争同一把锁时,竞争失败的线程进入BLOCKING状态,线程执行完成后,进入TERMINATED状态。

volatile关键字

使用volatile关键字是为了保证线程安全的,当某个线程对共享变量只是读操作而没有写操作时,使用volatile就可以保证线程安全。volatile的作用是保证可见性和有序性,因为读操作本来就具有原子性,因此仅使用volatile就可以保证线程安全。

· volatile能保证内存的可见性

使多个线程对同一个共享变量操作具有可见性

代码在写入volatile修饰的变量时,先改变工作内存中volatile变量的副本值,再将改变后的副本的值从工作内存刷新到主内存。

代码在读取volatile修饰的变量时,先从主内存中读取volatile变量的最新值到线程的工作内存中,再从工作内存中读取volatile变量的副本。

· volatile可以禁止代码指令重排序,建立内存屏障

例如:小李对自己的安排是:

1、打篮球;

2、吃饭;

3、取快递

如果不加volatile,小李的执行顺序可能是2、1、3或3、2、1等等,但是加上volatile就是1、2、3,这就是禁止指令重排序的作用。

· 但是volatile不能保证原子性

synchronized关键字

多个线程使用同一个对象进行加锁(基于对象头加锁),可以实现线程间的同步互斥。简单地说,它的原理就是基于对象头加锁,满足原子性、可见性、有序性

synchronized关键字的特性

①互斥:

synchronized会起到互斥效果,某个线程执行到某个对象的synchronized中时,如果其他线程也执行到同一个的对象的synchronized就会阻塞等待,进入synchronized代码块相当于加锁,退出synchronized修饰的代码块相当于释放锁。synchronized的锁是存在Java对象头中的。

互斥可以满足原子性,某个线程执行同一个对象加锁的同步代码,排斥另一个线程加锁,满足最小执行单位。

②刷新内存:

synchronized结束释放锁,会把工作内存中的数据刷新到主存,其他线程申请锁,获取的始终是最新数据(满足可见性)。synchronized的工作过程:获得互斥锁->从主内存中拷贝变量的最新副本到工作内存->执行代码->将更改后的共享变量的值刷新到主内存->释放互斥锁

③有序性:

某个线程执行一段同步代码,不管如何重排序,期间都不可能有其他线程指令的执行,这样多个线程执行同步代码,就是满足一定的顺序的

④可重入:

同一个线程可以多次申请同一个对象锁

synchronized原理

结合锁策略,谈synchronized锁的基本特点:

①开始时是乐观锁,如果锁冲突频繁,就转换为悲观锁

②开始时是轻量级锁实现,如果锁被持有的时间较长,就会转为重量级锁

③实现轻量级锁时大概率用到自旋锁策略

④是一种不公平锁

⑤是一种可重入锁

⑥不是读写锁

加锁工作过程:

JVM将synchronized锁分为无锁、偏向锁、轻量级锁、重量级锁状态,会根据情况依次进行升级。

①偏向锁:第一个加锁的线程,优先进入偏向锁状态,偏向锁并不是真的“加锁”,只是给对象头做了一个“偏向锁的标记”,记录这个锁属于哪个线程。如果后续没有其他线程来竞争该锁,就不用进行其他同步操作了,如果后续有其他线程竞争该锁,就取消原来的偏向锁状态,进入一般的轻量级锁。

②轻量级锁:随着其他线程进入竞争,偏向锁的状态被消除,进入轻量级锁状态(自适应的自旋锁)。这里的轻量级锁是通过CAS实现的,通过CAS检查并更新一块内存,如果更新成功表示加锁成功,如果更新失败,则认为锁被占用,继续自旋式的等待(并不放弃CPU).自旋操作是一直让CPU进行空转,比较浪费CPU资源。

③重量级锁:当线程冲突进一步激烈,自旋锁不能快速获取到锁状态,就会升级为重量级锁,重量级锁基于系统级别的mutex lock实现,代价非常大,是系统加锁

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java多线程~谈谈自己对于synchronized、volatile关键字的理解,线程的状态转换以及创建线程的方式 的相关文章

  • C语言经典题:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?

    include lt stdio h gt 通过for循环将变量i j k的取值锁定在1 xff0c 2 xff0c 3 xff0c 4之间 int main int num 61 0 int i 61 0 j 61 0 k 61 0 fo
  • 单词逆序输出(c语言)

    int main int l j 61 0 int tmp 61 0 存储输入字符串的数组 char arr 100 61 34 i love beijing 34 用来存储输出字符串的数组 char arr2 100 输入字符串 gets
  • 进程虚拟地址空间

    关键词 xff1a 进程虚拟地址空间 xff0c 进程描述符 xff0c 页表 xff0c 分段式 xff0c 段页式 在进入正式的内容之前 xff0c 我们先了解一个重要的概念 进程描述符PCB 在Linux操作系统中 xff0c 描述进
  • 简单了解函数调用过程

    什么是栈帧 C C 43 43 程序的基本组成部分是函数 当程序在运行时 xff0c 每个函数每次调用都会在调用栈上维护一个独立的栈帧 xff0c 栈帧中维持着函数所需的各种信息 栈帧也叫过程活动记录 xff0c 是编译器用来实现过程 函数
  • 错题汇总1

    1 以下程序的运行结果是 xff08 xff09 int main void printf 34 s 5 3s n 34 34 computer 34 34 computer 34 return 0 A computer puter B c
  • 使用C/C++制作信息管理系统(demo)

    要求 xff1a 在windows环境下使用Vistual studio以C C 43 43 语言编译一个具有基础框架的客户信息管理系统 必须使用到封装 继承 map容器 SQL数据库技术 我 是 分 割 线 未经过UI处理的基础系统功能效
  • 错题汇总2

    1 下列程序的打印结果是 char p1 15 61 34 abcd 34 p2 61 34 ABCD 34 str 50 61 34 xyz 34 strcpy str 43 2 strcat p1 43 2 p2 43 1 printf
  • C++之继承初识(不包含虚拟继承)

    C 43 43 是一种面向对象的语言 xff0c 而面向对象 xff0c 有着三大特征 封装 xff0c 继承 xff0c 多态 关于封装 xff0c 在我的其它博客中已经有过简单的介绍了 这里我将简单叙述一下面向对象的三大特征之二 继承
  • C++之虚拟继承与继承的小总结

    本来是想将虚拟继承的部分写在上一篇的 xff0c 但是虚拟继承的分析实在有些复杂 xff0c 为了方便我自己回顾 xff0c 就干脆单写一篇吧 我们之前说过了 xff0c 虚拟继承可以解决菱形继承的二义性以及数据冗余的问题 xff0c 实际
  • C++之初识多态(Visual Studio 2019)

    此文章关于多态的代码全部是使用Visua Studio2019 x86 实现的 xff0c C 43 43 多态在不同编译器中的实现细节可能不同 xff0c 所以部分情况下相同代码运行结果可能不同 xff0c 在此声明 目录 多态的概念 多
  • C工程与寄存器封装(lv9-day13)

    文章目录 C工程与寄存器封装1 C语言工程简介2 启动文件3 C语言实现LED闪烁3 1 C语言与汇编分别是怎么操作寄存器的3 2 用C语言实现LED闪烁 4 寄存器的封装4 1 第一种封装方式 xff08 宏定义 xff09 4 2 第二
  • C++中auto的用法

    目录 1 auto初始化 1 1auto还可以与指针 xff0c 引用以及const进行组合 xff0c 但是在不同场景下会有相对应的推导规则 1 1 1指针和引用 1 1 2const 2 增强for循环 3 auto的限制 C 43 4
  • 自定义函数实现strcat函数功能

    strcat函数定义 字符串追加 连接函数 它的功能就是在一个字符串后面追加上另外一个字符串 char strcat char destination const char source 特点 xff08 与strcpy类似 xff09 x
  • STM32实现智能加湿

    开发前的准备需要如下的材料 xff1a 雾化模块1个 STM32F103开发板一个 风扇驱动模块1个 xff08 可用继电器代替 xff09 我们采用的继电器是低电平触发的所以我们在使用的时候只用给它一个低电平的信号就可以控制它了 USB转
  • Qt项目实战:愤怒的小鸟(联机版)

    前言 本文章会详细介绍难点的内容 xff0c 不附带全部源码 xff0c 会将关键代码进行展示 因为只有截图 xff0c 这里在每一个动作和界面都添加了音效与BGM 同时附加了CG展示 素材和音效全部放在下面了 xff0c 需要可自行提取
  • Ubuntu中安装openCV时Cmake问题解决

    1 执行Cmake的语句指令 sudo cmake D CMAKE BUILD TYPE 61 Release D CMAKE INSTALL PREFIX 61 usr local 2 当执行完上述指令后遇见以下问题解决策略 问题1 xf
  • 使用 RGB-D 相机(Astra)实现 YOLO v3 实时目标检测

    设备和环境 xff1a 奥比中光RGB D相机 xff08 Astra xff09 xff1b Ubuntu16 04 首先 xff0c 先将自己的RGB D相机的环境与依赖构建好 xff0c 然后进行以下步骤构建darknet ros 1
  • Arduion应用U8g2库实现字符滚动效果

    由于U8g2库中没有可以位移的函数 xff0c 所以简单编写了一个可以实现字符滚动的代码 主要是为了记录一下自己学习Arduion的过程 算是一个记事本吧 xff01 当然如果你对于这方面有所需求 xff0c 可以拿去使用 主要是利用显示器
  • C语言老鼠走迷宫(单路径)算法详细讲解

    最近在学习C语言的一些经典算法 xff0c 其中遇到了一点困难 xff0c 导致卡进度了 琢磨了很久 xff0c 在绘制流程图时 xff0c 突然灵感大开理解了 xff0c 老鼠走迷宫算法的奇妙 所以写了这个 xff0c 一来是方便以后右和
  • 关于二分搜索法条件判断的研究

    最近在写二分搜索法时 xff0c 发现了几个现象 xff0c 在不同的条件设定之下 xff0c 系统会出现几种bug xff1a 1 当搜索的值 lt 最小值 时系统都会一直在循环内执行 2 当搜索的值 gt 最大值 时系统都会一直在循环内

随机推荐

  • C++ 库函数<string>示例

    最近在学C 43 43 自己整理了一部分C 43 43 库函数 lt string gt 的一些 函数 主要都是C 43 43 的 想到以后可能会用到 所以打算记录一下 方便自己也方便了大家 在啃这些函数的时候有很多借鉴之处 xff0c 在
  • 两台电脑间的串口通信

    目录 一 准备工作 二 实验过程 三 实验结果 一 准备工作 1 两台笔记本电脑 2 2个usb转串口模块 3 杜邦线若干 4 秒表 二 实验过程 两个串口线分别连接两台电脑 连线方式 xff1a 3V3 3V3 xff0c GND GND
  • c++ primer plus学习笔记(1)——基础知识

    本人还有一星期要开始期末考试了 xff0c 复习c 43 43 时顺便挖个坑 xff0c 之后会详细更新 目录 1 初识源代码 1 1 c 43 43 程序的产生 1 2 代码例 1 3 标记 空白 2 简单数据类型 2 1 变量名 2 2
  • C语言:sizeof和strlen计算字符串大小

    大家清楚 sizeof 和 strlen 的区别吗 xff1f sizeof是运算符 xff0c 确定的是字符所占空间大小 xff0c 参数可以有数组 指针 类型 对象 函数等 strlen是C语言的标准库函数 xff0c 确定是字符串的大
  • Python--爬虫--requests进阶,cookie/session模拟登录

    目录 一 原理 二 实际操作 三 结果 四 问题与总结 一 原理 以下内容为使用requests库发送请求 xff0c 使用cookie session模拟登录 xff08 并且登录时只需输入账号与密码 xff09 我们在使用搜索引擎访问网
  • linux系统移植U-boot与kernel的搭载流程(交互模式下)

    Linux系统移植四大部分 xff1a 搭建交叉开发环境 bootloader的选择和移植 本文选用bootloader下的U boot uImage的配置 编译与移植 根文件系统的制作 全部已完成 xff0c 本文只讲解 如何搭载这些东西
  • FreeRTOS任务创建、删除| FreeRTOS三

    目录 一 FreeRTOS任务创建与删除有关函数 1 1 创建 删除任务的API函数 1 1 1 动态创建任务 1 1 2 静态创建任务 1 1 3 删除任务 二 FreeRTOS任务创建与删除 xff08 动态方法 xff09 2 1 实
  • (学习)基于STM32的串口通信打印数据(HAL库+CubeMX)

    当我们在进行开发的时候 xff0c 通常需要将一些参数进行调整来达到效果 xff0c 这个时候就需要将单片机上的信息通过串口通信传送到PC机上来直观显示 一 基本的专有名词和原理介绍 USART xff1a 只能异步通信 USART xff
  • ROS通信——C++实现

    一 普通话题通信 创建功能包 xff1a catkin create pkg package roscpp rospy std msgs 创建发布者 xff1a include 34 ros ros h 34 include 34 std
  • ROS使用Python编写的步骤

    第一步 xff1a 和C 43 43 编写一样 xff0c 配置好工作空间 第二步 xff1a 在功能包下面建立一个scripts文件夹 第三步 xff1a 在scripts文件里面建立一个 py文件 第四步 编写python文件 注意 x
  • window的QT作为TCP客户端,ubuntu的python作为TCP服务端

    客户端 pro文件加入 QT 43 61 network mainwindow h ifndef MAINWINDOW H define MAINWINDOW H include lt QMainWindow gt include lt Q
  • RoboCom机器人大赛使用yolov5抽取20个随机图片进行人群识别

    目录 1 原理 2 思维流程 2 1 进行yolov5的环境搭建 2 1 1 在Linux的ubuntu环境anaconda的安装 2 1 2 Vscode的安装和配置 2 1 3 Github上面yolov5文件的下载 2 1 4 使用A
  • C语言实现字符串逆序、倒置字符串(字符串逆序问题的升级)

    一 字符串逆序 问题描述 xff1a 输入一个字符串str xff0c 将其内容颠倒过来 xff0c 并输出 数据范围0 lt len str lt 10000 输入描述 xff1a 输入一个字符串 xff0c 可以有空格 输出描述 xff
  • C语言实现“井字棋”游戏(三子棋)人机对弈

    井字棋游戏 xff1a 即三子棋 xff0c 英文名叫Tic Tac Tic xff0c 是一种在3 3格子上进行的连珠游戏 xff0c 和五子棋比较类似 xff0c 由于棋盘一般不画边线框 xff0c 格线排成井字故得名 题目分析 xff
  • C语言基础编程练习(精选例题+题解)

    目录 1 求最大公约数和最小公倍数 2 打印图形 3 质数因子 4 数字排序 5 十进制数转换为八进制数 xff08 进制转换 xff09 6 寻找完数 1 求最大公约数和最小公倍数 题目描述 xff1a 输入两个正整数m和n xff0c
  • C语言——实现“扫雷”小游戏(简易版)

    扫雷游戏想必我们都有玩过 xff0c 那么今天就用C语言来简单实现 扫雷 小游戏 xff01 目录 一 游戏规则 二 基本思路介绍 三 各功能代码实现 1 创建用户交互界面 2 初始化棋盘函数 3 埋雷函数 4 显示棋盘函数 5 排雷函数
  • linux驱动开发关于内核模块、设备模型、内存管理、模块参数介绍

    内核功能 多任务管理 内存管理 文件管理 网络协议栈 设备管理 设备驱动 我是分割线 一 内核开发特点 内核代码的运行时机 启动目标硬件应用程序调用系统调用硬件中断调用中断处理Linux内核和驱动间的接口 kABI 不稳定内核升级后 接口可
  • Java实现单链表及其基本操作

    目录 什么是单链表 xff1f 带头结点的单链表 不带头结点的单链表 模拟实现不带头结点的单链表 定义结点类 初始化 头插法创建单链表 尾插法创建单链表 打印单链表 单链表的查找 获取单链表的长度 按位置寻找前驱结点 单链表的插入 修改指定
  • 优化后的快速排序(详解)

    目录 快速排序 三数取中值分割法获得枢纽元 快速排序的主例程 直接插入排序代码 快速排序完整代码 快速排序 快速排序是实践中的一种快速的排序算法 xff0c 它的平均运行时间是O N log N xff0c 该算法之所以特别快 xff0c
  • Java多线程~谈谈自己对于synchronized、volatile关键字的理解,线程的状态转换以及创建线程的方式

    目录 创建线程的方式 继承Thread类 实现Runnable接口 实现Callable接口 线程的状态转换 volatile关键字 synchronized关键字 synchronized关键字的特性 synchronized原理 创建线