synchronized关键字学习之并发变程中的三个问题

307 阅读3分钟

深入学习并发编程中的synchronized

第一章:并发编程中的三个问题

可见性

学习什么是可见性问题

可见性概念

可见性(Visibility):是==指一个线程对共享变量进行修改,另一个先立即得到修改后的最新值==。

可见性演示

案例演示:一个线程根据boolean类型的标记flag, while循环,另一个线程改变这个flag变量的值,另一个线程并不会停止循环。

package visablity;
public class VisablityTest {
    /**
     * 目标演示可见性问题
     * 1.创建一个共享变量,
     * 2、创建多条线程修改和读取共享变量
     */
    private static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (flag) {
                // System.out.println("this is  thread1");,这里不能加上输出语句
            }
        }, "Thread1").start();

        //主线程休眠两秒,两秒之后启动新线程来讲我们的flag变为false;观察上面的线程是否还在继续执         //行,如果继续执行则就说明了上面的线程对我们的flag中的值变化不可见
        Thread.sleep(2000);

        new Thread(() -> {
            flag = false;
            System.out.println("线程修改了变量的值为false");
        }, "Thread2").start();
        
         //Thread1的循环一直在执行,没有中断,这就是并发变成中的可见性问题
         
    }
}

1587731177848

并发编程时,会出现可见性问题,当一个线程对共享变量进行了修改,另外的线程并没有立即看到修改后的最新值。
原子性

目标

学习什么是原子性问题

原子性概念

原子性(Atomicity):在一次或多次操作中,==要么所有的操作都执行并且不会受其他因素干扰而中断,要么所有的操作都不执行==。

案例演示:

package atomic;

import java.util.ArrayList;
import java.util.List;

public class AtomicDemo {
    //定义一个共享变量number
    //对number进行1000次++操作
    //使用5个线程来进行
    private static int number = 0;

    public static void main(String[] args) throws InterruptedException {
        List<Thread> list = new ArrayList<>();

        Runnable runnable = () -> {
            for (int j = 0; j < 1000; j++) {
                //让线程休眠,使的线程在这期间不能做任何的事情,当我从其他线程切换回当前的线程的时                 //候,从原来的起点开始执行但是大人时代变了
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                number++;

            }
        };

        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(runnable);
            t.start();
            list.add(t);
        }

        for (Thread thread : list) {
            //等待线thread程死亡(执行结束)
            //join方法其实就是阻塞当前调用它的线程,等待join执行完毕,当前线程继续执行
            thread.join();
        }
        Thread.sleep(1000);
        System.out.println("number is :" + number);
    }
}

​ 通过反汇编可以知道,number++是由多条语句组成,多条指令在一个线程的情况下是不会出问题的,但是在多线程情况下就可能会出现问题。比如一个线程在执行 时,另一个线程又执行 。会导致两次,实际上只加了1。

小结

并发编程时,会出现==原子性问题,当一个线程对共享变量操作到一半时,另外的线程也有可能来操作共享变量==,干扰了前一个线程的操作。

有序性

程序代码在执行过程中的先后顺序,由于Java在编译期以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码时的顺序。