• Java并发编程实战笔记—— 并发编程2


    1、ThreadLocal

      Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作。因此,尽管有两个线程同时执行一段相同的代码,而且这段代码又有一个指向同一个ThreadLocal变量的引用,但是这两个线程依然不能看到彼此的ThreadLocal变量域。ThreadLocal类提供了get和set等访问接口,这些方法为每个线程都存有一个独立的副本,因此每次get总是返回当前线程上一次最小使用set设置的值。

    private ThreadLocal myThreadLocal = new ThreadLocal();

      这样实例化一个ThreadLocal类,每个线程仅需要实例化一次即可。

    myThreadLocal.set("A  local value");
    String threadLocalValue = (String) myThreadLocal.get();

      这样使用set和get来存储和读取一个String对象,因为get返回的是一个Object对象,所以需要强制类型转换,set()方法则依赖一个Object对象参数。

      为了不使用强制类型转换可以使用泛型来实例化一个ThreadLocal对象。

    private ThreadLocal myThreadLocal1 = new ThreadLocal<String>();

      这样读取的时候就不需要进行强制类型转换了

    myThreadLocal1.set("Hello ThreadLocal");
    String threadLocalValues = myThreadLocal.get();

      然后我们还可以可以通过ThreadLocal子类的实现,并覆写initialValue()方法,就可以为ThreadLocal对象指定一个初始化值。

    private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
       @Override protected String initialValue() {
           return "This is the initial value";
       }
    };

      这样指定的初始值对所有线程都是可见的,在set方法被调用之前所有线程读取到的都将是这个初始值。

      一个完整的ThreadLocal示例

    class myThread implements Runnable{
        private ThreadLocal myThreadLocal = new ThreadLocal<String>(){
            @Override
            protected String initialValue() {
                return "a Initialization value ";
            }
        };
    
        @Override
        public void run() {
            System.out.println(myThreadLocal.get());
            myThreadLocal.set("" + Math.random());
            System.out.println(myThreadLocal.get());
        }
    }
    
    
    public class Fun {
    
        public static void main(String[] args) {
            myThread myRunnabe = new myThread();
            Thread myThread1 = new Thread(myRunnabe);
            Thread myThread2 = new Thread(myRunnabe);
    
    
            myThread1.start();
            myThread2.start();
    
        }
    }

      输出:

    a Initialization value 
    a Initialization value 
    0.3672775208272421
    0.5694837916871707

    2、不变性

      如果某个对象在创建之后就不能被修改,那么这个对象就被称为不可变对象。因为其状态将不会被改变,所以不同线程访问该对象的时候是安全的

      满足以下条件的对象是不可变的:

    • 对象在创建以后其状态就不能被修改
    • 对象的所有域都是final类型的
    • 对象是正确创建的

      在可变对象基础上构建不可变对象

        public final class Aclass{
            private final Set<String> set = new HashSet<String>();
            public Aclass(){
                set.add("aaa");
                set.add("bbb");
                set.add("ccc");
            }
            public boolean isSet(String word){
                return set.contains(word);
            }
        }

      这样一个类是可变,它的set对象是可变的,但是这个对象在创建之后就是不可改变的了,对于final类型的set对象只能访问不能被修改。

    3、安全发布

      在某些情况下我们希望在多个线程之间共享对象,此时必须保证安全的进行共享。

    public Holder holder;
    public void init(){
        holder = new Holder(42);
    }

      这个holder看起来是一个不可变对象,但其实它并不是,他并不满足“对象是正确创建的”这个条件,某些线程将会看到尚未创建完场的对象

      可变对象必须通过安全的方式来进行发布,这通常都意味着在发布和使用该对象的线程都必须使用同步。

      要安全得发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见,一个正确构造的对象可以通过以下方式来正确发布:

    • 在静态初始化函数中初始化一个对象引用
    public static Holder holder = new Holder(42); 
    • 将对象的引用保存到volatile类型的域或者AtomicReferance对象中
    • 将对象的引用保存到某个正确构造对象的final类型中
    • 将对象得引用保存到一个由锁保护的域中

      在线程安全库中的容器提供以下的安全发布保证:

    • 通过将一个键或者值放入Hashtable、synchronizedMap或者ConcurrentMap中,可以安全地将它发布给任何从这些容器中访问它的线程(无论是直接访问还是通过迭代器访问)
    • 通过将某个元素放入Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList或synchronizedSet中,可以将该元素安全地发布到任何从这些容器中访问该元素的线程
    • 通过将某个元素放入BlockingQueue或者ConcurrentLinkedQueue中,可以将该元素安全地发布到任何从这些队列中访问该元素的线程。

      事实不可变对象:如果对象从技术上来看是可变的,但其状态在发布以后就不会在改变,那么把这种对象称为“事实不可变对象”,刚刚的holder就是一个事实不可变对象。

      所以最后对象的发布需求取决于它的可变性:

    • 不可变对象可以通过任意机制发布
    • 事实不可变对象必须通过安全方向进行发布
    • 可变对象必须通过安全方式进行发布,并且必须是线程安全的或者由某个锁保护起来的

      在并发程序中使用和共享对象时,可以使用以下一些实用的策略:

    • 线程关闭:对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改
    • 只读共享:任何线程都不能修改的对象。
    • 线程安全共享:在线程内部实现同步。
    • 保护对象:通过持有特定的锁才能访问
  • 相关阅读:
    Permutations II
    N-Queens II
    Palindrome Number
    Minimum Path Sum
    JS的DOM操作2
    JS 的DOM操作
    函数概念
    JavaScript数组
    JavaScript循环及练习
    JS语言
  • 原文地址:https://www.cnblogs.com/xxbbtt/p/7878167.html
Copyright © 2020-2023  润新知