Java之线程篇三

news2024/11/9 10:24:30

​​​​​​​

目录

线程状态

观察线程的所有状态

线程状态及其描述

线程状态转换

代码示例1

代码示例2

线程安全 

概念

线程不安全的代码示例

线程不安全的原因

线程安全的代码示例-加锁

synchronized关键字

synchronized的特性

小结

形成死锁的四个必要条件

synchronized的使用示例

Java标准库中的线程安全类


线程状态
观察线程的所有状态

线程的状态是一个枚举类型 Thread.State

public class Demo11 {
    public static void main(String[] args) {
        for(Thread.State state:Thread.State.values())
            System.out.println(state);
    }
}

运行结果

线程状态及其描述

NEW:Thread对象已经有了,但是start方法还没调用;

RUNNABLE:就绪状态,线程已经在CPU上执行了/线程正在排队等待执行(即工作中或即将开始工作);

TERMINATED:Thread对象还在,但是内核中的下线程已经没了,即工作完成了;

TIMED_WARTING:阻塞状态,由于sleep这种固定时间的方式产生的阻塞;

WAITING:阻塞,由于wait这种不固定时间的方式产生的阻塞;

BLOCKED:阻塞,由于锁竞争导致的阻塞。

线程状态转换

代码示例1
public class Demo11 {
    public static void main(String[] args) {
        Object object=new Object();

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object){
                    while(true){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        },"t1");
        t1.start();

        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object){
                    System.out.println("hello");
                }
            }
        },"t2");
        t2.start();
    }
}

通过jconsole可以看到t1的状态是TIMED_WAITING,t2的状态是BLOCKED。

代码示例2
public class Demo11 {
    public static void main(String[] args) {
        Object object=new Object();

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object){
                    while(true){
                        try {
                            object.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        },"t1");
        t1.start();
    }
}

通过jconsole可以看到t1的状态是WAITING.

小结

BLOCKED表示等待获取锁,WAITING和TIMED_WAITING表示等待其它线程发来通知;

TIMED_WAITING线程在等待唤醒,但设置了时限;

WAITING线程在无限等待唤醒。

线程安全 
概念

如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

线程不安全的代码示例
public class Demo12 {
    private static int count=0;

    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count++;
            }
        });

        Thread t2=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count++;
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("count: "+count);
    }
}

运行结果

结果与预期结果不一致且差别很大,显然上述代码是线程不安全的。

线程不安全的原因

1.修改共享数据

上述代码涉及到多线程(两个及两个以上的线程)针对同一个变量count进行修改。

2.原子性

一条Java语句不一定是原子的,也不一定只是一条指令。

比如count++,其实是由三步操作组成的:

1.从内存中把数据读取到CPU;

2.对变量count进行++;

3.把数据写回到内存。

如果一个线程正在对一个变量操作,中途其它线程插入进来了,如果这个操作被打断,结果就可能是错误的。

3.可见性

可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到.

Java 内存模型 (JMM): Java虚拟机规范中定义了Java内存模型. 
目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果.

线程之间的共享变量存在 主内存 (Main Memory). 
每一个线程都有自己的 "工作内存" (Working Memory) . 
当线程要读取一个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据. 
当线程要修改一个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存. 

由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同一个共享变量的 "副本". 此时修改线程1 的工作内存中的值, 线程2 的工作内存不一定会及时变化.

4.指令重排序

如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,原来是按1->2->3的方式执行,优化后可能会按 1->3->2的方式执行,也是没问题,可以少跑一次前台。这种叫做指令重排序。
编译器对于指令重排序的前提是 "保持逻辑不发生变化". 这一点在单线程环境下比较容易判断, 但
是在多线程环境下就没那么容易了, 多线程的代码执行复杂程度更高, 编译器很难在编译阶段对代
码的执行效果进行预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价.

线程安全的代码示例-加锁
public class Demo12 {
    private static int count=0;

