之前几个文章中我已经说明了”线程安全“和”非线程安全“。
”非线程安全“其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是”脏读“,也就是取到的数据其实是被更改过得。而”线程安全“就是以获得的实例变量的值是进过同步处理的,不会出现藏独的现象。
1.方法内的变量为线程安全
”非线程安全“问题存在于”实例变量“中,如果是方法内部的私有变量,则不存在”非线程安全“问题,所得结果也就是”线程安全“的了。
下面我以一段代码说明,在实现方法内部声明一个变量时,是不存在”非线程安全“问题得。
示例代码:
方法代码:
package test;
/**
* @Author LiBinquan
*/
public class HasSelfPrivateNum {
public void addNum(String userName){
try{
int num = 0;
if ("a".equals(userName)){
num = 100;
System.out.println("a set over");
Thread.sleep(1000);
}else{
num = 200;
System.out.println("b set over");
}
System.out.println(userName +" num = "+num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
线程1:
package test;
import java.util.Random;
/**
* @Author LiBinquan
*/
public class ThreadTest1 extends Thread{
private HasSelfPrivateNum name;
public ThreadTest1(HasSelfPrivateNum name){
super();
this.name = name;
}
@Override
public void run() {
super.run();
name.addNum("a");
}
}
线程2:
package test;
import java.util.Random;
/**
* @Author LiBinquan
*/
public class ThreadTest2 extends Thread{
private HasSelfPrivateNum name;
public ThreadTest2(HasSelfPrivateNum name){
super();
this.name = name;
}
@Override
public void run() {
super.run();
name.addNum("b");
}
}
运行类代码:
package test;
/**
* @Author LiBinquan
*/
public class Run {
public static void main(String[] args) throws InterruptedException {
HasSelfPrivateNum num = new HasSelfPrivateNum();
ThreadTest1 threadTest1 = new ThreadTest1(num);
threadTest1.start();
ThreadTest2 threadTest2 = new ThreadTest2(num);
threadTest2.start();
}
}
输出:
由输出可见,方法中的变量不存在非线程安全问题,永远都是线程安全的。这是方法内部的变量时私有的特性造成的。
2.实例变量非线程安全
如果多个线程共同访问1个对象中的实例变量,则有可能出现”非线程安全“问题。
用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉情况。
我们将HasSelfPrivateNum类中的方法修改一下,
代码如下:
package test;
/**
* @Author LiBinquan
*/
public class HasSelfPrivateNum {
private int num =0;
public void addNum(String userName){
try{
if ("a".equals(userName)){
num = 100;
System.out.println("a set over");
Thread.sleep(1000);
}else{
num = 200;
System.out.println("b set over");
}
System.out.println(userName +" num = "+num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
输出结果:
由输出的内容我们可以看出,如果两个线程同时操作业务对象中的实例变量,则有可能出现”非线程安全“问题。这边我们只需要将HasSelfPrivateNum类中的方法加上synchronized即可。
代码如下:
package test;
/**
* @Author LiBinquan
*/
public class HasSelfPrivateNum {
private int num =0;
synchronized public void addNum(String userName){
try{
if ("a".equals(userName)){
num = 100;
System.out.println("a set over");
Thread.sleep(1000);
}else{
num = 200;
System.out.println("b set over");
}
System.out.println(userName +" num = "+num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
输出:
结论:在两个线程范文同一个对象中的同步方法时一定是线程安全的。
3.多个对象多个锁
这个我们还是以上面的测试代码为例,我们只需要将输出类改为:
代码如下:
package test;
/**
* @Author LiBinquan
*/
public class Run {
public static void main(String[] args) throws InterruptedException {
HasSelfPrivateNum num1 = new HasSelfPrivateNum();
HasSelfPrivateNum num2 = new HasSelfPrivateNum();
ThreadTest1 threadTest1 = new ThreadTest1(num1);
threadTest1.start();
ThreadTest2 threadTest2 = new ThreadTest2(num2);
threadTest2.start();
}
}
输出:
由上输出可得出,两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以一部的方式运行的。这个测试用例中创建了2个业务对象,在系统中产生出2个锁,所以运行结果是异步的,打印的效果就是先打印b,然后打印a。
从上面程序运行结果看,虽然在HasSelfPrivateNum中使用了synchronized关键字,但打印的顺序确实不同步的,是交叉的。
从这边可以看出synchronized取得的锁都是对