java 线程安全问题_java线程安全问题原因及解决办法

1.为什么会出现线程安全问题

计算机系统资源分配的单位为进程,同一个进程中允许多个线程并发执行,并且多个线程会共享进程范围内的资源:例如内存地址。当多个线程并发访问同一个内存地址并且内存地址保存的值是可变的时候可能会发生线程安全问题,因此需要内存数据共享机制来保证线程安全问题。

对应到java服务来说,在虚拟中的共享内存地址是java的堆内存,比如以下程序中线程安全问题:

public class ThreadUnsafeDemo {

private static final ExecutorService EXECUTOR_SERVICE;

static {

EXECUTOR_SERVICE = new ThreadPoolExecutor(100, 100, 1000 * 10,

TimeUnit.SECONDS, new LinkedBlockingQueue(100), new ThreadFactory() {

private AtomicLong atomicLong = new AtomicLong(1);

@Override

public Thread newThread(Runnable r) {

return new Thread(r, "Thread-Safe-Thread-" + atomicLong.getAndIncrement());

}

});

}

public static void main(String[] args) throws Exception {

Map params = new HashMap<>();

List futureList = new ArrayList<>(100);

for (int i = 0; i < 100; i++) {

futureList.add(EXECUTOR_SERVICE.submit(new CacheOpTask(params)));

}

for (Future future : futureList) {

System.out.println("Future result:" + future.get());

}

System.out.println(params);

}

private static class CacheOpTask implements Callable {

private Map params;

CacheOpTask(Map params) {

this.params = params;

}

@Override

public Integer call() {

for (int i = 0; i < 100; i++) {

int count = params.getOrDefault("count", 0);

params.put("count", ++count);

}

return params.get("count");

}

}

}

创建100个task,每个task对map中的元素累加100此,程序执行结果为:

{count=9846}

而预期的正确结果为:

{count=10000}

至于出现这种问题的原因,下面会具体分析。

判断是否有线程安全性的一个原则是:

是否有多线程访问可变的共享变量

2.多线程的优势

发挥多处理器的强大能力,提高效率和程序吞吐量

3.并发带来的风险

使用并发程序带来的主要风险有以下三种:

3.1.安全性问题:

竞态条件:由于不恰当的执行时序而出现不正确的结果

对于1中的线程安全的例子就是由于竞态条件导致的最终结果与预期结果不一致。关键代码块如下:

int count = params.getOrDefault("count", 0);

params.put("count", ++count);

当多个线程同时取的count的值的时候,每个线程计算之后,在写入到count,这时候会出现多个线程值被覆盖的情况,最终导致结果不正确。

如下图所示:

605ea305a723c96ff19ceaeddda15b88.png

3.2解决此类问题的几种方法

1.使用同步机制限制变量的访问:锁

比如:

synchronized (LOCK) {

int count = params.getOrDefault("count", 0);

params.put("count", ++count);

}

2.将变量设置为不可变

即将共享变量设置为final

3.不在线程之间共享此变量ThreadLocal

编程的原则:首先编写正确的代码,然后在实现性能的提升

无状态的类一定是线程安全的

3.3 内置锁

内置锁:同步代码块( synchronized (this) {})

进入代码块前需要获取锁,会有性能问题。内置锁是可重入锁,之所以每个对象都有一个内置锁,是为了避免显示的创建锁对象

常见的加锁约定:将所有的可变状态都封装在对象内部,并使用内置锁对所有访问可变状态的代码进行同步。例如:Vector等

同步的另一个功能:内存可见性,类似于volatile

非volatile的64位变量double、long:

JVM允许对64位的操作分解为两次32位的两次操作,可变64位变量必须用volatile或者锁来保护

加锁的含义不仅在于互斥行为,还包括内存可见性,为了所有线程都可以看到共享变量的最新值,所有线程应该使用同一个锁

原则:

除非需要跟高的可见性,否则应该将所有的域都声明为私有的,除非需要某个域是可变的,否则应该讲所有的域生命为final的

2.活跃性问题

线程活跃性问题主要是由于加锁不正确导致的线程一直处于等待获取锁的状态,比如以下程序:

