线程安全与线程非安全,怎样区分呢?
其实,当线程访问同一资源时,如同一个对象的同一个变量,会存在多个线程对同一资源的竞争访问,也就存在线程非安全。所以说,非安全指的是:多个线程访问同一个资源,可能会访问到非想要的资源。如线程一改写了变量a,而线程二此时又来访问变量a,则线程二得到的是修改后的变量a,而不是修改前的变量a。这样,就存在了数据的非安全访问,称之为线程非安全。这也可以称为数据的脏读。
反之,如果两个线程不会访问同样的资源,则不会存在上述问题,所以,称之为线程安全。
那是不是只要;两个线程访问同一对象的同一资源就是线程不安全的呢?
这也不是。如果访问的是同一个方法,而该方法有没有访问类的其他属性,则该方法的访问不存在线程安全问题。即:方法内部的局部变量是线程安全的。
代码如下:
package com.cmm.thread1;
public class ThreadSafe {
public void add(int a) throws InterruptedException{
int num = 0;
if(a == 100){
num = 100;
Thread.sleep(1000);
System.out.println("num = " + num);
}else{
num = 200;
System.out.println("num = " + num);
}
}
}
package com.cmm.thread1;
public class ThreadA extends Thread{
private ThreadSafe safe;
public ThreadA(ThreadSafe safe) {
this.safe = safe;
}
@Override
public void run() {
super.run();
try {
this.safe.add(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.cmm.thread1;
public class ThreadB extends Thread{
private ThreadSafe safe;
public ThreadB(ThreadSafe safe) {
this.safe = safe;
}
@Override
public void run() {
super.run();
try {
this.safe.add(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试代码如下:
package com.cmm.thread1;
public class Main {
public static void main(String[] args) {
ThreadSafe safe = new ThreadSafe();
ThreadA threadA = new ThreadA(safe);
ThreadB threadB = new ThreadB(safe);
threadA.start();
threadB.start();
}
}
多次测试结果为:
num = 200
num = 100
可以看出,多个线程访问同一方法的局部变量是线程安全的。
但是,如果是类的属性,则存在线程不安全的问题。
修改代码如下:
package com.cmm.thread1;
public class ThreadSafe {
private int num = 0;
public void add(int a) throws InterruptedException{
if(a == 100){
num = 100;
Thread.sleep(1000);
System.out.println("num = " + num);
}else{
num = 200;
System.out.println("num = " + num);
}
}
}
测试结果如下:
num = 200
num = 200
可以看出,出现的数据脏读。
当多线程访问同一资源时,就会出现脏读问题。所以,这个时候,需要用一定的手段,来使得同一时刻,只有一个线程可以对共享资源进行访问。访问分两种,只读,读写。所以又有不同的处理。
后面,会详细介绍线程同步问题。
在这里先总结一句话:线程同步问题,就是为了解决多个线程对共享数据的同时访问问题,访问又分为只读与读写两种,所以,线程同步,需要分场景做不同的讨论处理。