Java 线程安全和线程不安全理解

当我们查看JDK API的时候,总会发现一些类说明写着,线程安全或者线程不安全,顿时懵逼。

线程安全不安全,主要是在多线程执行的情况下,如果由于线程之间抢占资源而造成程序的bug即为线程不安全,下面就拿arraylist 和Vector来举个例子:

这里的arraylist 是线程不安全的,Vector是线程安全的。

package Thread;

import java.util.List;
import java.util.concurrent.CountDownLatch;

public class MyThread  implements Runnable{
    private List list;
    private CountDownLatch countDownLatch;
    public MyThread(){}
    public  MyThread(List 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 list= new ArrayList();
        //List list = new Vector();
        //线程数
        int threadCount =10000;
        //用来让主线等待thread 个执行完毕
        CountDownLatch  count=new CountDownLatch(threadCount);
        for(int i=0;inew 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 list= new ArrayList();
        List list = new Vector();
        //线程数
        int threadCount =10000;
        //用来让主线等待thread 个执行完毕
        CountDownLatch  count=new CountDownLatch(threadCount);
        for(int i=0;inew 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