public class DeadLock {

private static final Object[] LOCK_ARRAY;

static {

LOCK_ARRAY = new Object[2];

LOCK_ARRAY[0] = new Object();

LOCK_ARRAY[1] = new Object();

}

public static void main(String[] args) throws Exception {

TaskOne taskOne = new TaskOne();

taskOne.start();

TaskTwo taskTwo = new TaskTwo();

taskTwo.start();

System.out.println("finished");

}

private static class TaskOne extends Thread {

@Override

public void run(){

synchronized (LOCK_ARRAY[0]) {

try {

Thread.sleep(3000);

} catch (Exception e) {

}

System.out.println("Get LOCK-0");

synchronized (LOCK_ARRAY[1]) {

System.out.println("Get LOCK-1");

}

}

}

}

private static class TaskTwo extends Thread {

@Override

public void run() {

synchronized (LOCK_ARRAY[1]) {

try {

Thread.sleep(1000 * 3);

} catch (Exception e) {

}

System.out.println("Get LOCK-1");

synchronized (LOCK_ARRAY[0]) {

System.out.println("Get LOCK-0");

}

}

}

}

}

在两个线程持有一个锁,并在在锁没有释放之前,互相等待对方持有的锁,这时候会造成两个线程会一直等待,从而产生死锁。在我们使用锁的时候应该考虑持有锁的时长,特别是在网络I/O的时候。

在使用锁的时候要尽量避免以上情况,从而避免产生死锁

3.性能问题

在使用多线程执行程序的时候,在线程间的切换以及线程的调度也会消耗CPU的性能。

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

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

相关文章

java foward_java 中sendredirect()和forward()方法的区别

HttpServletResponse.sendRedirect与RequestDispatcher.forward方法都可以实现获取相应URL资源。sendRedirect实现请求重定向&#xff0c;forward实现的是请求转发。在web服务器内部的处理机制也是不一样的。1. 跳转方式运用forward方法只能重定向到同一个Web应用程序中的一个资…

java万能编码转换_java编码转换的详细过程

常见的JAVA程序包括以下类别&#xff1a;*直接在console上运行的类(包括可视化界面的类)*JSP代码类(注&#xff1a;JSP是Servlets类的变型)*Servelets类*EJB类*其它不可以直接运行的支持类这些类文件中&#xff0c;都有可能含有中文字符串&#xff0c;并且常用前三类JAVA程序和…

java里怎么存入数据并进行排序_Java数据结构之排序---插入排序

插入排序的基本介绍&#xff1a;插入排序是对想要排序的序列以插入的方式寻找该元素的适当的位置&#xff0c;从而达到排序的目的。插入排序的基本思想&#xff1a;把n个待排序的元素看成一个有序表和一个无序表&#xff0c;开始时&#xff0c;有序表只有一个元素(整个序列的第…

db h2 数据类型_H2Database数据类型

标签&#xff1a;整数(INT)INT | INTEGER | MEDIUMINT | INT4 | SIGNED值的范围为&#xff1a; -2147483648 到 2147483647.对应到Java类型&#xff1a; java.lang.Integer.例如&#xff1a;INT布尔型(BOOLEAN)BOOLEAN| BIT | BOOL可能的值为&#xff1a; TRUE 和 FALSE。对应到…

etmvc mysql乱码_etmvc中集成spring使用druid连接池

etmvc中ActiveRecord(下称AR)在使用上可以独立使用&#xff0c;其数据库的连接信息通过activerecord.properties进行配置&#xff0c;具体如下&#xff1a;domain_base_classcom.et.ar.ActiveRecordBasecom.et.ar.ActiveRecordBase.driver_classcom.mysql.jdbc.Drivercom.et.ar…

java ftp读取文件内容_java读取ftp中TXT文件的案例

最近在开发关于java读取ftp中TXT文件&#xff0c;其中有些坑踩了一下&#xff0c;再次做个记录1、读取文件时我会根据文件名称去生成数据库表&#xff0c;oracle数据库对于表名的长度是有限制的&#xff0c;最多30个字符2、对于多个文件的ftp的读取&#xff0c;每次获取文件后再…

java sql server 2016_SQL server 2016 安装步骤

1.进入安装中心&#xff1a;可以参考硬件和软件要求、可以看到一些说明文档2.选择全新安装模式继续安装3.输入产品秘钥&#xff1a;这里使用演示秘钥进行4.在协议中&#xff0c;点击同意&#xff0c;并点击下一步按钮&#xff0c;继续安装5.进入全局规则检查项&#xff0c;这里…

java resource file_Java 获取Resource目录下的文件解决办法

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼Java 获取Resource目录下的 文件有两种方式&#xff1a;Java代码中的类&#xff0c;要获取Resource资源 文件目录 下文件绝对路径寻址注意这个 / 址的是根 目录 &#xff0c;用绝对路径&#xff0c;可能会出现的问题是&#xff0c;…

java中有哪些锁_Java中有哪些锁?

