线程的同步之Synchronized在单例模式中的应用
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在单例模式中的应用的更多相关文章
- 线程的同步之Synchronized的使用
一.介绍 线程的同步:一般的并发指的就是多个线程访问同一份资源.多个线程同时访问(修改)同一份资源的话,就会有可能造成资源数据有误. 如果多个线程访问多个不同资源,就不会造成线程同 ...
- java thread 线程锁同步,锁,通信
12.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步 Case:银行取钱问题,有以下步骤: A.用户输入账户.密码,系统判断是否登录成功 B.用户输入取款金额 ...
- Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)
关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...
- 单例模式(Singleton)的同步锁synchronized
单例模式,有“懒汉式”和“饿汉式”两种. 懒汉式 单例类的实例在第一次被引用时候才被初始化. public class Singleton { private static Singleton ins ...
- Java线程同步(synchronized)——卖票问题
卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1, ...
- JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this
JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- java 为什么wait(),notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?
wait()作用:该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止.条件:在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法.进入wai ...
- Java中线程的同步问题
在生活中我们时常会遇到同步的问题,而且大多数的实际问题都是线程的同步问题 我这里以生活中的火车售票来进行举例: 假设现在我们总共有1000张票要进行出售,共有10个出售点,那么当售票到最后只有一张票时 ...
随机推荐
- YOGA Tablet 2 1371f 触屏失效,无声卡,蓝牙键盘都无法使用的解决办法
安装驱动! 下载地址 http://www.lenovocare.com.cn/Handler/Download.ashx?fileid=1234 安装后电源管理,声卡,触摸屏即可使用! 蓝牙键盘连接 ...
- mydumper安装
安装依赖包: yum install glib2-devel mysql-devel zlib-devel pcre-devel openssl-devel cmake 下载二进制包: wget ht ...
- java 反射 (一)
原文地址https://www.zhihu.com/question/24304289 首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成 ...
- 34sqlite
sqlite,本地数据库.主要运用在小型的程序,传送方便(如发送附带数据库的程序,但MySQL有点大,或许没安装). 如何创建本地数据库? 1.新建一个.txt的文本文件. 2.直接将后缀名有.txt ...
- 2017-2018 ACM-ICPC Southeastern European Regional Programming Contest (SEERC 2017) Solution
A:Concerts 题意:给出一个串T, 一个串S,求串S中有多少个串T,可以重复,但是两个字符间的距离要满足给出的数据要求 思路:先顺序统计第一个T中的字符在S中有多少个,然后对于第二位的以及后面 ...
- 2018-2019 ICPC, NEERC, Northern Eurasia Finals (Unrated, Online Mirror, ICPC Rules, Teams Preferred) Solution
A. Alice the Fan Solved. 题意: 两个人打网球,要求teamA 的得分与其他队伍拉开尽量大 输出合法的方案 思路: $dp[i][j][k][l] 表示 A 赢i局,其他队伍赢 ...
- 《FontForge常见问题FAQ》字王翻译版
<FontForge常见问题FAQ> 字王翻译版 原文: http://fontforge.github.io/en-US/faq/ 翻译: 字王·中国 blog: http://bl ...
- docker 容器目录挂载 | 进出容器
docker run --name wnginx -d -p 9001:80 -v /home/www:/usr/share/nginx/html nginx --name 别名 -d ...
- 微信 audio 获取 duration 为 NaN 的解决方法
先加load() myaudio.load(); myaudio.oncanplay = function () { alert(myaudio.duration); } load() 方法用于在更改 ...
- Gulp和Webpack对比
在现在的前端开发中,前后端分离.模块化开发.版本控制.文件合并与压缩.mock数据等等一些原本后端的思想开始逐渐渗透到“大前端”的开发中.前端开发过程越来越繁琐,当今越来越多的网站已经从网页模式进化到 ...