zoukankan      html  css  js  c++  java
  • 线程安全

    一、类的线程安全定义

    (Doung Lee)如果多线程下使用这个类,不管多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的。

    类的线程安全表现为:

      操作的原子性

      内存的可见性

    不做正确的同步,在多个线程之间共享状态的时候,就会出现线程不安全。

    二、怎么才能做到类的线程安全

    1.栈封闭

    所有的变量都是在方法内部声明的这些变量都处于栈封闭状态(这样的话,就是安全的,因为方法是在栈针里面的,栈针又是线程级别的)

    2.无状态

    没有任何成员变量的类就叫无状态的类。

    3.让类不可变

    让状态不可变,两种方式:

    1). final关键字,对于一个类,所有的成员变量应该是私有的,同样的只要有可能,所有的成员变量应该加上final关键字,但是加上final,要注意如果成员变量又是一个对象时,这个对象所对应的类也要是不可变,才能保证整个类是不可变的。

    我们需要注意到的是,当成员变量被final修饰的时候,就只能有get()方法,没有set()方法;(例子:比如ConcurrentHashMap中的Node类)

    看一个代码事例:

    public class ImmutableFinalRef {
        
        private final int a;
        private final int b;
        private final User user;//这里,就不能保证线程安全啦
        
        public ImmutableFinalRef(int a, int b) {
            super();
            this.a = a;
            this.b = b;
            this.user = new User();
        }
    
        public int getA() {
            return a;
        }
    
        public int getB() {
            return b;
        }
        
        public User getUser() {
            return user;
        }
    
        public static class User{
            private int age;
    
            public User(int age) {
                super();
                this.age = age;
            }
    
            public int getAge() {
                return age;
            }
    
            public void setAge(int age) {
                this.age = age;
            }
            
        }
        
        public static void main(String[] args) {
            ImmutableFinalRef ref = new ImmutableFinalRef(12,23);
            User u = ref.getUser();
            //u.setAge(35);
        }
    }
    View Code

    看到上面的代码的时候,我们注意到在成员变量的对象,还是会被赋值的。只要下面的方式才行:

    package com.xiangxue.ch7.safeclass;
    
    
    /**
     *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
     *
     *类说明:看起来不可变的类,实际是可变的
     */
    public class ImmutableFinalRef {
        
        private final int a;
        private final int b;
        private final User user;//这里,就不能保证线程安全啦
        
        public ImmutableFinalRef(int a, int b) {
            super();
            this.a = a;
            this.b = b;
            this.user = new User();
        }
    
        public int getA() {
            return a;
        }
    
        public int getB() {
            return b;
        }
        
        public User getUser() {
            return user;
        }
    
        public static class User{
            private final int age;
    
            public User(int age) {
                super();
                this.age = age;
            }
    
            public int getAge() {
                return age;
            }
    
        }
        
        public static void main(String[] args) {
            ImmutableFinalRef ref = new ImmutableFinalRef(12,23);
            User u = ref.getUser();
            //u.setAge(35);
        }
    }
    View Code

    2). 根本就不提供任何可供修改成员变量的地方,同时成员变量也不作为方法的返回值。

    看例子。其实在成员变量加上final,是最好的

    public class ImmutetableToo {
        private List<Integer> list =  new ArrayList<>(3);
        
        public ImmutetableToo() {
            list.add(1);
            list.add(2);
            list.add(3);
        }
        
        public boolean isContains(int i) {
            return list.contains(i);
        }
    
    }
    View Code

    4.使用不可变的类

    不可变类(Immutable Objects):当类的实例一经创建,其内容便不可改变,即无法修改其成员变量。

    Java 中八个基本类型的包装类和 String 类都属于不可变类,而其他的大多数类都属于可变类。

    5.volatile

    保证类的可见性,最适合一个线程写,多个线程读的情景,(比如ConcurrentHashMap)

    6.安全的发布

    这个时候我们可以和 3 中的2 进行对比

    public class UnsafePublish {
        //要么用线程的容器替换
        //要么发布出去的时候,提供副本,深度拷贝
        private List<Integer> list =  new ArrayList<>(3);
        
        public UnsafePublish() {
            list.add(1);
            list.add(2);
            list.add(3);
        }
        
        //讲list不安全的发布出去了
        public List<Integer> getList() {
            return list;
        }
    
        //也是安全的,加了锁--------------------------------
        public synchronized int getList(int index) {
            return list.get(index);
        }
        
        public synchronized void set(int index,int val) {
            list.set(index,val);
        }    
        
    }
    View Code

    7.使用TheadLocal

  • 相关阅读:
    (转)测试经验交流
    关于软件质量和软件测试的一点点看法 (转)
    提取Chrome插件为crx文件
    [转]用星际快速入门PHP面向对象编程
    函数式编程js学习的进阶
    asp.net程序就是IIS的插件
    文档单一化、版本化
    NuGet
    Linux服务器程序编程的几个坎
    webform也是一种mvc
  • 原文地址:https://www.cnblogs.com/lys-lyy/p/11107117.html
Copyright © 2011-2022 走看看