java多线程初探(一)构建线程安全类

java多线程——线程安全-举例 之所以会输出8、9、7,是因为进程内的所有线程都可以访问到该区域,当第一个线程已经获得9这个数了,还没来得及输出,下一个线程在这段时间的空隙获得了8这个值,故输出时会输出8、9的顺序值。线程安全性的一个重要方面是确保对共享资源的访问是原子的,即不可分割的,要么全部完成,要么全部不完成。如果忽略了线程安全性,可能会导致难以调试和重现的错误,这些错误可能只在特定的线程调度或系统负载下才会出现。因此,开发者需要仔细考虑并设计线程安全的代码,使用适当的同步机制,并对共享资源进行适当的访问控制。 阅读详情

这里先记录一些基础概念和说明

一、构建线程安全类方式

如果多个线程访问同一个可变的状态变量时,没有使用合适的同步,那就会出现不可预知的错误,修复方式如下:

1、不在线程之间共性这个变量(线程封闭)

2、将变量修改为不可变(final)

3、在访问变量时使用同步(同步代码块)


二、无状态对象

无状态对象一定是线程安全的,无状态对象定义如下:

/**
 * 有状态bean:包含实例变量对象,如这里的user
 */
public class StatefulBean {
    public int state;
    // 由于多线程环境下,user是引用对象,是非线程安全的
    public User user;
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}

/**
 * 无状态bean:没有实例变量对象。
 */
public class StatelessBeanService {
    public int state;
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
    }
}

延伸到spring的bean就是,只包含简单字段(没有类类型的属性)的实体是线程安全的无状态对象,包含类类型属性的实体则相反(如在mybatis中可能会在Article实体内添加User user这样的类类型的属性,以便一次查询到作者)

在spring中默认mvc各层都是单例的,因为service和dao层一般都是无状态的(没有类类型属性,以及类类型属性的get,set方法)。这样多个线程使用同一个实例也是安全的。


三、竞态条件

竞态条件:当某个计算正确性,取决于多个线程的交替执行时序所引发,例如:

private int count=0;
++count;
System.out.println(count);
++count不是一个原子操作,是三个独立的操作:读取count值,给值加1,将结果写入count变量;是读取-修改-写入的操作。

多线程下在线程A写入时,线程B可能对count做了第二次的修改,结果就会偏差1,这种操作方式引发的现象就是竞态条件。

同理先检查-后执行(延迟初始化)的操作也会引发竞态条件,如A条件下执行a方法,在检查后和执行a方法前,其他线程操作将A条件改为了B,这时仍执行a方法得到的结果就可能是不正确的。常用的if else,判断条件如果是共享变量就可能引起竞态条件


四、复合操作

复合操作:如上例,将读取-修改-写入的操作,和先检查-后执行的操作,统称为复合操作:包含了一组必须已原子方式执行才能保证线程安全的操作。


五、综上简单案例如下:

package gcc.thread.test;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * Created by gcc on 2018/3/29.
 * thread.start(),内部会调用start0(),而这个方法是本地(native)方法,能启动线程,同时也创建了一个线程。
 * thread.run(),只是调用一个java方法,不是真正启动多线程。
 */
public class Test1 implements Runnable {

