目录
实例变量共享导致的“非线程安全问题”
如何解决这个问题?
i--与System.out.println()出现引起的“非线程安全问题”
非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。
实例变量共享导致的“非线程安全问题”
首先,自定义线程类中的实例变量针对其他线程可以是共享的,也可以是不共享的。
public class MyThread extends Thread{
private int count = 5;
public MyThread(String name){
super();
this.setName(name);//设置线程名称
}
@Override
public void run(){
super.run();
while (count>0){
count--;
System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
}
}
}
public class Run {
public static void main(String[] args) {
MyThread a = new MyThread("A");
MyThread b = new MyThread("B");
MyThread c = new MyThread("C");
a.start();
b.start();
c.start();
}
}
运行结果:
也就是说,一共创建了3个线程,每个线程有自己的count变量,在执行过程中自己减少自己的count变量的值。这也就表明当前情况是线程不共享的情况。
public class MyThread extends Thread{
private int count = 5;
@Override
public void run(){
super.run();
count--;
System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
}
}
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread a = new Thread(myThread,"A") ;
Thread b = new Thread(myThread,"B") ;
Thread c = new Thread(myThread,"C") ;
a.start();
b.start();
c.start();
}
}
运行结果:
出现两个3,说明,A和B同时对count进行了处理,产生了“非线程安全”问题。
此时为什么会出现“非线程安全”问题呐?
原因在于:在某些JVM中,count--操作要分解成3步去执行:(1)获取到原有的count值。(2)计算count-1。(3)对count进行重新赋值。在这个过程中可能会被其他线程打断。也就是说,在这三个步骤中,如果有多个线程同时访问,那么很大概率会出现“非线程安全”问题。
i--操作会出现以上的问题,同理i++操作也同样会出现以上的问题。
以上出现非线程安全的情况是多个线程操作同一个对象的同一个实例变量,导致值不准确。
如何解决这个问题?
这个时候我们就需要进行上锁了。
在run方法前加上synchronized关键字,这样可以使多个线程在执行run方法时,“排队”执行。
如果一个线程要执行run方法,首先要判断run方法是否上锁,如果上锁,说明有其他线程在调用run方法,这个线程就要进行等待,等待其他线程将run方法调用结束后,才能继续调用run方法。如果没有上锁,那么直接调用。
当一个线程想要执行同步方法里面的代码,它会首先尝试去拿到这把锁,如果能拿到,那么该线程就会执行同步方法中的代码。如果不能拿到,那么这个线程就会不断的去尝试拿到这把锁,直到它成功拿到了这把锁为止。
i--与System.out.println()出现引起的“非线程安全问题”
public class MyThread extends Thread{
private int i = 5;
@Override
public void run() {
System.out.println("i="+(i--)+" threadName="+Thread.currentThread().getName());
}
}
public class Run {
public static void main(String[] args) {
MyThread run = new MyThread();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
Thread t4 = new Thread(run);
Thread t5 = new Thread(run);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
运行结果:
这里输出了2个5,也就是说,这里也出现了非线程安全问题。
println的源码:
虽然println方法在内部是synchronized同步的,但是i--操作是在进入println之前发生的,所以依旧有可能出现非线程安全问题。
这也就告诉我们:在synchronized之前执行的代码也有可能是不安全的。