zoukankan      html  css  js  c++  java
  • 线程安全的概念和Synchronized(读书笔记)

         并行程序开发的一大关注重点就是线程安全,一般来说,程序并行化为了获取更多的执行效率,但前提是,高效率不能以牺牲正确性为代价,线程安全就是并行程序的根本和根基.volatile并不能真正保证线程安全,他只能确保一个线程修改了数据后,其他线程能够看到这个改动!
    public class AccountingVol implements Runnable {
        static AccountingVol instance = new AccountingVol();
        static volatile int i = 0;
    
        public static void increase() {
            i++;
        }
    
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            for (int j = 0; j < 10000000; j++) {
                increase();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
            t1.start();t2.start();
            t1.join();t2.join();
            System.out.println("i = " + i);
        }
    }
     
    上面代码显示了一个计数器,两个线程同时对i进行累加操作,各执行10000000次.我们希望得到的结果是20000000,但事实并非总是如此,得到的i总是小于预期结果!这就是线程不安全的恶果.
         为了解决这个问题Java提供了 synchronized来实现这个功能. 
    • synchronized的作用是实现线程间的同步问题,他的工作时对同步的代码加锁.使得每一次,只能有一个线程进入同步块,从而保证线程间的安全性,
    关键字synchronized可以有很多用法,
    • 指定加锁对象:对给定加锁.进入同步代码前要获得给定对象的锁.
    • 直接作用于实例方法,相当于对当前实例加锁,进入同步代码前要获得当前实例的锁
    • 直接作用于静态方法,.相当于对当前类加锁,进入同步代码前要获得当前类的锁
    我们队上边的例子修改,让他线程安全:
    public class AccountingSync implements Runnable {
        static AccountingSync instance = new AccountingSync();
        static int i = 0;
    
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            for (int j = 0; j < 10000000; j++) {
                synchronized (instance) {
                    i++;
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
            t1.start();t2.start();
            t1.join();t2.join();
            System.out.println("i = " + i);
        }
    }
    
    //上述代码还可以写成下面的形式:
    public class AccountingSync2 implements Runnable {
        static AccountingSync2 instance = new AccountingSync2();
        static int i = 0;
    
        public synchronized void increase() {
            i++;
        }
    
        @Override
        public void run() {
            for (int j = 0; j < 10000000; j++) {
                increase();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
            t1.start();t2.start();
            t1.join();t2.join();
            System.out.println("i = " + i);
        }
    }
    
    //一种错误的同步方式如下:
    public class AccountingSyncBad implements Runnable {
    
        static int i = 0;
    
        public synchronized void increase() {
            i++;
        }
    
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            for (int j = 0; j < 10000000; j++) {
                increase();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(new AccountingSyncBad());
            Thread t2 = new Thread(new AccountingSyncBad());
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println("i = " + i);
        }
    }
     
    虽然我们对increase()方法 做了同步处理,但是2个线程指向的是不同的实例.换言之就是.两个线程使用的是两把不同的锁.因此无法保证线程安全
    修改如下:

    public static synchronized void increase() {
        i++;
    }
     
    这样increase()方式就是类方法,而不是实例方法,因此线程还是可以同步的.
     
         除了用于线程同步,确保线程安全之外,synchronized还可以确保线程间的可见性和有序性.从可见性角度上讲,synchronized可以完全替代volatile的功能,只是使用上没有那么方便,就有序性而言,由于synchronized限制每一次只能有一个线程可以访问同步快,.因此 无论同步块内代码如何被乱序执行,只要确保串行语义一致,那么执行结果总是一样的.而其他访问线程.又必须在获得锁后方能进入代码块读取数据,因此,它们看到的最终结果并不取决于代码的执行过程,从而有序性问题自然得到了解决,换言之,被synchronized限制了多个线程是串行执行的.
  • 相关阅读:
    SDOI2011古代朱文
    LIS 堆优化
    ZR2019 广州 游记
    LG2709 小B的询问
    [SCOI2009] 生日礼物
    [SDOI2008]沙拉公主的困惑
    [LG3396]哈希冲突
    ZROI2018.8.2 菜鸡互啄杯组队 ACM 赛
    ZROI 菜鸡互啄杯 III
    [LG4016] 负载平衡问题
  • 原文地址:https://www.cnblogs.com/ten951/p/6171031.html
Copyright © 2011-2022 走看看