• 线程安全的概念和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限制了多个线程是串行执行的.
  • 相关阅读:
    Android的LinearLayout中orientation默认值为什么是HORIZONTAL
    Android中HttpURLConnection对象是怎么生成的
    记一个擦除硬盘数据,防止已删除文件被恢复的程序
    添加一个Android框架层的系统服务与实现服务的回调
    在 Activity 中实现 getContentView 操作
    (01)明明配置了log4j.properties为什么还是不打印日志
    (05)pom.xml文件报错web.xml is missing and <failOnMissingWebXml> is set to true
    (04)maven中的几个常用插件
    (03)开发环境eclipse、myEclipse本地tomcat调试发布maven项目遇到的糟心事
    (04)Storm与Kafka结合使用简单案例
  • 原文地址:https://www.cnblogs.com/ten951/p/6171031.html
Copyright © 2020-2023  润新知