Java静态static工具类线程安全问题研究
针对静态方法有以下一些前提:
- 静态方法和实例方法的区别是静态方法只能引用静态变量,静态方法通过类名来调用,实例方法通过对象实例来调用
- 每个线程都有自己的线程栈,栈与线程同时创建,每一个虚拟机线程都有自己的程序计数器PC,在任何时刻,一个虚拟机线程只会执行一个方法的代码,这个方法称为该线程的当前方法,如果这个方法不是native的,程序计数器就保存虚拟机正在执行的字节码指令的地址。
- 线程调用方法的时候会创建栈帧,用于保存局部变量表和操作数栈以及指向该类常量池的引用
- 静态方法虽然是同一个方法,但是不同线程在调用,程序计数器的值是不一样的,操作这两个线程不会相互影响(假设不存在访问共享变量的情况)
在设计工具类时,这要没有共享的变量,静态工具类方法不需要加锁。在使用单例模式做工具类,这个时候静态方法就需要加锁,因为所有的线程虽然都是有自己的方法栈,但是在方法栈中操作的是同一个对象的实体(所以需要加锁,加锁的代价是所有的线程需要等待锁的释放才能使用该对象的引用)在使用多例模式做工具类时,这个时候也是不需要加锁,因为所有的线程都有自己的方法栈,但是方法栈帧中创建了独立的对象引用,每个线程都是在操作各自方法栈帧中的局部对象引用,所以这时候不要同步。
由于web天生并发性,导致我们的一般java工具类会在这样的环境下出现问题。
其实问题的根源就是我们的工具类不是线程安全的。
有一个生成md5的工具类:
public class MD5 {
private static long[] state = new long[4];
private static long[] count = new long[2];
private static byte[] buffer = new byte[64];
private static byte[] digest = new byte[16];
private String digestHexStr="";
public static MD5() {
}
//计算MD5
public static String getMD5ofStr(String inbuf) {
}
}
变量state, count ,buffer ,digest 算法中用到的核心数据,digestHexStr存放计算的结果。在多线程并发访问的情况下,这些变量是会被“共享”的,所以会导致计算结果不准确甚至出现异常。
有三种比较简单的方法可以解决:
getMD5ofStr方法变成非static的普通方法,这样每次调用这个方法都必须new一个新的MD5对象。
getMD5ofStr方法变成同步方法(同步代码块,显示锁,synchronized method都可以)。
将被“共享”的变量放到方法getMD5ofStr里面,不设置成员变量。
考虑到现在系统有些地方已经开始使用这个工具类了,不便改动结构,先采用第二种快速修复bug,然后腾出时间用第三种发放重构。
PS:
工具类能否设计成单例?如果能最好。单例能减少创建类和分配内存的开销,减少垃圾回收次数。
工具类能否设计成不变类?如果能最好,不变类天生线程安全!
在并发环境下,工具类能不能不用同步?不管怎么说,同步都是要有一些开销的。
PPS:
这样会好一些:
public final class MD5 {
private MD5(){}
//计算MD5
public static String getMD5ofStr(String inbuf) {
long[] state = new long[4];
long[] count = new long[2];
byte[] buffer = new byte[64];
byte[] digest = new byte[16];
String digestHexStr="";
........
}
}
其它示例:
public class StaticTest {
private static int count = 0;
private static int counts = 0;
/**
* 不会存在并发问题
*
* @return
*/
public static String getTestStr() {
String xx = Thread.currentThread().toString();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return xx;
}
/**
* 存不存在并发问题与传入的变量有关
* 假如thread a和thread b都在操作对象a则存在
* @param user
* @return
*/
public static String getTestUser(User user) {
String str = "id: " + user.getId() + "name: " + user.getName();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return str;
}
/**
* 存在并发问题
*
* @return
*/
public static int getTestCount() {
count++;
count++;
count++;
count++;
count++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
count++;
count++;
count++;
count++;
return count;
}
/**
* 不存在并发问题
*
* @return
*/
public synchronized static int getTestCountS() {
counts++;
counts++;
counts++;
counts++;
counts++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
counts++;
counts++;
counts++;
counts++;
counts++;
return counts;
}
public static void main(String[] args) {
User user = new User();
for (int i = 0 ; i < 1000 ; i++){
final int finalI = i;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
User userTmp = new User();
user.setId(finalI);
user.setName(Thread.currentThread().toString());
userTmp.setId(finalI);
userTmp.setName(Thread.currentThread().toString());
//局部变量不存在问题
System.out.println("getTestStr: " + Thread.currentThread() + StaticTest.getTestStr());
//与user有关
System.out.println("getTestUser: " + Thread.currentThread() + StaticTest.getTestUser(user));
System.out.println("getTestUseS: " + Thread.currentThread() + StaticTest.getTestUser(userTmp));
//线程不安全
System.out.println("getTestCount: " + Thread.currentThread() + StaticTest.getTestCount() % 10);
//安全但是慢需要加锁
System.out.println("getTestCountS: " + Thread.currentThread() + StaticTest.getTestCountS() % 10);
}
});
thread.start();
}
}
}
参考:
http://blog.csdn.net/thekenofdis/article/details/74529886(以上内容部分转自此篇文章)
https://www.cnblogs.com/LvLoveYuForever/p/6077148.html
http://www.genshuixue.com/i-cxy/p/7637981
http://wuhaocn.iteye.com/blog/2269391(以上内容部分转自此篇文章)
Java静态static工具类线程安全问题研究的更多相关文章
- Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo
Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo CountDownLatch countDownLatch这个类使一个线程等待其他线程 ...
- HttpTool.java(在java tool util工具类中已存在) 暂保留
HttpTool.java 该类为java源生态的http 请求工具,不依赖第三方jar包 ,即插即用. package kingtool; import java.io.BufferedReader ...
- java文件处理工具类
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedRead ...
- java格式处理工具类
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOExceptio ...
- 使用java的Calendar工具类获取到本月的第一天起始时间和最后一天结束时间。
1.使用java的Calendar工具类获取到本月的第一天起始时间和最后一天结束时间. package com.fline.aic.utils; import java.text.DateFormat ...
- JAVA 8 日期工具类
JAVA 8 日期工具类 主题描述 JAVA中日期时间的历史 代码成果 主题描述 JAVA的日期时间一直比较混乱,本来以为joda会是巅峰,但是JAVA 8改变了我的思想.但是即便在JAVA 8面前, ...
- java 解析excel工具类
java 解析excel工具类 CreateTime--2018年3月5日16:48:08 Author:Marydon ReadExcelUtils.java import java.io.Fi ...
- UrlUtils工具类,Java URL工具类,Java URL链接工具类
UrlUtils工具类,Java URL工具类,Java URL链接工具类 >>>>>>>>>>>>>>>&g ...
- Java基础-DButils工具类(QueryRunner)详解
Java基础-DButils工具类(QueryRunner)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC ...
随机推荐
- SpringMVC学习 -- REST
REST:表现层状态转化. REST 是目前最流行的一种互联网软件架构.他结构清晰.符合标准.易于理解.扩展方便 , 所以正得到越来越多网站的采用. 状态转化:浏览器 form 表单只支持 GET 和 ...
- 我们用CloudStack做什么
原文地址:http://www.sdfengxi.com/?p=376 我想很多同学会有类似的疑问,就是我配置好了CloudStack或者OpenStack之类的环境之后能够提供什么服务或者应用呢?下 ...
- Activiti工作流引擎核心介绍
引言 Activiti 作为一个遵从 Apache 许可的工作流和业务流程管理开源平台,其核心是基于 Java 的超快速.超稳定的 BPMN 2.0 流程引擎,强调流程服务的可嵌入性和可扩展性,同时更 ...
- Spring - IoC(10): 生命周期
Spring 容器可以管理 singleton 作用域 Bean 的生命周期,容器能够跟踪 Bean 实例的创建.销毁.管理 Bean 生命周期行为主要有两个时机: 注入 Bean 的依赖关系之后 即 ...
- 编写类du命令Python脚本
#!/usr/bin/env python #_*_ coding:utf-8 _*_ #计算整个目录的大小,脚本接受-H参数,来加上适当的单位 #功能像du看齐 import os,sys from ...
- python3,循环,方法练习2
1:编写for循环,利用索引遍历出每一个字符 msg = 'hello egon 666' msg = 'hello egon 666' i = 0 for i in range(0, len(msg ...
- RabbitMQ消息队列(四): 消息路由
1. 路由: 前面的示例中,我们或得到的消息为广播消息,但是无法更精确的获取消息的子集,比如:日志消息,worker1只需要error级别的日志, 而worker2需要info,warning,err ...
- HTTP===如何理解网关
首先举个例子: 假设你的名字叫小不点(很小),你住在一个大院子里,你的邻居有很多小伙伴,父母是你的网关.当你想跟院子里的某个小伙伴玩,只要你在院子里大喊一声他的名字,他听到了就会回应你,并且跑出来跟你 ...
- Linux内核:关于中断你需要知道的【转】
转自:http://blog.csdn.net/duqi_2009/article/details/38009717 1.中断处理程序与其他内核函数真正的区别在于,中断处理程序是被内核调用来相应中断的 ...
- IpmiTool常见问题解决办法
IpmiTool常见问题解决办法 http://blog.csdn.net/c9h8o4/article/details/17138029 关于IPMI的几个问题 http://blog.csdn.n ...