Java静态工具类线程安全的一些建议

针对静态方法有以下一些前提
  • 静态方法和实例方法的区别是静态方法只能引用静态变量,静态方法通过类名来调用,实例方法通过对象实例来调用
  • 每个线程都有自己的线程栈,栈与线程同时创建,每一个虚拟机线程都有自己的程序计数器PC,在任何时刻,一个虚拟机线程只会执行一个方法的代码,这个方法称为该线程的当前方法,如果这个方法不是native的,程序计数器就保存虚拟机正在执行的字节码指令的地址。
  • 线程调用方法的时候会创建栈帧,用于保存局部变量表和操作数栈以及指向该类常量池的引用
  • 静态方法虽然是同一个方法,但是不同线程在调用,程序计数器的值是不一样的,操作这两个线程不会相互影响(假设不存在访问共享变量的情况)

在设计工具类时,这要没有共享的变量,静态工具类方法不需要加锁。在使用单例模式做工具类,这个时候静态方法就需要加锁,因为所有的线程虽然都是有自己的方法栈,但是在方法栈中操作的是同一个对象的实体(所以需要加锁,加锁的代价是所有的线程需要等待锁的释放才能使用该对象的引用)在使用多例模式做工具类时,这个时候也是不需要加锁,因为所有的线程都有自己的方法栈,但是方法栈帧中创建了独立的对象引用,每个线程都是在操作各自方法栈帧中的局部对象引用,所以这时候不要同步。

那么问题来了,如果有共享变量的时候应该怎么办呢?一下是一些建议

1.静态方法 
    无论是静态方法还是实例方法,在内存中都只有一份代码,也就是只占用一份内存空间 
方法属于一个程序块,只有当别人调用它时才会调到内存里面去执行,也就是说当前有多少个线程在执行就有多少组方法块里的局部变量 

2.静态变量 
    只存在一份,多个线程公用一份,一个线程修改就会影响其他线程 

3.结论 
   静态方法是使用得当是线程安全的,因为每次调用会创建一份私有块,如果是静态变量是的的话就要加锁挥着其他处理。 

由于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="";  
        ........  
    }  
}  

  1. class User{  
  2.     private int id;  
  3.     private String name;  
  4.   
  5.     public int getId() {  
  6.         return id;  
  7.     }  
  8.   
  9.     public void setId(int id) {  
  10.         this.id = id;  
  11.     }  
  12.   
  13.     public String getName() {  
  14.         return name;  
  15.     }  
  16.   
  17.     public void setName(String name) {  
  18.         this.name = name;  
  19.     }  
  20. }  

Java代码   收藏代码
  1. public class StaticTest {  
  2.     private static int count = 0;  
  3.     private static int counts = 0;  
  4.   
  5.     /** 
  6.      * 不会存在并发问题 
  7.      * 
  8.      * @return 
  9.      */  
  10.     public static String getTestStr() {  
  11.         String xx = Thread.currentThread().toString();  
  12.         try {  
  13.             Thread.sleep(10);  
  14.         } catch (InterruptedException e) {  
  15.             e.printStackTrace();  
  16.         }  
  17.         return xx;  
  18.     }  
  19.   
  20.     /** 
  21.      * 存不存在并发问题与传入的变量有关 
  22.      * 假如thread a和thread b都在操作对象a则存在 
  23.      * @param user 
  24.      * @return 
  25.      */  
  26.     public static String getTestUser(User user) {  
  27.         String str = "id: " + user.getId() + "name: " + user.getName();  
  28.         try {  
  29.             Thread.sleep(10);  
  30.         } catch (InterruptedException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.         return str;  
  34.     }  
  35.   
  36.     /** 
  37.      * 存在并发问题 
  38.      * 
  39.      * @return 
  40.      */  
  41.     public static int getTestCount() {  
  42.         count++;  
  43.         count++;  
  44.         count++;  
  45.         count++;  
  46.         count++;  
  47.         try {  
  48.             Thread.sleep(10);  
  49.         } catch (InterruptedException e) {  
  50.             e.printStackTrace();  
  51.         }  
  52.         count++;  
  53.         count++;  
  54.         count++;  
  55.         count++;  
  56.         count++;  
  57.         return count;  
  58.     }  
  59.   
  60.     /** 
  61.      * 不存在并发问题 
  62.      * 
  63.      * @return 
  64.      */  
  65.     public synchronized static int getTestCountS() {  
  66.         counts++;  
  67.         counts++;  
  68.         counts++;  
  69.         counts++;  
  70.         counts++;  
  71.         try {  
  72.             Thread.sleep(10);  
  73.         } catch (InterruptedException e) {  
  74.             e.printStackTrace();  
  75.         }  
  76.         counts++;  
  77.         counts++;  
  78.         counts++;  
  79.         counts++;  
  80.         counts++;  
  81.         return counts;  
  82.     }  
  83.   
  84.     public static void main(String[] args) {  
  85.         User user = new User();  
  86.         for (int i = 0 ; i < 1000 ; i++){  
  87.             final int finalI = i;  
  88.             Thread thread = new Thread(new Runnable() {  
  89.                 @Override  
  90.                 public void run() {  
  91.                     User userTmp = new User();  
  92.                     user.setId(finalI);  
  93.                     user.setName(Thread.currentThread().toString());  
  94.                     userTmp.setId(finalI);  
  95.                     userTmp.setName(Thread.currentThread().toString());  
  96.                     //局部变量不存在问题  
  97.                     System.out.println("getTestStr: " + Thread.currentThread()  + StaticTest.getTestStr());  
  98.                     //与user有关  
  99.                     System.out.println("getTestUser: " + Thread.currentThread() + StaticTest.getTestUser(user));  
  100.                     System.out.println("getTestUseS: " + Thread.currentThread()  + StaticTest.getTestUser(userTmp));  
  101.   
  102.                     //线程不安全  
  103.                     System.out.println("getTestCount: "  + Thread.currentThread() + StaticTest.getTestCount() % 10);  
  104.   
  105.                     //安全但是慢需要加锁  
  106.                     System.out.println("getTestCountS: "  + Thread.currentThread() + StaticTest.getTestCountS() % 10);  
  107.                 }  
  108.             });  
  109.             thread.start();  
  110.         }  
  111.   
  112.   
  113.     }  
  114. }  

    

猜你喜欢

转载自blog.csdn.net/qq_36773257/article/details/79918061