单例模式与多线程

立即加载/饿汉模式

立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就是直接new实例化。

立即加载/饿汉模式实在调用方法前,实例已经被创建了

package Six;

public class MyObject {

    // 立即加载方式==饿汉模式
private static MyObject myObject = new MyObject(); private MyObject() {
} public static MyObject getInstance() {
// 此代码版本为立即加载
// 此版本代码的缺点是不能有其它实例变量
// 因为getInstance()方法没有同步
// 所以有可能出现非线程安全问题
return myObject;
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
} }
package Six;

public class Run {

    public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread(); t1.start();
t2.start();
t3.start(); } }

说明对象是同一个,也就实现了立即加载型单例设计模式

延迟加载/"懒汉模式"

延迟加载就是在调用get()方法时实例才被创建,常见的实现办法就是在get()方法中进行new实例化

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
// 延迟加载
if (myObject != null) {
} else {
myObject = new MyObject();
}
return myObject;
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
}
package Six;

public class Run {

    public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
} }

此实验虽然取得一个对象的实例,但如果实在多线程的环境中,就会出现多个实例的情况

延迟加载/"懒汉模式"缺点

延迟加载在多线程环境中是错误的。

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
} }
package Six;

public class Run {

    public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread(); t1.start();
t2.start();
t3.start(); } }

打印出了三个对象,并不是单例的,如何解决?见下

延迟加载/“”懒汉模式“”的解决方案

(1)声明synchronized关键字

既然多个线程可以同时进入getInstance()方法,那么只需要对getInstace()方法声明synchronizaed关键字即可

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} synchronized public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

但此方法的运行效率底下,是同步运行的,下一个线程想要取得对象,则必须等上一个线程释放锁后,才可以继续执行。

(2)尝试同步代码块

同步方法是对方法的整体进行持锁,这对运行效率来讲是不利的。等同于上面的写法

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
synchronized (MyObject.class) {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
} } catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

(3)针对某些重要的代码进行单独的同步

同步代码块可以针对某些重要的代码进行单独的同步,而其他的代码则不需要同步,这样在运行时,效率完全可以得到大幅度提升

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
synchronized (MyObject.class) {
myObject = new MyObject();
} }
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

此方法只对实例化对象的关键代码进行同步,从语句的结构上讲,运行效率得到了提升,但在多线程的环境下还是无法解决得到一个实例对象的结果。

(4)使用DCl双检查锁机制

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
synchronized (MyObject.class) {
if(myObject ==null) {
myObject = new MyObject();
} } }
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

DCL是大多数多线程结合单例模式使用的解决方案

使用静态内置类实现单例模式

其他代码同上

package Six;

public class MyObject {

    // 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
} private MyObject() {
} public static MyObject getInstance() {
return MyObjectHandler.myObject;
} }

使用staic代码块实现单例模式

package Six;

public class MyObject {

    private static MyObject instance = null;

    private MyObject() {
} static {
instance = new MyObject();
} public static MyObject getInstance() {
return instance;
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.getInstance().hashCode());
}
}
}
package Six;

public class Run {

    public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread(); t1.start();
t2.start();
t3.start(); } }

使用enum枚举数据类型实现单例模式

package Six;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; public enum MyObject {
connectionFactory; private Connection connection; private MyObject() {
try {
System.out.println("调用了MyObject的构造");
String url = "jdbc:sqlserver://localhost:1079;databaseName=ghydb";
String username = "sa";
String password = "";
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(driverName);
connection = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} public Connection getConnection() {
return connection;
}
}
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.connectionFactory.getConnection()
.hashCode());
}
}
}
package Six;

public class Run {

    public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread(); t1.start();
t2.start();
t3.start(); } }

完善使用enum枚举实现单例模式

前面违反了"职责单一原则”

package Six;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; public class MyObject { public enum MyEnumSingleton {
connectionFactory; private Connection connection; private MyEnumSingleton() {
try {
System.out.println("创建MyObject对象");
String url = "jdbc:sqlserver://localhost:1079;databaseName=y2";
String username = "sa";
String password = "";
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(driverName);
connection = DriverManager.getConnection(url, username,
password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} public Connection getConnection() {
return connection;
}
} public static Connection getConnection() {
return MyEnumSingleton.connectionFactory.getConnection();
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.getConnection().hashCode());
}
}
}
package Six;

public class Run {

    public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread(); t1.start();
t2.start();
t3.start(); }
}

