synchronized在单例模式中的使用

在单例模式中有一种懒汉式的单例,就是类初始化的时候不创建对象。等第一次获取的时候再创建对象。这种单例在单线程下是没有问题的获取的也都是同一个对象。但是如果放入多线程中就会获取多个不同对象问题。

1、首先来看一个懒汉式的单例模式:

1
2
3
4
5
6
7
8
9
10
11
//懒汉式的单例类
class MyJvm{
    private static MyJvm instance = null;
    private MyJvm(){}
    public static MyJvm getInstance(){
        if (instance == null) {
            instance = new MyJvm();        
        }
        return instance;
    }
}

测试类:

1
2
3
4
5
6
7
public static void main(String[] args) {
    MyJvm jvm1 = MyJvm.getInstance();
    MyJvm jvm2 = MyJvm.getInstance();
    System.out.println(jvm1);
    System.out.println(jvm2);
     
}

测试结果:单线程的情况下,懒汉式的结果是没问题的。

com.fz.Thread.syn.MyJvm@18f1d7e

com.fz.Thread.syn.MyJvm@18f1d7e

2、现在加入多线程访问的情况

编写一个多线程的类:

1
2
3
4
5
6
7
//多线程类
class MyJvmThread extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->创建:"+MyJvm.getInstance());
    }
}

然后再使用多线程测试:

1
2
3
4
5
6
public static void main(String[] args) {
    MyJvmThread thread1 = new MyJvmThread();
    MyJvmThread thread2 = new MyJvmThread();
    thread1.start();
    thread2.start();
}

测试结果如下:多线程情况下就出现了问题,可以获取多个不同的对象

Thread-0-->创建:com.fz.Thread.syn.MyJvm@785d65

Thread-1-->创建:com.fz.Thread.syn.MyJvm@3bc257

3、给单例加锁:直接加在方法上

修改MyJvm这个单例类:直接在方法上加锁(效率低)

1
2
3
4
5
6
7
8
9
10
11
//懒汉式的单例类
class MyJvm{
    private static MyJvm instance = null;
    private MyJvm(){}
    public static synchronized MyJvm getInstance(){//这里在方法上直接加上synchronized
        if (instance == null) {
            instance = new MyJvm();        
        }
        return instance;
    }
}

使用多线程的main方法测试:结果是正确的

Thread-1-->创建:com.fz.Thread.syn.MyJvm@2c1e6b

Thread-0-->创建:com.fz.Thread.syn.MyJvm@2c1e6b

虽然以上结果是正确的,但是存在效率问题。每个获取getInstance()方法的对象都需要锁等待。假如第一个线程进来发现instance为null就创建了,

等第二个线程进来的时候其实instance已经存在对象了,但是还是会进行锁等待。造成效率低。

4、将synchronized加在代码块中:

继续修改MyJvm这个类,将synchronized加在创建instance对象上

1
2
3
4
5
6
7
8
9
10
11
12
13
//懒汉式的单例类
class MyJvm{
    private static MyJvm instance = null;
    private MyJvm(){}
    public static MyJvm getInstance(){
        synchronized(MyJvm.class){////静态方法里没有this,所以只能锁定类的字节码信息
            if (instance == null) {
                instance = new MyJvm();        
            }
            return instance;
        }
    }
}

如果就像上面这样把getInstance()方法中的全部代码都使用synchronized锁住的话,同样会有和第3步(给单例加锁:直接加在方法上)一样的效率低的问题。即使instance已经初始化了,后进来的线程还是会锁等待。

所以,继续改进。加入双重检索(double checking)的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//懒汉式的单例类
class MyJvm{
    private static MyJvm instance = null;
    private MyJvm(){}
    public static MyJvm getInstance(){
        if (instance == null) {//第1个线程进来会进入锁然后创建对象,第2个线程再走到这的时候已经不会再进入同步块不会出现锁等待了
            synchronized(MyJvm.class){////静态方法里没有this,所以只能锁定类的字节码信息
                if (instance == null) {
                    instance = new MyJvm();        
                }
            }
        }
        return instance;
    }
}

最后再使用多线程main方法测试下:结果正确,并且效率会比上面的方法更快。

Thread-0-->创建:com.fz.Thread.syn.MyJvm@3bc257

Thread-1-->创建:com.fz.Thread.syn.MyJvm@3bc257