1.公平锁 / 非公平锁2.可重入锁 / 不可重入锁3.独享锁 / 共享锁4.互斥锁 / 读写锁5.乐观锁 / 悲观锁6.分段锁7.偏向锁 / 轻量级锁 / 重量级锁8.自旋锁一、公平锁 / 非公平锁公平锁公平锁是指多个线程按照申请锁的顺序来获取锁。非公平锁非公平锁是指多个线程获取锁的顺序并不是…

java中对象类型转换_Java中的对象的类型转换介绍(附代码)

本篇文章给大家带来的内容是关于Java中的对象的类型转换介绍(附代码)&#xff0c;有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对你有所帮助。向上转型&#xff1a;子类对象转为父类&#xff0c;父类可以是接口。公式&#xff1a;Father f new Son(…

java中的single_解析java泛型(一)

对于我们java中的泛型&#xff0c;可能很多人知道怎么使用并且使用的还不错&#xff0c;但是我认为想要恰到好处的使用泛型&#xff0c;还是需要深入的了解一下它的各种概念和内部原理。本文将尽可能的囊括java泛型中的重要的概念。主要内容如下&#xff1a;泛型的定义及为什么…

java面板中添加面板_如何把窗体加入面板中 java

展开全部一般来说,我们常把JPanel[面板]放到JFrame窗体中但是也有一种内部窗体JInternalFrame ,可以放到其他的容器JDesktopPane里,效果图e69da5e887aa62616964757a686964616f31333363373731如下代码如下import java.awt.*;import java.awt.event.*;import java.beans.Property…

mysql 如果存在修改_mysql如存在并发修改可能,一定要注意保证数据一致性

近日&#xff0c;因人员调整接手了一个其他部门负责的项目。随后发现其中的很多关键环节是没有考虑mysql并发操作的&#xff0c;现列出存在的一例问题 并分享如何解决的。问题描述&#xff1a;用户账户余额转移赠送 (用户A将自己的账户剩余金额赠送给用户B),同一时刻还可能存在…

python prt_Python中的self

self代表类的实例&#xff0c;而非类。实例来说明Python1234567classTest:defprt(self):print(self)print(self.__class__)tTest()t.prt()执行结果如下Python12从上面的例子中可以很明显的看出&#xff0c;self代表的是类的实例。而self.class则指向类。self不必非写成self有很…

微信对账单 java_微信支付对账,你是如何处理的?

支付对账&#xff0c;即检查第三方支付与数据库中账单是否一一对应&#xff0c;涉及到微信对账单的处理&#xff0c;成功时&#xff0c;微信账单接口返回数据以文本表格的方式返回&#xff0c;第一行为表头&#xff0c;后面各行为对应的字段内容&#xff0c;字段内容跟查询订单…

java技术文章在哪里看呢_谈论Java技术似乎无处不在,但究竟是什么呢?文章末尾有福利...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼谈论Java技术似乎无处不在&#xff0c;但究竟是什么呢&#xff1f;下面的部分将解释Java技术如何既是一种编程语言&#xff0c;又是一种平台&#xff0c;并概述这种技术可以为您做些什么。Java编程语言是一种高级语言&#xff0c;可…

java中常用的关键字_java中的常用的关键字

在java编程过程中&#xff0c;会经常写到一些关键字&#xff0c;但是对于他们应用的方式其实一直是模仿状态&#xff0c;并没有很实质的去了解&#xff0c;为什么要这么用&#xff0c;导致程序不能优化&#xff0c;或者出现了bug不明所以&#xff0c;因而再详细的去重温了一下各…

java如何处理灰度图片_Java图片的灰度处理方法

通过看网上各种大牛的总结&#xff0c;和自己亲身测试总结一下Java图片的灰度处理方法(1)我们熟知的图片中的像素点有RGB值。(2)图片灰度化的方式大概分为四种&#xff0c;第一种是最大值法(取颜色RGB中的最大值作为灰度值)&#xff1b;第二种是最小值法(取颜色RGB的最小值作为…

java多项式和_在Java中查找多项式的根

小编典典请找到以下相同的示例示例public class PolynomialRootFinder {/*** * Given a set of polynomial coefficients, compute the roots of the polynomial. Depending on* the polynomial being considered the roots may contain complex number. When complex numbers …

java7 nio2 新特性_JDK7新特性,你知道几个?

前言之前学习的过程中&#xff0c;每天都是老师说这个是JDK7以后可以使用&#xff0c;那个是JDK8以后可以使用&#xff0c;每天都记的很混乱&#xff0c;今天专门忙里偷闲&#xff0c;归拢整理下JDK7的新特性&#xff0c;对于JDK的新特性&#xff0c;后期会进行整理更新&#x…