线程安全是指在多线程的运行环境之下,通过一定的同步机制,保证多个线程对同一共享资源的操作能够得到正确的执行,符合这样条件的类称为线程安全的类
如下代码:
public class Main {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
count++;
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
count++;
}
}
});
t1.start();t2.start();
t1.join();t2.join();
System.out.println(count);
}
}
两个线程同时操作同一个数据,都累加1000次,结果应该为2000,但事实上并非如此
虽然count++只是一条语句,但是翻译成机器码之后就编程了多条指令,在串行条件下,我们来分析下累加的过程:
1 读取count的值
2 count值加1
3 将新的值写回内存
在多线程的环境下,两个线程间的代码执行顺序是不明确的,很可能出现如下情况
1 线程A读取count的值为1
2 线程B读取count的值为1
3 线程Acount值+1
4 线程Bcount值+1
5 线程A写回内存,count为1
6 线程B写回内存,count为1
其实一开始,i++只是一条语句,如果不是在底层变成了多个命令,自然也就不会发生这么莫名其妙的问题,这就是操作的原子性,我们把对共享资源的操作想象成一组指令,只要能保证在这组操作执行结束之前其他线程不能操作该资源,那问题就可以得到解决,这就是同步,
public class Main {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
add();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
add();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
public static synchronized void add() {
count++;
}
}
synchronized是java提供的同步机制,可以保存在synchronized修饰的代码未执行结束之前,保证其他线程无法执行count++操作,这样就使得count++的操作具备原子性。但是,这会导致并行的性能降低。
特别说明
无论你写的类是否线程安全,你都无法阻止别人使用该类的方式,在进行java Web开发的时候,虽然你写的类没有考虑线程安全的问题,但服务器会使用多线程去访问同一段代码,这是应对多用户的需要。所以,保证自己的类是线程安全的,如果不是,则需要进行特殊说明。