zoukankan      html  css  js  c++  java
  • 《JAVA并发编程实战》示例程序 第三章

     3.1 可见性

     

    程序清单3-1 在没有同步的情况下共享变量(不要这么做)

    /**
     * 主线程和读线程都将访问共享变量:ready 和 number
     * 结果可能
     * 1、 主线程先运行完,读线程后运行:读线程在控制台打印输出42,
     * 2、 读线程先执行完,主线程后执行:读线程在控制台打印输出0
     * 3、 NoVisibility根本无法终止,读线程可能永远都看不到ready的值
     * 4、 重排序(Reordering):读线程看到了主线程中顺序靠后写入的ready的值,但没看到主线程先写入的number的值
     */
    @NotThreadSafe
    public class NoVisibility {
        private static boolean ready;
        private static int number;
        
        //读线程
        private static class ReaderThread extends Thread {
            @Override
            public void run() {
                while(!ready)
                    Thread.yield();
                System.out.println(number);
            }
        }
        
        public static void main(String[] args) {
            new ReaderThread().start();
            number = 42;
            ready = true;
        }
    }

    程序清单3-2  非线程安全的可变整数类

    //mutable 英[ˈmju:təbl] adj.    易变的,性情不定的;
    @NotThreadSafe
    public class MutableInteger {
        
        private int value;
        
        public int getValue() { return value; }
        
        public void setValue(int value) { this.value = value; }
    }

    程序清单3-3  线程安全的可变整数类

    @ThreadSafe
    public class SynchronizedInteger {
        
        @GuardedBy("this")private int value;
        
        public synchronized int getValue() { return value; }
        
        public synchronized void setValue(int value) { this.value = value; }    
    }

    程序清单3-4 数绵羊

    /**
     * volatile通常用作某个操作完成、发生中断或者状态的标志
     * volatile语义不足以确保递增操作(count++)操作的原子性
     * asleep 状态量一旦在某个线程中被更改了,其它线程也应该看到
     * */
    @ThreadSafe
    public class CountSheep {
        
        volatile boolean asleep;
        
        public void countSomeSheep() {
            while(!asleep) {
                for (int i = 0; i < 100; i++) {
                    System.out.println("sheep"+i);
                }
            }
        }
    }

    3.2 发布与逸出

    程序清单3-5   发布一个对象

    /**
     * “发布”一个对象的意思是:是对象能够在当前作用域之外的代码中使用。
     * 1、将一个指向该对象的引用保存到其他代码可以访问的地方
     * 2、在某一个非私有的方法中返回该引用
     * 3、将引用传递到其他类的方法中
     * @author guchunchao
     *
     */
    public class PublicObject {
        
        private static Set<Object> knownSecret;
        
        public void initialize() {
            knownSecret = new HashSet<>(); //将方法体中new的对象发布到了方法体外的变量中
        }
    }

    程序清单3-6  使内部的可变状态逸出(不要这么做)

    /**
     * 按如下方式发布states,任何修改者都能修改这个数组的内容,不安全
     * @author gcc
     */
    public class UnsafeStates {
        private String[] states = {"aaa","bbb","ccc"};
        
        public String[] getStates() { return states; }
    }

    程序清单3-7  隐式地使this引用逸出(不要这么做)

    public class ThisEscape {
        public ThisEscape(EventSource source) {
            source.registerListener(
                new EventListener() {    //发布EventListener时,也隐含的将ThisEscape实例本身:this发布了
                    public void onEvent(Event e) {
                        doSomething(e);
                    }
                }
            );
        }
    }

    程序清单3-8  使用工厂方法来防止this引用在构造过程中逸出

    public class SafeListener {
        private final EventListener listener;
        private ThisEscape(EventSource source) {
            listener =     new EventListener(
                public void onEvent(Event e) {
                    doSomething(e);
                }
            );
        }
        
        public static SafeLisener newInstance(EventSource source) {
            SafeLisener safe = new SafeLisener();
            soure.registerListener(safe.listener);
        }
        
    }

    3.3  线程封闭

    程序清单3-9 基本类型的局部变量与引用变量的线程封闭性

    /**
     * 栈封闭
     * 由于任何方法都无法获得对基本类型的引用,因此基本类型的局部变量始终封闭在线程内
     * @author guchunchao
     *
     */
    public class BasicTypeStackClose {
        //candidate [ˈkændɪdət] 报考者; 申请求职者; 攻读学位者; 最后命运或结局如何已显然可见者;
        public int loadTheArk(Collection<Object> candidates) {
            SortedSet<Object> animals;
            int numPairs = 0;    //方法局部变量,始终在栈中
            Object candidate = null;
            
            animals = new TreeSet<Object>();
            animals.addAll(candidates);
            
            ++numPairs;
            return numPairs;    //即使被return了外界也无法修改。
        }
    }

    程序清单3-10  使用ThreadLocal来维持线程封闭性

    /**
     * ThreadLocal对象通常用于防止对可变的、非线程安全的单实例变量(Singleon)或全局变量进行共享。
     * ThreadLocal的get与set等访问接口或方法为每个使用该变量的线程都存有一份独立的副本,因此get总是能返回当前线程在调用set时设置的新值。
     * 
     * 单线程应用程序中维持一个全局的数据库连接,并在程序启动时初始化,从而避免在调用每个方法时都要传递一个Connection对象。
     * 
     * 而JDBC的连接对象不一定是线程安全的,因此当多线程应用程序在没有协同的情况下使用全局变量时,就不是线程安全的。
     * 将JDBC连接保存到ThreadLocal对象中,每个线程都会拥有属于自己的JDBC连接。
     * 
     * 更多ThreadLocal内容见:http://www.cnblogs.com/dolphin0520/p/3920407.html
     */
    public class ThreadLocalTemplate {
        private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
            public Connection initialValue() {
                return DriverManager.getConnection("DB_URL");  //JDBC的Connection不一定是线程安全的,封装到ThreadLocal中保证每个线程都会拥有自己的JDBC连接对象副本
            }
        };
       
        public static Connection getConnection() {
            return connectionHolder.get();
        }
    }

    3.4  不变性

    不可变对象一定是线程安全的! Why?

    程序清单3-11 在不可变对象基础上构建不可变类

    /**
     * final修饰的类:
     * 从字面意思来理解就是不会发生变化的类,那么是什么不会发生变化呢,其实就是类的状态,也就是不变类的实例一旦被创建,其状态就不会发生变化。
     * 举个例子:
     *         如果人是一个class,那么我们中的每一个都是人这个类的具体的instance,
     *         如果人这个类只有一个状态就是生身父母,那么它就是一个不变类,因为每一个人在出生的那一刹那,生身父母就已经被设置了值,而且终生都不会发生变化。
     * 不变类有什么好处呢?
     *        1) 不变类是线程安全的,由于不变类的状态在创建以后不再发生变化,所以它可以在线程之间共享,而不需要同步。
     *        。。。。*/
    @Immutable    //[ɪˈmju:təbl]不可改变的;
    public final class ThreeStooges {
        
        private final Set<String> stooges = new HashSet<>();//[stu:dʒ]喜剧里的配角或丑角,助手,伙伴;
        
        public ThreeStooges() {
            stooges.add("hello");
            stooges.add("world");
            stooges.add("hello world");
        }
        
        public boolean isStooge(String name) {
            return stooges.contains(name);
        }
    }

    程序清单3-12  对数值及因数分解结果进行缓存的不可变容器类

    /**
     * 对于在访问和更新多个相关变量是出现的竞态条件问题,可以通过将这些变量全部保存在一个不可变对象中来消除。
     * 如果是一个可变的对象,那么就必须使用锁来确保原子性。
     * 如果是一个不可变对象,那么当线程获得了对该对象的饮用后,就不必担心另一个线程会修改对象的状态。
     * 如果要更新这些变量,可以创建一个新的容器对象,但其他使用原有对象的线程仍然会看到对象处于一致的状态。
     * @author guchunchao
     *
     */
    @Immutable
    public class OneValueCache {//存缓存的容器类
        private final BigInteger lastNumber;
        private final BigInteger[] lastFactors;
        public OneValueCache(BigInteger lastNumber, BigInteger[] lastFactors) {
            this.lastNumber = lastNumber;
            this.lastFactors = lastFactors;
        }
        public BigInteger[] getLastFactors(BigInteger i) {
            if(lastNumber == null || lastNumber.equals(i))
                return null;
            else
                return Arrays.copyOf(lastFactors, lastFactors.length);
        }
    }
    
    
    //程序清单3-13  使用指向不可变容器对象的 volatile类型引用以缓存最新结果
    @ThreadSafe
    public class VolatileCacheFactorizer implements Servlet {
        
        private volatile OneValueCache cache = new OneValueCache(null, null);
    
        @Override
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
            BigInteger i = extractFromReqest(req);
            BigInteger[] factors = cache.getLastFactors(i);
            if(factors == null) {
                factors = factor(i);
                cache =  new OneValueCache(i, factors);
            }
            encodeIntoResponse(res,factors);
        }
    }

    3.5 安全发布

    程序清单3-14    在没有足够同步的情况下发布对象(不要这么做)

    //不安全的发布
    public Holder holder;
    
    public void initialize() {
         holder = new Holder(42);
    }
    

      

    程序清单3-15 由于未被正确发布,因此这个类可能出现故障

    //不安全的发布
    public class Holder {
        private int n;
        public Holder(int n) {
             this.n = n;  
        }  
       public void assertSanity() {
           if(n != n)
          throw new AssertionError("This Statement is false.");
       }
    }
    

      

      

  • 相关阅读:
    【SAS NOTE】OUTPUT
    【SAS NOTES】_NULL_
    【SAS NOTE】sas 9.2 安装
    【SAS NOTE】FREQ
    纯数学教程 Page 203 例XLI (1)
    纯数学教程 Page 203 例XLI (3)
    纯数学教程 Page 203 例XLI (2)
    Prove Cauchy's inequality by induction
    纯数学教程 Page 325 例LXVIII (15) 调和级数发散
    纯数学教程 Page 325 例LXVIII (15) 调和级数发散
  • 原文地址:https://www.cnblogs.com/guchunchao/p/10705137.html
Copyright © 2011-2022 走看看