在JDK API,有很多类上面都写着是线程安全或者线程不安全,比如StringBuilder就是线程不安全的类,在多线程的情况下,如果需要同步,则建议使用StringBuffer。
存在成员变量的类用于多线程时是不安全的,不安全体现在这个成员变量可能发生非原子性的操作,而变量定义在方法内也就是局部变量是线程安全的。在日常开发中,通常需要考虑成员变量或者说全局变量在多线程环境下,是否会引发一些问题。以下是示例:
创建一个线程不安全的类:
public class Count {
private int num;//成员变量
public void count() {
for(int i=1;i<=10;i++){
num+=i;
}
System.out.println("线程:"+Thread.currentThread().getName()+";数据:"+num);
}
}
创建多线程并执行
public class ThreadSecurityTest {
public static void main(String[] args){
Runnable runnable = new Runnable() {
Count count = new Count();
@Override
public void run() {
count.count();
}
};
for(int i=0;i<10;i++){
new Thread(runnable).start();
}
}
}
这里,我们想要的结果是输出的值是一样的55
而实际上的输出结果是(会有不同的输出结果,反正不会是上面的):
线程:Thread-0;数据:55
线程:Thread-3;数据:165
线程:Thread-2;数据:275
线程:Thread-9;数据:495
线程:Thread-5;数据:550
线程:Thread-4;数据:220
线程:Thread-1;数据:165
线程:Thread-6;数据:330
线程:Thread-8;数据:440
线程:Thread-7;数据:385
完全乱掉,也就是说,所有的线程都在任意时刻读取和修改成员变量num的值,而且没有同步。也可以说这种错误是由于成员变量造成的
当我们把Count类的成员变量num变为方法变量的时候:
public class Count {
//private int num;
public synchronized void count() {
int num = 0;
for(int i=1;i<=10;i++){
num+=i;
}
System.out.println("线程:"+Thread.currentThread().getName()+";数据:"+num);
}
}
结果
线程:Thread-0;数据:55
线程:Thread-3;数据:55
线程:Thread-4;数据:55
线程:Thread-2;数据:55
线程:Thread-1;数据:55
线程:Thread-7;数据:55
线程:Thread-9;数据:55
线程:Thread-8;数据:55
线程:Thread-6;数据:55
线程:Thread-5;数据:55
或者我们把runnable的成员变量变为方法变量
线程:Thread-4;数据:55
线程:Thread-2;数据:55
线程:Thread-9;数据:55
线程:Thread-7;数据:55
线程:Thread-3;数据:55
线程:Thread-0;数据:55
线程:Thread-1;数据:55
线程:Thread-5;数据:55
线程:Thread-6;数据:55
线程:Thread-8;数据:55
输出都符合我们的预期。