操作系统调度线程时是随机的,抢占式,因为其随机性导致了bug,就说这个线程是不安全,若没有bug,则说这个线程是不安全。
例如:有一个变量,两个线程对其进行自增操作->导致了线程不安全。
class Counter{
public int count=0;
public void increase(){
count++;
}
}
public class TextDemo1 {
public static void main(String[] args) throws InterruptedException {
final Counter counter=new Counter();
Thread t1=new Thread(()->{
for (int i = 0; i < 5000; i++) {
counter.increase();
}
});
Thread t2=new Thread(()->{
for (int i = 0; i < 5000; i++) {
counter.increase();
}
});
t1.start();
t2.start();
t1.join();//t1.join( )作用是等待t2开始执行,然后执行t1.若是不加join(),则t1,t2互相等待,而不执行,count就等于0了。
t2.join();
System.out.println(counter.count);
}
}
输出:
7771
Process finished with exit code 0
原因:t1和t2各自让count自增5000次,输出应是10000。
但是由于t1和t2抢占式,随机性的执行。导致结果在5000-10000之间。
(1):线程是抢占式执行,线程之间充满随机性:
方案:没啥办法,这坑货。无能为力。
(2):多个线程对同一个变量进行修改。
如上述例子出现的count原应加到10000,但但直加到5000-10000的原因。
解决方案:加锁,使其串行化执行。
//抢占式执行修改代码案例
class Counter2{
public int count=0;
synchronized void increase(){
count++;
}
}
public class TextDemo2 {
public static void main(String[] args) throws InterruptedException {
final Counter2 counter=new Counter2();
Thread t1=new Thread(()->{
for (int i = 0; i < 5000; i++) {
counter.increase();
}
});
Thread t2=new Thread(()->{
for (int i = 0; i < 5000; i++) {
counter.increase();
}
});
t1.start();
t2.start();
t1.join();//t1.join( )作用是等待t2开始执行,然后执行t1.若是不加join(),则t1,t2互相等待,而不执行,count就等于0了。
t2.join();
System.out.println(counter.count);
}
}
(3)、针对变量的操作不是原子的。
什么是原子性:一种操作对应一条机器指令。
若如上面所说的count++,一个加加操作对应了三条指令,就不是原子的。
解决方案:也是加锁:“synchronized”,意味着把这三条指令打包成了一组指令,然后把这一组指令看出成一条指令了,类似于数学里的“整体代换”思想。
(4)、内存可见性导致线程不安全。指:(1)、针对同一变量,一个线程t1 进行读操作,一个线程t2 进行写操作。t1频繁的读取t2的写出来的数据,读着读着t1,开始偷懒,没读到 t2突然修改的数值。
如图:
解决方案:还是给锁上,相当于用synchronizes,或者volatile给丫监视着(监工),不准偷懒!!
class Counter3{
public volatile int flag=0; //保证内存的可见性
}
public class TextDemo3 {
public static void main(String[] args) {
Counter3 counter3=new Counter3();
Thread t1=new Thread(()->{
while (counter3.flag==0){
}
System.out.println("循环结束");
});
Thread t2=new Thread(()->{
Scanner scanner=new Scanner(System.in);
System.out.println("输入一个整数");
counter3.flag=scanner.nextInt();
});
t1.start();
t2.start();
}
}
(5)、指令重排序,导致不安全
指:我们写的几条代码的前后顺序,不影响代码的最终结果时,编译器人工智能的调整了代码的先后顺序,以使得执行更高效的操作。但是在多线程环境操作下,他丫的人工智障的也把先后顺序给调了,此时就产生了各个线程的无序操作。
书面解释:
“编译器对于指令重排序的前提是 “保持逻辑不发生变化”. 这一点在单线程环境下比较容易判断, 但是在多线程环境下就没那么容易了, 多线程的代码执行复杂程度更高, 编译器很难在编译阶段对代码的执行效果进行预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价.”
解决方案:还是加锁,这次加锁,synchronized相当于排个领导,要求"别个我犯傻,按照公司章程办事,禁止重排序。”