    public static void main(String[] args) throws InterruptedException {
        Object locker=new Object();

        Thread t1=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                synchronized (locker) {
                    count++;
                }
            }
        });

        Thread t2=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                synchronized (locker) {
                    count++;
                }
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("count: "+count);
    }
}

运行结果

synchronized关键字
synchronized的特性

1)互斥

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

synchronized用的锁是存在Java对象头里的。

synchronized 的底层是使用操作系统的 mutex lock 实现的 .

2)可重入

Java 中的 synchronized 可重入锁 ,

synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;

理解死锁

一个线程没有释放锁 , 然后又尝试再次加锁 .
// 第一次加锁, 加锁成功
lock();
// 第二次加锁, 锁已经被占用, 阻塞等待. 
lock();
按照之前对于锁的设定 , 第二次加锁的时候 , 就会阻塞等待 . 直到第一次的锁被释放 , 才能获取到第
二个锁 . 但是释放第一个锁也是由该线程来完成 , 结果这个线程已经躺平了 , 啥都不想干了 , 也就无
法进行解锁操作 . 这时候就会 死锁 .
代码示例
static class Counter {
    public int count = 0;
    synchronized void increase() {
        count++;
   }
    synchronized void increase2() {
        increase();
   }
}

在上面的代码中, increase 和 increase2 两个方法都加了 synchronized, 此处的 synchronized 都是针对 this 当前对象加锁的. 
在调用 increase2 的时候, 先加了一次锁, 执行到 increase 的时候, 又加了一次锁. (上个锁还没释
放, 相当于连续加两次锁),这个代码是完全没问题的. 因为 synchronized 是可重入锁.

小结

在可重入锁的内部, 包含了 "线程持有者" 和 "计数器" 两个信息. 
如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取到锁, 并让计数器自增. 
解锁的时候计数器递减为 0 的时候, 才真正释放锁. (才能被别的线程获取到)

形成死锁的四个必要条件

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

1.互斥条件(锁的基本特性)

   当一个线程持有一把锁之后,另一个线程也想要获取到锁,就要阻塞等待。

2.不可抢占条件(锁的基本特性)

   当锁已经被线程1拿到之后,线程2只能等线程1主动释放,不能强行抢过来。

3.请求与保持条件(代码结构)

   一个线程尝试获取多把锁,已经获取到部分数量的锁,但仍尝试获取其它线程已经占有的锁。

4.循环等待/环路等待(代码结构)

   等待的依赖关系,形成了环。

这四个条件同时满足时,系统中就可能发生死锁。

解决死锁的方法通常包括死锁预防、死锁避免、死锁检测和死锁恢复等策略。 

比如包括调整代码结构,避免循环等待;对锁进行编号,先加编号大的锁或编号小的锁。

synchronized的使用示例

 synchronized 本质上要修改指定对象的 "对象头". 从使用角度来看, synchronized 也势必要搭配一个具体的对象来使用.

1.直接修饰普通方法

public class SynchronizedDemo {
    public synchronized void methond() {
   }
}

2.直接修饰静态方法

public class SynchronizedDemo {
    public synchronized static void method() {
   }
}

3.修饰代码块

锁当前对象

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            
       }
   }
}

锁类对象

public class SynchronizedDemo {
    public void method() {
        synchronized (SynchronizedDemo.class) {
       }
   }
}

我们重点要理解,synchronized 锁的是什么. 两个线程竞争同一把锁, 才会产生阻塞等待. 
两个线程分别尝试获取两把不同的锁, 不会产生竞争.

Java标准库中的线程安全类
Java 标准库中很多都是线程不安全的 . 这些类可能会涉及到多线程修改共享数据 , 又没有任何加锁措施 .
ArrayList
LinkedList
HashMap
TreeMap
HashSet
TreeSet
StringBuilder
但是还有一些是线程安全的 . 使用了一些锁机制来控制 .
Vector ( 不推荐使用 )
HashTable ( 不推荐使用 )
ConcurrentHashMap
StringBuffer

