线程安全与非安全
线程安全是指,如果代码中有多个线程同时运行,如果每一次运行的结果都是一样,或者每一次运行的结果都是跟预期一样的,或者说跟单线程运行的结果一样。那么就是线程安全。
非线程安全是指多个线程同时运行的时候,出现不确定的结果。在运行的过程中可能产生脏数据。就是线程不安全。
线程安全产生的原因
其实就是两个或多个线程同时运行的时候,共享了某个数据。在多个线程同时操作该数据的情况下,该数据没有提供加锁机制。会导致该数据在多线程操作完后是一个脏数据
例如
演示一下Stringbuilder和Stringbuffer的线程安全与不安全。
开启三条线程,每条线程同时对Stringbuffer和StringBuilder进行一千次的字符追加。那么当三条线程线程运行完毕的时候。理想结果下buffer 和 builder 的长度都应该是3000。执行结果如下
public class Demo14 {
public static void main(String[] args) throws InterruptedException {
StringBuffer buffer = new StringBuffer();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 3; i++) { //启动3条线程 同时对buffer 和 builder进行添加
new Thread(() -> {
for (int j = 0; j < 1000; j++) {//每条线程添加1000个字符
buffer.append("x");
builder.append("y");
}
}).start();
}
Thread.sleep(3000);
System.out.println("buffer lenght --------> " + buffer.length());
System.out.println("builder lenght --------> " + builder.length());
}
}
第一次
第二次
显然,在builder作为三条线程的共享数据时,我们无法对builder的结果进行预测。因为Stringbuilder本身就不是一个线程安全的类。当在三个线程操作他的时候,有可能有三个线程都在同时对着某一个长度下标添加一个字符串,本应该是添加3个字符,却因为操作的是同一个下标,变成了一个。
试想一下,如果在多条线程同时操作一个线程不安全的数据集的时候,你有可能每次都获得你期望的结果吗?
再想一下,ArrayList是一个线程不安全的集合,如果把他的每个元素分别提交到一个线程里面去做处理,会有问题吗?
public class Demo15 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
List<String> list = Arrays.asList("云长", "翼德", "子龙", "孟起", "汉升", "孔明", "伯约", "幼常", "文长");
ExecutorService service = Executors.newFixedThreadPool(list.size());
for (int i = 0; i < list.size(); i++) {
service.submit(new Mybuilder(list.get(i)));
}
service.shutdown();
}
}
class Mybuilder implements Runnable {
String name;
public Mybuilder(String name) {
this.name = name;
}
@Override
public void run() {
//处理集合里面的每一个元素
String s = Thread.currentThread().getName() + "------->" + name;
System.out.println(s);
}
}