    private int count=0;
//    private final AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) {
        Test1 test1 = new Test1();
        Thread thread1 = new Thread(test1);
        Thread thread2 = new Thread(test1);
        Thread thread3 = new Thread(test1);
        thread1.start();
        thread2.start();
        thread3.start();

    }

    public void run() {
        for (int i = 0; i <30 ; i++) {
// System.out.println("结果:"+Thread.currentThread().getName()+"--:"+count.incrementAndGet());
            System.out.println("结果:"+Thread.currentThread().getName()+"---:"+count++);
            //线程休眠时间0.1-0.5秒
            try {
                Thread.sleep(100*(new Random().nextInt(5)+1));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

如上,Atomic开头的类是java自带的原子操作类,可以将类似count++的复合操作改为同作用的原子操作。案例默认的是复合操作,会引发的错误结果。如下:

结果:Thread-1---:11
结果:Thread-0---:12
结果:Thread-2---:12
结果:Thread-1---:13
结果:Thread-0---:14
结果:Thread-2---:15


六、使用synchronized方法,如下:

    public synchronized void run() {
        for (int i = 0; i <50 ; i++) {
            ++count;
            System.out.println("结果:"+Thread.currentThread().getName()+"----"+count);
            //线程休眠时间0.1-0.3秒
            try {
                Thread.sleep(10*(new Random().nextInt(3)+1));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

其它代码与(五)相同,这里增加synchronized,这是java内置锁,加在方法上则在同一时间只有一个线程能执行这个方法,解决了多线程不安全问题,但效率大大降低,不推荐使用,测试结果如下:

结果:Thread-0----47
结果:Thread-0----48
结果:Thread-0----49
结果:Thread-0----50
结果:Thread-2----51
结果:Thread-2----52
结果:Thread-2----53


七、使用synchronized代码块,如下:

    public void run() {
        for (int i = 0; i <50 ; i++) {
            synchronized (this){
                ++count;
                System.out.println("结果:"+Thread.currentThread().getName()+"----"+count);
            }
            //线程休眠时间0.1-0.3秒
            try {
                Thread.sleep(10*(new Random().nextInt(3)+1));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

其它代码与(五)相同,这里增加synchronized(this){},添加代码块,效率高于synchronized方法,通过平衡性能、安全和编码的简单性来确定同步代码块的大小,相对于(六)此方式更优,测试结果如下:

结果:Thread-0----1
结果:Thread-2----2
结果:Thread-1----3
结果:Thread-1----4
结果:Thread-0----5
结果:Thread-2----6
结果:Thread-0----7

注意:

同步代码块防止了一个对象在被使用时,同时被另一个线程修改。

还有要知道的一点是,同步机制保证了可见性。

可见性:读操作和写操作在两个线程时,不能保证读操作可以适时的看到其他线程写入的值,为了保证写入可见,使用同步机制。



Java实现HTTP协议断点续传的多线程与线程安全实践 本文还有配套的精品资源,点击获取 简介:Java多线程和线程安全是构建高效并发应用程序的关键,本实践将深入探讨如何在Java中实现基于HTTP协议的断点续传功能,该功能在大文件下载和网络传输中非常实用。我们将学习线程的基础知识,包括线程的创建与管理、线程同步和HTTP协议的断点续传机制,并通过实践提升对这些概念的理解和应用能力。 1. Java中线程的概念与管理 ... 阅读详情

相关推荐

LabVIEW QMH模板实战:从创建到错误处理的完整指南

本文提供LabVIEW QMH模板从创建到错误处理的完整实战指南。详细解析了队列消息处理器(QMH)作为LabVIEW核心设计模式如何实现任务解耦与异步通信,涵盖事件处理循环、消息处理循环的搭建、用户事件创建、队列引用管理、消息优先级控制以及健壮的错误处理机制,帮助开发者构建稳定、可维护的复杂应用程序。

white的博客 725

JAVA多线程(二)构建线程安全的类

构建线程安全的类 volatile 只保证数据可见性,不保证数据同步。也就是说,JVM只保证使用了volatile的数据变更后对所有线程暴露最新值,并不会对线程内部缓存数据的操作多限制。多线程同时变更某个共享的volatile数据并不会产生正确结果。 在满足以下条件时,可使用volatile: 1.写入变量时并不依赖变量当前值...

weixin_33913377的博客 140

别再盲调PID了!手把手教你用VOFA+实时可视化电机速度曲线(附STM32F4源码)

本文详细介绍了如何使用VOFA+工具实现PID参数的可视化调试,帮助开发者从盲调转向精准调优。通过实时可视化电机速度曲线,结合STM32F4源码示例,展示了如何高效调试Kp、Ki、Kd参数,解决响应慢、超调等常见问题,大幅提升电机控制系统的性能。

weixin_28366475的博客 128

设计线程安全的类

为了防止多个线程在并发访问同一个对象时产生的相互干扰,这些对象应该要么是线程安全的对象,要么是事实不可变的对象,或者由锁来保护的对象。如果分配并填充了一个HashMap对象,那么就相当于创建了多个对象:HashMap对象,在HashMap对象中包含的多个对象,以及在Map. Entry中可能包含的内部对象。所有权意味着控制权。在线程安全的程序中,虽然可以将程序的所有状态都保存在公有的静态域中,但与那些将状态封装起来的程序相比,这些程序的线程安全性更难以得到验证,并且在修改时也更难以始终确保其线程安全性。

2301_77783312的博客 499

Unity中USD场景导入与工业级协作实战指南

USD(Universal Scene Description)是一种面向复杂三维场景的结构化数据模型,其核心在于分层描述、变体管理与非破坏性组合,本质是支持多团队并行协作的场景协议。它不同于传统模型格式,强调运行时可编辑性与版本可控性,技术价值体现在跨软件资产一致性、零拷贝动态叠加及内存中实时合成。典型应用场景包括建筑BIM实时漫游、汽车多配置仿真、影视级灯光材质协同等工业管线。在Unity中,USD已深度集成于URP/HDRP管线,通过原生USD Importer、Runtime与Exporter三组件

weixin_32073537的博客 301

SQLSTATE58030

在优化一条SQL的时候遇到了执行错误 gtlions =# select t1.telnumber,t2.ua,t2.url,t1.apply_name,t2.apply_name from tb1 t1 gtlions -# left outer join tb2 t2 on t1.sid = t2.ipsid gtlions-# where t1.sum_date=20141128 a

Gtlions Tracker 3660

多线程环境下Java如何实现线程安全

线程安全前言多线程环境下面临的风险分析造成线程安全问题多线程不安全的原因解决线程安全问题的方法synchronized关键字synchronized的具体操作synchronized底层实现原理 前言 本篇博客将根据现有知识对Java多线程做以小结,以下博客仅作为个人学习过程的小结,如能对各位博友有所帮助不胜荣幸。 本篇博客将简单介绍线程安全的概念,以及实习线程安全的几种方法,重点叙述了几种常用的加锁操作,后期随学习深入还会补充修改。 多线程环境下面临的风险 之前介绍到,多线程的引入很大的提高了一个任务执行

相鱼南故 1432

位置点位到达判断FC(Easy+Codesys+博途平台)

IN rActPositon REAL 轴当前实际位置 1IN arAbsPosition REAL[10] 各工作点位坐标 2IN rDeadZone REAL 位置达到判断域值 范围 3OUT abInAbsPos BOOL[10] 到达各绝对定位点位标志 位置到达输出 4VAR i INT 循环计数 5VAR rErr REAL 位置偏差 6。

RXXW_Dor的博客 536

构建 线程安全 类

 设计前的准备:确定对象状态由哪些变量构成确定限制变量的不变约束定制访问对象状态的策略设计时应当遵循的规则:若操作过程可能出现非法状态转换,则该操作必须是原子的 若存在某些非法状态,则必须封装该状态的状态变量,以免客户将对象置于非法状态 若类的状态变量独立,并且委托给了线程安全的对象,那么该类也是线程安全的 若不变约束涉及多个

primer2006的专栏 621

搭建问答系统

数据描述 import docx import jieba from gensim.models import Word2Vec import os import numpy as np import pickle as pk import warnings from copy import deepcopy warnings.filterwarnings("ignore") from mu...

微信号:RunsenLiu 1840

设备接入与APP(应用程序)接入华为云iotDA平台的详细操作步骤及获取方式

设备及APP(应用程序) 登录 IoTDA 平台所需的 入场与操作清单内容,提供逐项详细操作步骤和获取方式,并穿插贴士便于理解与实际操作落地。内容基于 华为云 IoTDA 控制台 和 IAM 控制台 的实际使用路径整理。

hy行者勇哥的博客 3059

java创建线程安全的类

如果一个对象想要被多个线程安全的并发访问,那么这个对象必须是或线程安全的或事实不可变的或由锁来保护的。 1.java监视器模式 大多数对象都是组合对象。当从头开始构建一个类,或者将多个非线程安全的类组合为一个类时,java监视器模式是非常有用的。 遵循java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护。比如创建姓名类,类里面只有name一个成员变量,则将na...

weixin_30814223的博客 341

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Java集合中那些类是线程安全的

线程安全类 在集合框架中,有些类是线程安全的,这些都是jdk1.1中的出现的。在jdk1.2之后,就出现许许多多非线程安全的类。 下面是这些线程安全的同步的类: vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。 statck:堆栈类,先进后出 hashtable:就比

Mexican的博客 10万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Java多线程之线程安全问题

Java多线程中的线程安全问题, 解决方法, 线程安全和不安全的标准类.

trong_ 的烂笔头 7225

java线程安全类

java线程安全类介绍

heat3_6的博客 1420

Java 多线程编程中如何保证线程安全?

线程安全是指在多线程环境下,对共享资源的访问和操作不会导致数据不一致、程序崩溃或其他不可预期的结果。也就是说,多个线程同时访问和修改共享资源时,程序的行为仍然符合预期,如同单线程环境下执行一样。例如,一个计数器类在单线程环境下可以正常工作,但在多线程环境中,如果多个线程同时对计数器进行自增操作,可能会出现计数不准确的问题,而线程安全的计数器类则能避免这种情况。这些类使用 CAS(Compare-And-Swap)算法实现,能够在不使用锁的情况下保证对共享变量的原子操作。实现线程安全的方法:使用同步机制。

qq_45626589的博客 1382

多线程——线程安全的集合类

介绍了这么多关于多线程相关的知识后,在我们 Java 中哪些集合类在使用的时候是线程安全的呢?其实,我们了解的集合类大部分都是线程不安全的,像 HashMap、ArrayList……,那么本篇文章就来介绍一下哪些集合类是线程安全的,它们又是如何保证线程安全的。

HKJ_numb1的博客 2120

JAVA多线程之初探List线程安全的三种实现方式

初探List线程安全的三种实现方式 先演示一下List线程不安全的情况 package JUC.unsafe; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; /** * ArrayList线程...

千里之行,始于足下 2583

多线程:Vector是线程安全的吗

线程安全,在java的多并发编程中是重要概念,意思是,多个线程同时操作一个对象,在各种不同情况下,都不会造成不同的后果。 一个经典问题,Vector到底是不是线程安全的?   很多人都会回答,是,vector是线程安全的。 诚然,不止是很多IT教材上是这么写的,就连JDK的作者写的注释也是这么说的。如下图(图为 JDK中Vector的源代码):注释中,红框标记的部分写着:如果你不需要线程安...

五山口老法师 3787

4.Java加强-多线程的实现-Thread类-线程安全

【代码】4.Java加强-多线程的实现-Thread类-线程安全。

weixin_45286894的博客 939

多线程的创建、Thread类、线程安全、同步、通信

多线程的创建 方式一:继承Thread类 方式二:实现Runnable接口 方式三:JDK 5.0新增:实现Callable接口 Thread的常用方法 线程安全 线程安全问题是什么、发生的原因 线程安全问题案例模拟 线程同步 同步思想概述 方式一:同步代码块 方式二:同步方法 方式三:Lock锁 线程通信【了解】 线程池【重点】 线程池概述 线程池实现的API、参数说明 线程池处理Runnable任务 线程池处理Callable任务 Executors工具类实现线程池 定时器 并发、并行 线程的生命周期

weixin_52270382的博客 3212

最新Java多线程之线程安全问题_java 线程安全

在Java中最常用的加锁操作就是使用关键字进行加锁.

2401_84281748的博客 1183

Java多线程编程中,如何确保线程安全与资源高效利用?

因此,在实际开发中,我们需要不断地学习和探索新的技术和方法,以提高我们的编程能力和应对复杂问题的能力。可以修饰方法或代码块,当一个线程进入同步代码块或方法时,其他线程将无法进入该同步代码块或方法,直到当前线程退出同步代码块或方法。线程池维护了一定数量的线程,当需要执行新任务时,线程池会复用已有的线程,而不是创建一个新线程。例如,当线程尝试获取锁失败时,可以等待一段时间再重试,或者主动放弃CPU的使用权,让其他线程有机会执行。这些集合类内部实现了复杂的同步机制,以确保在多线程环境下对集合的访问是线程安全的。

AntonioMargery的博客 1964

[线程]JUC中常见的类 及 集合类在多线程下的线程安全问题

下面介绍的内容是面试中常考, 但是实际开发中用不到的知识。

m0_73992740的博客 1092
上一篇: java编程思想随笔(四)/违例简单使用
下一篇: java8类库简单了解(一)Object
gcc_front
博客等级 码龄9年 16粉丝 98原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值