我们可以看到,例如StringBuffer类的成员,有不少是加锁的:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java设计模式之命令模式介绍和案例示范

一、命令模式简介 命令模式&#xff08;Command Pattern&#xff09;是一种行为型设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使你可以用不同的请求对客户端进行参数化、对请求排队或记录日志&#xff0c;以及支持可撤销的操作。命令模式的核心思想是将发出请…

kvm 虚拟机命令行虚拟机操作、制作快照和恢复快照以及工作常用总结

文章目录 kvm 虚拟机命令行虚拟机操作、制作快照和恢复快照一、kvm 虚拟机命令行虚拟机操作(创建和删除)查看虚拟机virt-install创建一个虚拟机关闭虚拟机重启虚拟机销毁虚拟机 二、kvm 制作快照和恢复快照**创建快照**工作常见问题创建快照报错&#xff1a;&#xff1a;intern…

超详细、史上最全pytorch安装教程

一、anaconda安装 1.下载 Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirrorhttps://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 这里划到最下面选择5.3.1最新版&#xff1a; 2.下载完成后安装 点击next 点击 I agree 选择All Us…

ignav的INS的状态更新

ignav的代码 static void updstat(const insopt_t *opt,insstate_t *ins,const double dt,const double *x0,const double *P0,double *phi,double *P,double *x,double *Q) {opt->exprn?getprn(ins,opt,dt,Q): getQ(opt,dt,Q); // //phi 状态转移矩阵 &#xff0c;离散化…

算法学习攻略总结 : 入门至进阶,通关之路指南

❃博主首页 &#xff1a; <码到三十五> ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a; <搬的每块砖&#xff0c;皆为峰峦之基&#xff1b;公众号搜索(码到…

CircleProgressView 鸿蒙ArkTS自定义View实现圆形进度条

上篇的截图中除了一个上下的箭头&#xff0c;还有一个圆形进度条&#xff0c;今天我们来讲讲这个如何进行实现 我们看这个图形的构造&#xff0c;其实很简单&#xff1a;一个圆形图形&#xff0c;以及一个文本来显示进度 所以我们用一个层叠布局 绘制一个带颜色的圆形&#xff…

『功能项目』播放动画时禁止点击移动【40】

我们打开上一篇39GameObject对象池 - 第三职业的项目&#xff0c; 本章要做的事情是在第三职业播放续航攻击动画时禁止点击时触发的移动函数&#xff0c;换句话说是在播放攻击动画时禁止移动 修改脚本&#xff1a;PlayerRayClickNavigation.cs 运行项目 - 播放第三职业续航技能…

2-92 基于matlab的KPCA的TE过程的故障监测

基于matlab的KPCA的TE过程的故障监测&#xff0c;利用核主元分析法(KPCA)来进行故障检测的思想,将输入空间中复杂的非线性问题转化为特征空间中的线性问题&#xff0c;计算步骤&#xff1a;&#xff08;1&#xff09; 选择监控变量&#xff0c;收集正常工况下的各变量的样本&am…

【警告 C6031:返回值被忽略:scanf】

警告 C6031 返回值被忽略: “scanf”。 错误 C4996 scanf: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. #include <stdio.h> int max(int x, int y…

OKHttp实现原理分享

前言介绍 大约在2年半之前&#xff0c;就想写一篇关于OKHttp原理的文章&#xff0c;一来深入了解一下其原理&#xff0c;二来希望能在了解原理之后进行更好的使用。但是因为种种原因&#xff0c;一直无限往后推迟&#xff0c;最近因为我们情景智能半个月一次的分享轮到我了&…

手势识别&手势控制系统-OpenCV&Python(源码和教程)

项目特点 手部手势识别&#xff1a; 项目利用计算机视觉技术来识别手部的各种手势。这种技术可以应用于多种场景&#xff0c;比如人机交互、游戏控制、无障碍技术等。 自定义手势&#xff1a; 用户可以自定义手势&#xff0c;这意味着可以通过训练新的手势模式来扩展系统的功能…

基于vue框架的城市网约车管理系统v34td(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,司机,订单评价,完成订单,司机接单,打车订单 开题报告内容 基于Vue框架的城市网约车管理系统开题报告 一、研究背景与意义 1.1 研究背景 随着城市化进程的加速和互联网技术的飞速发展&#xff0c;网约车服务作为一种新兴的出行方…

基于java+SpringBoot+Vue的阿博图书馆管理系统设计与实现

开发语言:Java 数据库:MySQL技术:SpringBootMyBatis工具:IDEA/Ecilpse、Navicat、Maven 系统简介 阿博图书馆管理系统是一款基于Java、SpringBoot和Vue.js技术开发的信息化管理系统&#xff0c;旨在为图书馆提供一个高效、便捷的图书管理与借阅服务。系统通过B/S架构&#x…

FinalShell连接Linux服务器并解决反复输入密码问题

FinalShell是一款由国人开发的SSH客户端工具&#xff0c;它支持多平台&#xff0c;包括Windows、Mac OS X和Linux。FinalShell主要用于一体化服务器管理&#xff0c;它不仅是一个SSH客户端&#xff0c;还具备强大的开发和运维功能&#xff0c;能够充分满足开发和运维的需求。 本…

人脸匿名化初步研究:解决人脸隐私安全

1、人脸匿名化定义&#xff1a; 将人脸图像匿名化方法从图像语义修改、图像语义保持、视觉可恢复以及深度学习过程中的人脸隐私保护四个方面进行分类&#xff0c;将人脸视频匿名化方法从聚焦面部区域隐私的视频匿名化方法和面向生物特征隐私的视频匿名化方法两个方面进行分类 …

开源FormCreate低代码表单组件的配置项和事件的详解

在使用开源FormCreate低代码表单时&#xff0c;您可以通过各种 props 来定制表单的行为和外观。这些参数允许您控制表单的生成规则、配置选项、双向数据绑定等&#xff0c;为复杂的表单场景提供了强大的支持。 源码地址: Github | Gitee FormCreate组件Props 以下是常用的 pr…

【项目开发 | Python】基于“羊了个羊“风格的消除类小游戏

原创文章,不得转载。 目标:使用 Python 开发"羊了个羊"风格的消除类小游戏,合理运用 AIGC 工具提高开发效率;使用文生图工具实现图片设计等工作。 文章目录 项目背景项目介绍+项目展示游戏逻辑概述主界面游戏界面获胜界面失败界面附加功能项目细节项目测试测试样…

zabbix之钉钉告警

钉钉告警设置 我们可以将同一个运維组的人员加入到同一个钉钉工作群中&#xff0c;当有异常出现后&#xff0c;Zabbix 将告警信息发送到钉钉的群里面&#xff0c;此时&#xff0c;群内所有的运维人员都能在第一时间看到这则告警详细。 Zabbix 监控系统默认没有开箱即用…

JavaScript进阶day4

目录 1.深浅拷贝 1.1 浅拷贝 1.1.1 浅拷贝的认识 1.1.2 浅拷贝的小结 1.2 深拷贝 1.2.1 递归实现深拷贝 1.2.2 js类库lodash/cloneDeep实现深拷贝 1.2.3 JSON.stringify()实现深拷贝 2.异常处理 2.1 throw 抛异常 2.2 try /catch 捕获异常 2.3 debugger 3.处理thi…

嵌入式边缘计算:融合创新与未来展望

本文深入探讨了嵌入式边缘计算。首先解析了其概念&#xff0c;指出它是将计算和数据存储能力嵌入边缘设备以实现本地数据处理。阐述了其低延迟、高可靠性、节省带宽、隐私保护和高效节能等技术特点。接着介绍了关键技术&#xff0c;包括嵌入式系统设计、边缘计算架构、通信技术…