經常看到一些類,有的說線程安全,有的說線程不安全,頓時懵逼。
線程安全不安全,主要是在多線程執行的情況下,如果由於線程之間搶占資源而造成程序的bug即為線程不安全,下面就拿arraylist 和Vector來舉個例子:
這里的arraylist 是線程不安全的,Vector是線程安全的
package Thread;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class MyThread implements Runnable{
private List<Object> list;
private CountDownLatch countDownLatch;
public MyThread(){}
public MyThread(List<Object> list,CountDownLatch countDownLatch){
this.list=list;
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//給每個線程添加10個元素
for(int i=0;i<10;i++){
list.add(new Object());
}
//完成一個子線程
countDownLatch.countDown();
}
}
package Thread;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
public class ThreadTest {
/**
* 這里要比較的是arraylist 和Vector來測試
* arraylist 是線程不安全的
* Vector 線程安全的
*
*/
public static void test(){
//用來測試的list集合
List<Object> list= new ArrayList<Object>();
//List<Object> list = new Vector<Object>();
//線程數
int threadCount =10000;
//用來讓主線等待thread 個執行完畢
CountDownLatch count=new CountDownLatch(threadCount);
for(int i=0;i<threadCount;i++){
Thread thread=new Thread(new MyThread(list, count));
thread.start();
}
try {
//主線程所有都執行完成后,再向下執行
count.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(list.size());
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
test();
}
}
}
運行結構:
99995
99998
99989
99973
99894
99970
99974
99977
99990
99989
當使用Vector時,即把測試的集合換一下
package Thread;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
public class ThreadTest {
/**
* 這里要比較的是arraylist 和Vector來測試
* arraylist 是線程不安全的
* Vector 線程安全的
*
*/
public static void test(){
//用來測試的list集合
//List<Object> list= new ArrayList<Object>();
List<Object> list = new Vector<Object>();
//線程數
int threadCount =10000;
//用來讓主線等待thread 個執行完畢
CountDownLatch count=new CountDownLatch(threadCount);
for(int i=0;i<threadCount;i++){
Thread thread=new Thread(new MyThread(list, count));
thread.start();
}
try {
//主線程所有都執行完成后,再向下執行
count.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(list.size());
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
test();
}
}
}
這樣運行的結果:
100000
100000
100000
100000
100000
100000
100000
100000
100000
100000
很明顯,使用Vector 這個類運行正確,這就是所謂的線程安全
當然,這只是代碼層面上的,其實多線程不安全,主要因為cpu分配機制,誰獲得了cpu誰就能執行,因此造成了線程的不安全.
我們可以使用synchronized 關鍵字來同步代碼塊,達到線程安全:
下面舉個synchronized同步的例子:
package Thread1;
public class Bank {
private int sum = 0;
public void add(int n) {
sum = sum + n;
System.out.println("sum= " + sum);
}
}
package Thread1;
public class Cus implements Runnable {
Bank b = new Bank();
@Override
public void run() {
for (int i = 0; i < 3; i++) {
b.add(100);
}
}
}
package Thread1;
public class Test {
public static void main(String[] args) {
Cus c = new Cus();
for (int i = 0; i < 3; i++) {
new Thread(c).start();
}
}
}
沒有使用synchronized修飾的時候,運行結構是:
sum= 100
sum= 400
sum= 500
sum= 300
sum= 200
sum= 600
sum= 800
sum= 700
sum= 900
當然synchronized 必須要在線程運行的代碼塊中修飾:
package Thread1;
public class Bank {
private int sum = 0;
public void add(int n) {
sum = sum + n;
System.out.println("sum= " + sum);
}
}
package Thread1;
public class Cus implements Runnable {
Bank b = new Bank();
@Override
public void run() {
synchronized(this){
for (int i = 0; i < 3; i++) {
b.add(100);
}
}
}
}
package Thread1;
public class Test {
public static void main (String [] args) {
Cus c = new Cus();
for(int i=0;i<3;i++){
new Thread(c).start();
}
}
}
這樣保證,每次只有一個線程在運行,結果為:
sum= 100
sum= 200
sum= 300
sum= 400
sum= 500
sum= 600
sum= 700
sum= 800
sum= 900
OK,OVER
每天進步一點點,堅持下去
