Java并发编程—错误的加锁方式和volatile

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

Java并发编程—Java多线程基本概念

Java并发编程—线程间的共享

1、错误的加锁方式导致的线程不安全

上篇文章我们写的是Java线程间的共享,及共享的时候出现的线程安全问题,也分析了怎么解决线程安全问题,介绍了对象锁synchronized的使用,但是错误的使用方法,就算你用了synchronized,它也是不安全的



    private static class Worker implements Runnable{

        private Integer i;

        public Worker(Integer i) {
            this.i=i;
        }

        @Override
        public void run() {
            synchronized (i) {
                i++;
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

        }

    }
    public static void main(String[] args) throws InterruptedException {
        Worker worker=new Worker(1);
        for(int i=0;i<5;i++) {
            new Thread(worker).start();
        }
    }
复制代码

上面的代码我们使用了对象锁,但是我们多运行几次,发现运行的结果和我们想象的并不一样,发生了线程不安全,这是为什么呢?
我们就看看这个类编译完之后都干了一些什么了不得的事

image.png
看到没,它++的时候,用了Integer的valueOf方法,nice,咱们就去看一眼valueOf方法

image.png
看完这个方法,再结合我们上篇文章所说的知识,明白没。也就是说每次我们Worker里的Integer++的时候,他都会返回一个新new出的Integer,也就说我们锁的对象一直在变,所以线程在锁变化的时候,就极有可能产生了不安全

2、volatile

volatile关键字,最轻量的同步机制
volatile只能保证操作的可见性,不能保证操作的同步性,如果我们不加volatile关键字,我们线程之间操作对象的变量是不可见的,什么是不可见的呢
上代码

private  static boolean ready;
    private static int number;

    private static class PrintThread extends Thread{
        @Override
        public void run() {
            System.out.println("PrintThread is running.......");
            while(!ready);
            System.out.println("number = "+number);
        }
    }

    public static void main(String[] args) {
        new PrintThread().start();
        SleepTools.second(1);
        number = 51;
        ready = true;
        SleepTools.second(5);
        System.out.println("main is ended!");
    }
复制代码

image.png
上面的代码执行结果,子线程启动后,在主线程里更改了ready和number 的值,子线程并没有停止运行而且也没收到number的值,这就是操作的不可见性
看看我们加了volatile之后的运行结果

private  static volatile boolean ready;
    private static volatile int number;
复制代码

image.png
我们主线程操作number和ready子线程可见,也就结束了子线程的执行打印出来number

3、总结

synchronized它的作用是同步代码块,但是错误的使用方式,也是没有作用的,volatile是最轻量级的同步机制,但是只能保证操作的可见性,保证不了操作的同步性,想要实现操作的同步性,还得用synchronized!如果有不对的地方,请大佬们在评论区指出,我会第一时间进行改正,希望大佬们一键三连!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享