一.何为线程不安全类?
一般认为,当多个线程并发的对一个类对象进行操作后发生了线程安全问题,那么这个类就是线程不安全的类。
那么就有人认为:那为什么要编写线程不安全的类呢?把所有类都编写成线程安全的类不就行了,这是不现实的,我们知道,实现线程安全就要付出一定的性能开销,在有些情况下使用线程不安全的类性能要比使用线程安全的类的性能要好很多。
总结:线程不安全的类的性能一般要比线程安全的类高。
二.Java中线程不安全的类与其对应的安全类
我们在进行java编程时,有必要区分java库中一些线程安全和不安全的类来提高我们的效率
1.StringBuilder和StringBuffer
java中关于字符串的类有三个,String,String**Builder,StringBuffer,其中String我们知道是不可变类,它一定线程安全,那么剩下的StringBuild和StringBuffer是不是线程安全的呢?
我们可以做个小实验:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SafeClass {
public static int clientTotal=5000;
public static int threadTotal=200;
public static StringBuilder stringBuilder=new StringBuilder();
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService= Executors.newCachedThreadPool();//线程池
final Semaphore semaphore=new Semaphore(threadTotal);//信号量
final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);//阻塞倒计数
for(int i=0;i<clientTotal;i++){
executorService.execute(()->{
try{
semaphore.acquire();
Mothen();
semaphore.release();
}catch(Exception e){
e.printStackTrace();
}
countDownLatch.countDown();//计数器减一操作
});
}
countDownLatch.await();//阻塞线程
executorService.shutdown();//其中先前提交的任务将被执行,但不会接受任何新任务
System.out.println(stringBuilder.length());
}
public static void Mothen(){
stringBuilder.append("i");
}
}
结果:
我们的并发次数是5000次,而对StringBuilder对象操作后的长度却是4978,显然线程不安全。
同理我们把上面的StringBuilder换成StringBuffer:
显然线程安全。
由此我们推测出StringBuffer类中一定加了线程安全操作的:
@Override
public synchronized void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
toStringCache = null;
value[index] = ch;
}
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
果然,其中的几乎每个方法都用synchronized进行同步了,反之,StringBuilder中没有,但它的性能比StringBuffer好。
2.ArrayList和Vector
ArrayList和Vector都是动态数组,它们那个是线程安全的,那个不安全呢?
我们做个小实验:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SafeClass {
public static int clientTotal=5000;
public static int threadTotal=200;
public static List list= new ArrayList();
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService= Executors.newCachedThreadPool();//线程池
final Semaphore semaphore=new Semaphore(threadTotal);//信号量
final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);//阻塞倒计数
for(int i=0;i<clientTotal;i++){
executorService.execute(()->{
try{
semaphore.acquire();
Mothen();
semaphore.release();
}catch(Exception e){
e.printStackTrace();
}
countDownLatch.countDown();//计数器减一操作
});
}
countDownLatch.await();//阻塞线程
executorService.shutdown();//其中先前提交的任务将被执行,但不会接受任何新任务
System.out.println(list.size());
}
public static void Mothen(){
list.add(1);
}
}
很明显,不安全,反之,将ArrayList改为Vector后:
同理,下面的关于线程安全和不安全的就不展示实例了:
关于集合中线程安全和不安全的类,有:
Vector,Stack,Hashtable线程安全之外,其他都不安全。
4.Collections.synchronizedXXX(List,Set ,Map)
如果我们想得到同步集合类,我们可以通过Collections中的静态工厂方法中得到相应的同步容器类:
例如:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SafeClass {
public static int clientTotal=5000;
public static int threadTotal=200;
public static List list= Collections.synchronizedList(new ArrayList<>());//化不安全为安全
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService= Executors.newCachedThreadPool();//线程池
final Semaphore semaphore=new Semaphore(threadTotal);//信号量
final CountDownLatch countDownLatch=new CountDownLatch(clientTotal);//阻塞倒计数
for(int i=0;i<clientTotal;i++){
executorService.execute(()->{
try{
semaphore.acquire();
Mothen();
semaphore.release();
}catch(Exception e){
e.printStackTrace();
}
countDownLatch.countDown();//计数器减一操作
});
}
countDownLatch.await();//阻塞线程
executorService.shutdown();//其中先前提交的任务将被执行,但不会接受任何新任务
System.out.println(list.size());
}
public static void Mothen(){
list.add(1);
}
}
同理其他的也是如此