【这里可以在测试的时候加入开始时间和结束时间来更清楚的看到执行效率】

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class SynDemo3 {
    public static void main(String[] args) {        
        MyJvmThread thread1 = new MyJvmThread();
        MyJvmThread thread2 = new MyJvmThread();
        thread1.start();
        thread2.start();
    }
}
//懒汉式的单例类
class MyJvm{
    private static MyJvm instance = null;
    private MyJvm(){}
    //双重检索方式:效率高,第一次访问需要锁等待。之后的就直接获取了
    public static MyJvm getInstance(){
        if (instance == null) {
            synchronized(MyJvm.class){//静态方法里没有this,所以只能锁定类的字节码信息
                if (instance == null) {
                    instance = new MyJvm();        
                }
            }
        }
        return instance;
    }
    //synchronized代码块范围太大:效率低,每个访问的对象都需要进行锁等待
    public static MyJvm getInstance3(){
        synchronized(MyJvm.class){//静态方法里没有this,所以只能锁定类的字节码信息
            if (instance == null) {
                instance = new MyJvm();        
            }
            return instance;
        }
    }
    //将synchronized加在方法上:效率低,每个访问的对象都需要进行锁等待
    public static synchronized MyJvm getInstance2(){//这里在方法上直接加上synchronized
        if (instance == null) {
            instance = new MyJvm();        
        }
        return instance;
    }
    //普通获取:线程不安全
    public static MyJvm getInstance1(){
        if (instance == null) {
            instance = new MyJvm();        
        }
        return instance;
    }
}
//多线程类
class MyJvmThread extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->创建:"+MyJvm.getInstance());
    }
}

线程的同步之Synchronized在单例模式中的应用的更多相关文章

  1. 线程的同步之Synchronized的使用

       一.介绍        线程的同步:一般的并发指的就是多个线程访问同一份资源.多个线程同时访问(修改)同一份资源的话,就会有可能造成资源数据有误. 如果多个线程访问多个不同资源,就不会造成线程同 ...

  2. java thread 线程锁同步,锁,通信

    12.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步 Case:银行取钱问题,有以下步骤: A.用户输入账户.密码,系统判断是否登录成功 B.用户输入取款金额 ...

  3. Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)

    关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...

  4. 单例模式(Singleton)的同步锁synchronized

    单例模式,有“懒汉式”和“饿汉式”两种. 懒汉式 单例类的实例在第一次被引用时候才被初始化. public class Singleton { private static Singleton ins ...

  5. Java线程同步(synchronized)——卖票问题

    卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1, ...

  6. JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this

    JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...

  7. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  8. java 为什么wait(),notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?

    wait()作用:该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止.条件:在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法.进入wai ...

  9. Java中线程的同步问题

    在生活中我们时常会遇到同步的问题,而且大多数的实际问题都是线程的同步问题 我这里以生活中的火车售票来进行举例: 假设现在我们总共有1000张票要进行出售,共有10个出售点,那么当售票到最后只有一张票时 ...

随机推荐

  1. centos 目录文件管理 mkdir,rm,touch,误删文件extundelete,cp,mv,cat,more,less ,head,tail,chown,chmod ,umask 第四节课

    centos 目录文件管理 mkdir,rm,touch,误删文件extundelete,cp,mv,cat,more,less ,head,tail,chown,chmod ,umask  第四节课 ...

  2. kafka详解

    一.基本概念 介绍 Kafka是一个分布式的.可分区的.可复制的消息系统.它提供了普通消息系统的功能,但具有自己独特的设计. 这个独特的设计是什么样的呢? 首先让我们看几个基本的消息系统术语:Kafk ...

  3. HDU4135Co-prime(容斥原理)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4135 题目解析: 给你一个闭区间[A,B](1 <= A <= B <= 1015) ...

  4. 概率图模型PFM——无向图

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAdYAAAFPCAIAAAB/EXiGAAAgAElEQVR4nO2df4wl1XXn6/+0VuG/II ...

  5. SqlServer2005 各版本区别

    SQL2005 分五个版本,如下所列, 1.Enterprise(企业版), 2.Development(开发版), 3.Workgroup,(工作群版) 4.Standard,(标准版) 5.Exp ...

  6. VS2010/MFC编程入门之二十四(常用控件:列表框控件ListBox)

    前面两节讲了比较常用的按钮控件,并通过按钮控件实例说明了具体用法.本文要讲的是列表框控件(ListBox)及其使用实例. 列表框控件简介 列表框给出了一个选项清单,允许用户从中进行单项或多项选择,被选 ...

  7. ng-深度学习-课程笔记-13: 目标检测(Week3)

    1 目标定位( object localization ) 目标定位既要识别,又要定位,它要做的事就是用一个框框把物体目标的位置标出来. 怎么做这个问题呢,我们考虑三目标的定位问题,假定图中最多只出现 ...

  8. tslib移植中环境变量编辑

    (1)将/usr/local/tslib下的所有文件复制到移植系统文件中/usr/local(2)编辑移植系统中/etc/profile添加触摸屏支持内容:在/etc/profile文件中设置tsli ...

  9. 分布式session的管理

    在分布式架构或微服务架构下,必须保证一个应用服务器上保存Session后,其它应用服务器可以同步或共享这个Session,可能会出现在A1系统登录后创建并保存Session,再次发起请求,请求被转发到 ...

  10. SQL学习笔记三(补充-3)之MySQL完整性约束

    阅读目录 一 介绍 二 not null与default 三 unique 四 primary key 五 auto_increment 六 foreign key 七 作业 一 介绍 约束条件与数据 ...