zoukankan      html  css  js  c++  java
  • Java单例模式

    单例模式之饿汉模式:

    /**
     * 
     * @author Taowd
     * 功        能:单例模式,饿汉模式,线程安全,效率比较低
     * 优点:写法简单,线程安全
     * 缺点:加载速度比较慢,某些特定情况下会耗费内存
     * 编写时间:2017-5-11-上午8:37:37
     */
    public class Singleton2 {
    
        // 私有化构造参数
        private Singleton2() {
    
        }
    
        // 将自身实力对象设置一个属性,并加上static和final修饰符
        private static final Singleton2 singleton2 = new Singleton2();
    
        // 静态方法返回该类的示例
        public static Singleton2 getInstance() {
            return singleton2;
        }
    }

    单例模式之懒汉模式:

    package com.taowd.singleton;
    
    /**
     * 
     * @author Taowd
     * 功能:单例模式,懒汉模式(饱汉模式),非线程安全的
     * 优点:编写简单,类加载速度快,使用速度慢
     * 缺点:并发环境下可能出现多个Singleton实例
     * 编写时间:2017-5-11-上午8:27:25
     */
    public class Singleton1 {
        // 把构造方法私有化,防止通过new Singleton() 去实例化
        private Singleton1() {
    
        }
    
        // 定义一个Singleton类型的示例,不进行初始化,注意这里没有使用final关键字
        private static Singleton1 singleton;
    
        // 定义一个静态方法,外部调用时再初始化Singleton,但是多线程访问时可能造成重复初始化的问题
        public static Singleton1 getInstance() {
            if (singleton == null) {
                singleton = new Singleton1();
            }
            return singleton;
        }
    }

    单例模式之终极模式:

    package com.taowd.singleton;
    
    /**
     * 
     * @author Taowd
     * 功能:最终版,最优方案
     * 优点:内存占用低,效率高,线程安全,多线程操作原子性
     * 编写时间:2017-5-11-上午8:43:08
     */
    public class Singleton {
        // 把构造方法私有化,防止通过new Singleton() 去实例化
        private Singleton() {
    
        }
    
        // 定义一个Singleton类型的示例,不进行初始化,注意这里没有使用final关键字
        // volatile 保证了多线程访问instance变量的可见性,避免了instance初始化时,其他变量属性还没赋值完就被另外线程调用
        // volatile:
        private static volatile Singleton instance;
    
        // 定义一个静态方法,外部调用时再初始化Singleton,但是多线程访问时可能造成重复初始化的问题
        public static Singleton getInstance() {
            // 对象实例化与否判断(不适用同步代码,instance不等于null时,直接返回对象,提高执行效率)
            if (instance == null) {
                // 同步代码块(对象未初始化时,使用同步代码,保证多线程访问对象在第一次被创建后,不再重复被创建)
                // synchronized:
                synchronized (Singleton.class) {
                    // 未初始化,则初始化instance变量
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    单例模式的测试程序:

    package com.taowd.test;
    
    import org.junit.Test;
    
    import com.taowd.singleton.Singleton1;
    
    public class SingletonTest {
    
        @Test
        public void TestSingleton() {
            Singleton1 singleton1 = Singleton1.getInstance();
            Singleton1 singleton2 = Singleton1.getInstance();
            System.out.println(singleton1 == singleton2 ? "同一对象" : "不是同一对象");
        }
    }

      以前对单例模式不是很清楚,通过这几个例子也学习了一下单例模式,其中对volatile关键字的含义的不是很明白,因为很少用到这个关键字,查阅了一下资料下面将我的查阅的信息简单说明一下。

      volatile关键字可以保证可见性和执行顺序,可见性既是先发生的原子修改操作一定会在读操作之前执行完成(同一时间对volatile变量只能有一个操作);执行顺序的含义既是volatile关键字会阻止编译器或JVM对代码重排序,或把volatile变量从同步区域(synchronized方法,synchronized代码块..)中移除出来。

    Java的内存模型

    Java的内存模型(JMM)可以保证一个线程修改了某个变量,该变量的改变对另一个线程是可见的,也就是说另一个线程取得的这个变量的值,一定是前一个线程修改之后的值。这个也被称作"happens-before" 。

    volatile关键字使用场所

    volatile关键字只能修饰变量,不能修饰类,也不能修饰方法。

    想要把某个变量共享,该变量的读写操作必须是原子性的,并用volatile关键字修饰。

    • volatile修饰的long和double类型的变量读写操作是原子性的。long和double都是64位的,给long和double类型的变量赋值跟平台相关,在有些平台上不是原子操作。很多平台给long和double变量赋值需要2步操作,每一步只写32位,在这2个步骤之间,其他线程获取的long或double类型的变量的值的状态是不正确的。

    • volatile变量有类似synchronized同步代码的可见性,即每个线程读取到的都是最新更新的值。volatile的局限性很大,你只能获取volatile变量的值,直接设置volatile变量的值,不能比较之后再设置volatile的值,因为在你做比较操作的区间很有可能有其他线程修改了该volatile变量的值。

    • 被volatile关键字修饰,表明该变量是要被多个线程访问的,编译器不会对与volatile变量相关的代码做重排或其他多线程下不允许的优化。没有被volatile关键字修饰,编译器会重排代码,缓存变量的值,减少从主存中直接获取变量的值。

    用例子来详细彻底说明Java的volatile关键字工作原理

    不用volatile关键字修饰,有可能某个线程可以获取到另一个线程设置的isActive的值,编译器有可能缓存了isActive的值等等,使用volatile关键字可以避免这些情况。

    • 双重检查单例模式在JDK4是有问题的,volatile可以修复这个问题。本文的《第一个例子说明volatile变量的含义》中举的例子就是说这个问题。

    volatile关键字的总结

    1. volatile关键字只用修饰变量,不能修饰方法和类。

    2. volatile变量的值都是从主存中获取的,而不是从线程的本地内存。

    3.long和double变量被volatile关键字修饰之后,读写(赋值操作,读取操作)都是原子操作.

    4. 使用volatile关键字可以避免内存不一致的错误;写入volatile变量一定会比接下来的读操作先发生。5. 从jdk5开始对volatile变量的修改对其他的线程都是可见的;当线程读取volatile变量的时候,会先把其他线程中缓存着的volatile变量(如果还没有更新到主存中的时候)强制写入到主存。

    6. 除了long和double其他的基本类型读写操作都是原子性的;引用类型的读写操作也是原子性的。

    7. volatile变量只能做简单的读写,没有锁,没有阻塞。

    8.volatile变量可以是空的.

    9. volatile不能保证原子性,比如volatile修饰的int变量++操作还是非原子的。

    10. 变量没有在多个线程之间共享,没有必要做任何同步的操作,比如使用volatile关键字修饰。

    synchronized和volatile关键字的比较

    volatile关键字代替不了synchronized关键字,不过在某些场合可以作为替代方案。

    1. volatile关键字只能修饰字段,而synchronized只能修饰代码块和方法。

    2. synchronized关键字需要获得锁释放锁,volatile关键字不需要。

    3.synchronized代码块或方法在等待锁的时候会被阻塞;volatile不是这样的。

    4. synchronized代码块或方法会比volatile关键字更影响性能。

    5. volatile关键只同步被修饰的变量,而synchronized关键字却同步代码块或方法中所有的变量,并且还会获得锁释放锁,所以synchronized的负载更大。

    6. 不能同步(synchronized)null对象,而volatile变量可以是null的。

    7. 读取volatile变量效果等同获取锁,写入volatile变量效果等同释放锁。

  • 相关阅读:
    201720181 JaWorld 第三周作业
    201720181 Java小组1623 第一周作业
    201720181 JaWorld 第四、五周作业
    201720181 Java小组1623 第二周作业
    无法打开用户默认数据库的解决方法
    技术网站/博客网址收藏
    .net中防止用户多次登录的方法
    asp.net导出excel方法总结
    面向对象设计模式之Bridge桥接模式(结构型)
    面向对象设计模式之Composite组合模式(结构型)
  • 原文地址:https://www.cnblogs.com/taowd/p/6903672.html
Copyright © 2011-2022 走看看