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里怎么存入数据并进行排序_Java数据结构之排序---插入排序

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

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中的对象的类型转换介绍(附代码)

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

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),同一时刻还可能存在…

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

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

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

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

java jsp校验提示信息_java Jquery表单校验代码jsp页面

jsp.file欢迎注册EasyMall/* 注册表单的js校验 */var formObj {/* 检查输入项是否为空 */"checkNull" : function(name, msg){var value $("input[name"name"]").val().trim();//清空之前的提示消息formObj.setMsg(name, "");if(val…

错误处理方法 java_JAVA常见错误处理方法 和 JVM内存结构

OutOfMemoryError在开发过程中是司空见惯的&#xff0c;遇到这个错误&#xff0c;新手程序员都知道从两个方面入手来解决&#xff1a;一是排查程序是否有BUG导致内存泄漏&#xff1b;二是调整JVM启动参数增大内存。OutOfMemoryError有好几种情况&#xff0c;每次遇到这个错误时…

java中如何分隔字符串_Java中分割字符串

java.lang.String的split()方法, JDK 1.4 or laterpublic String[] split(String regex,int limit)示例代码public classStringSplit {public static voidmain(String[] args) {String sourceStr "1,2,3,4,5";String[] sourceStrArray sourceStr.split(",&quo…

php测试号推送消息失败,信息发送失败是什么原因

手机突然信息发送失败可能是以下原因&#xff1a;1.是因为我们的手机出现了欠费的情况,所以发不出短信,这种情况是最为普遍的,需要我们及时的进行缴费。2.手机的信息中心的号码设置有误,应该根据你所在省份的实际信息中心号码进行设置,这样一般就能解决这方面的问题。可能是你的…

php ajax 概率 转盘,php+jquery实现转盘抽奖 概率可任意调

phpjquery实现转盘抽奖 概率可任意调phpjquery实现转盘抽奖 概率可任意调Posted by: xiaomiao 2014/05/13in Code, PHP 3 Commentsphpjquery实现转盘抽奖查看DEMO演示转盘抽奖&#xff0c;炫丽的一般是flash做的。不懂flash而又不需要那么炫丽&#xff0c;可以简单的通过jquer…

php自动抓取文章图片,从文章中提取图片,把图片保存到本地,自动提取缩略图...

开发二代旅游网站程序和CMS的时候&#xff0c;有一个需求&#xff0c;就是从网上复制的内容&#xff0c;里面包含图片的&#xff0c;需要对把图片提取出来&#xff0c;并且保存到本地&#xff0c;并且把图片的URL地址本地化&#xff0c;以下是实现的代码。开发二代旅游网站程序…

简单的php探针,php探针程序的推荐

在我们之前的文章已经为大家介绍了什么是php探针&#xff0c;以及他的主要作用是什么&#xff0c;如果你接触了cms或许就会有点了解&#xff0c;当然&#xff0c;不要紧&#xff0c;看完这篇就知道php探针是做什么的了。php探针通常是用来探测空间、服务器运行的状况和php相关信…

php熊掌号怎么设置json-ld,dedecms织梦系统对接百度熊掌号并添加JSON_LD数据

百度近期推出的百度熊掌号非常的不错,我的dedecms织梦系统早早就对接好了,它能对你的原创文章进行原创保护,并评出熊掌号搜索指数,熊掌号搜索指数是对你文章的内容质量,用户喜爱、原创能力、活跃表现、领域专注五个维度进行计算评估而得到的。你的dedecms织梦网站开通熊掌号之后…

php获取信息,PHP文件信息获取函数

知识点&#xff1a;basename():获取文件名&#xff0c;传入第二个参数则只显示文件名&#xff0c;不显示后缀dirname():获取文件路径pathinfo():将文件信息存入一个数组&#xff0c;通过索引basename&#xff0c;dirname&#xff0c;extension可以获得对应的文件名&#xff0c;…

mysql pdo 查询一条数据,使用 PDO 关联查询 MySQL 数据

使用pdo关联查询mysql数据try {$pdo new PDO(mysql:hostlocalhost;dbnametest;, root, 123456);// 0.等值联结$sql SELECT c.name, o.id, o.customer_id, o.price FROM orders o, customer c WHERE o.customer_id c.id AND c.name :name;// 1.内联结(与上面等值联结返回的查…

mysql 数据类型怎么用,myMySQL数据库怎么更改表中某字段的数据类型? MySQL数据库使用教程...

在mysql中&#xff0c;可以使用“ALTER TABLE”语句配合“MODIFY”关键字来更改表中某字段的数据类型&#xff1b;语法格式“ALTER TABLE MODIFY ”。(推荐教程&#xff1a;mysql视频教程)在 MySQL 中&#xff0c;ALTER TABLE 语句可以改变原有表的结构&#xff0c;例如增加或删…