《Java多线程编程核心技术》读后感(十四)的更多相关文章

  1. Java多线程编程核心技术,第四章

    1,ReentrantLock 2,object的wait(),wait(x),notify(),notifyAll(),分别等于Condition的await(),await(x,y),signal ...

  2. Java多线程编程核心技术(三)多线程通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  3. Java多线程编程核心技术(二)对象及变量的并发访问

    本文主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.阅读本文应该着重掌握如下技术点: synchronized对象监视器为O ...

  4. Java多线程编程核心技术(一)Java多线程技能

    1.进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程. 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位. 举个例子: 打开你的计算机上的任务管 ...

  5. Java多线程编程核心技术---学习分享

    继承Thread类实现多线程 public class MyThread extends Thread { @Override public void run() { super.run(); Sys ...

  6. Java多线程编程核心技术---对象及变量的并发访问(二)

    数据类型String的常量池特性 在JVM中具有String常量池缓存的功能. public class Service { public static void print(String str){ ...

  7. Java多线程编程核心技术

    Java多线程编程核心技术 这本书有利于对Java多线程API的理解,但不容易从中总结规律. JDK文档 1. Thread类 部分源码: public class Thread implements ...

  8. 《Java多线程编程核心技术》推荐

    写这篇博客主要是给猿友们推荐一本书<Java多线程编程核心技术>. 之所以要推荐它,主要因为这本书写得十分通俗易懂,以实例贯穿整本书,使得原本抽象的概念,理解起来不再抽象. 只要你有一点点 ...

  9. 《java多线程编程核心技术》(一)使用多线程

    了解多线程 进程和多线程的概念和线程的优点: 提及多线程技术,不得不提及"进程"这个概念.百度百科对"进程"的解释如下: 进程(Process)是计算机中的程序 ...

  10. 《Java 多线程编程核心技术》- 笔记

    作为业务开发人员,能够在工作中用到的技术其实不多.虽然平时老是说什么,多线程,并发,注入,攻击!但是在实际工作中,这些东西不见得用得上.因为,我们用的框架已经把这些事做掉了. 比如web开发,外面有大 ...

随机推荐

  1. 关于Android6.0 之EasyPermissionUtil

    之前6.0权限用第三方类库比较多,但是都是挺麻烦的,今天给大家推荐一个好用的第三方类库: gitHub地址:https://github.com/yxping/EasyPermissionUtil 使 ...

  2. 【学习笔记】快速傅里叶变换(FFT)

    [学习笔记]快速傅里叶变换 学习之前先看懂这个 浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理--gzy hhh开个玩笑. 讲一下\(FFT\) ...

  3. centos 安装postgresql 完整版

    按步骤 执行命令即可: yum install https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-c ...

  4. Action类的工作机制

    Action类的工作机制 Execute()方法包含以下参数 ActionMapping:包含了这个Action的配置信息,和struts-config.xml文件中的<action>元素 ...

  5. PAT 甲级 1104. Sum of Number Segments (20) 【数学】

    题目链接 https://www.patest.cn/contests/pat-a-practise/1104 思路 最容易想到的一个思路就是 遍历一下所有组合 加一遍 但 时间复杂度 太大 会超时 ...

  6. Java多线程系列 基础篇01 线程的状态

    1.进程和线程 进程: 计算机中程序关于某数据集合的一次运行活动,是计算机系统进行资源分配和调度的基本单位,是操作系统结构的基础. 线程: 线程是进程的实例,是CPU进行资源分配和调度的最小单位,线程 ...

  7. 《avascript 高级程序设计(第三版)》 ---第三章 基本概念2

    1.乘性操作符: 1)*法操作法: Infinity * 0 = NaN  Infinity * 非零 = Infinity 或 - Infinity   2)/法操作符: Infinity / In ...

  8. 排序List集合

    这两天写代码过程中遇到一个需求,需要按照某个字段排序List集合,自己实现了一半,发现网上有个更好的版本,就采用了这个,记录下来. 使用这个工具类要注意一个就是 如果你按照age 字段排序,那么age ...

  9. BZOJ 1650 [Usaco2006 Dec]River Hopscotch 跳石子:二分

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1650 题意: 数轴上有n个石子,第i个石头的坐标为Di,现在要从0跳到L,每次条都从一个石 ...

  10. NOIP 2016【蚯蚓】

    好吧,我承认我是个智障-- 这道题一眼看上去就是个堆,然而实际上有单调性. 注意到,如果 \(q = 0\) 的话,将蚯蚓的左右两边分开丢进两个队列中,则两个队列都是单调不增的,因为每次取出的蚯蚓长度 ...