如何同步保证线程安全
1、 问题描述:
如果一个资源或对象可能被多个线程同时访问,它就是一个共享资源;例如类的成员变量,包括类变量和实例变量,再比如对一个文件进行写操作等。一般情况下,对共享资源的访问需要考虑线程安全的问题。
如果一个对象的完整生命周期只在一个线程内,则不需要考虑线程安全,例如一个局部变量。下面为一个示例代码:
- public class C1 {
- public static java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
- //其他代码
- }
假如在一个JSP中这样的去调用:
- <a.jsp>:
- <%
- Java.util.Date date = C1.sdf.parse(“2003-4-15”);
- %>
则这样的代码不是线程安全的。因为java.text.SimpleDateFormat不是线程安全的,a.jsp中的代码将会有若干个线程同时执行,而都访问的是同一个线程不安全的对象,这样就不是一个线程安全的代码。正确的写法应该如下:
- <a.jsp>:
- <%
- java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
- Java.util.Date date = sdf.parse(“2003-4-15”);
- %>
2、 原因分析:
此时,sdf对象从创建到销毁都位于一个方法中,相当于一个局部变量,不是一个共享资源,因此则没有线程不安全的问题。
3、 解决方法或过程:
1) 如果对象是immutable,则是线程安全的,例如:String,可以放心使用。
2) 如果对象是线程安全的,则放心使用
3) 有条件线程安全,对于Vector和Hashtable一般情况下是线程安全的,但是对于某些特殊情况,需要通过额外的synchronized保证线程安全。
4) 使用synchronized关键字;
对于上例中可以改写jsp代码,在sdf上进行同步,而不需要每次创建一个新的对象来保证线程安全,代码如下:
- <%
- synchronized(C1.sdf){
- Java.util.Date date = C1.sdf.parse(“2003-4-15”);
- }
- %>
这种写法是在一个对象级别上进行同步,也就是说任何时候,对于这个对象,最多只能有一个线程在执行同步方法。
另外一种写法是在Class级别上进行同步,写法如下:
- public class C1 {
- public static java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
- public void method(){
- synchronized(C1.class){
- //synchronized code
- }
- }
- }
这种写法表示无论C1有多少个实例,在任何一个时间点,最多只能有一个线程和一个实例进入同步块中。这种同步会比较大的影响性能。
5) 有些对象不能在多线程间共享,则只能在方法内部使用,或者只在一个线程内部使用。