0 序

  • 此文系对最常见的设计模式————单例模式的最全总结。

1 概述:单例模式

模式定义

  • 单例模式:
  • 保证1个类有且仅有1个实例,并提供1个访问它的全局访问点。
  • 1个类有且仅有1个实例,并自行实例化向整个系统提供。

即 为确保一个类只有一个实例,并为整个系统提供一个全局访问点的一种通用方法。

  • 如果一个类的实例应该在JVM初始化时被创建出来,应考虑使用: 饿汉式单例模式。

  • 在使用懒汉式单例模式时,应考虑到: 线程安全性问题。

  • 通常,我们可以让1个全局变量,使得1个对象被访问,但它不能防止你实例化多个对象。

1个最好的办法就是,让类自身负责保存它的唯一实例。

这个类可以保证没有其它实例可以被创建。并且它可以提供1个访问该实例的方法。

模式的组成: 3要素

  • 私有的静态的实例对象 instance
  • 私有的构造函数 Singleton()

保证在该类外部,无法通过new的方式来创建对象实例

  • 公有的、静态的、唯一的访问该实例对象的方法 getInstance()

模式特点

优点

  • 允许可变数目的实例。
  • 可保证1个类仅存在唯一的实例(保证没有其它实例可以被创建)
  • 唯一实例的受控访问:可严格地控制客户访问实例的方式和访问实例的时机。
  • 可保证对单例类的所有实例化得到的都是同一个实例
  • 单例模式在实例化过程中具有一定的伸缩性

类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。

  • 节约系统资源,避免对共享资源的多重占用。

当需要频繁创建和销毁的对象时,单例模式无疑可以提高系统的性能。

缺点

  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。
  • 不适用于变化的对象

    如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
  • 不易扩展,违背“开闭原则”。

    单例类的扩展有很大的困难,因为:单例模式中没有抽象层。
  • 滥用单例将带来一些负面问题:

例如:为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;

如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

适用场景

  • 需要频繁实例化,然后销毁的对象。
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  • 有状态的工具类对象。
  • 频繁访问数据库或文件的对象。
  • 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。

Eg:日志文件,应用配置等

  • 控制资源的情况下,方便资源之间的互相通信。

Eg:线程池、数据库连接池等

案例实践

案例:简单的单例实现

Singleton类,定义1个getInstance()操作,允许客户端访问它的唯一实例。

getInstance是静态方法,主要负责创建自己类的唯一实例。

  • Singleton
public class Singleton {
private static Singleton instance;//即 初始为null
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
  • Client
public class Client {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
//true (变量的对象引用地址一致,说明二者是指向同一实例化对象)
}
}

案例:线程池对象/连接池对象的创建

  • 为了程序的高效率地使用多线程并发,然而是循环调用,可能导致创建线程数过多,考虑采用线程池管理,这时候创建线程池仍然是处于循环调用中,也可能导致多个线程池,这时候就考虑使用单例模式
/**
* 多线程下,线程安全的一种依赖于静态内部类实现的单例模式
*/
public class ThreadPoolFactoryUtil {//单例类
private ExecutorService executorService;
//在构造函数中创建线程池
private ThreadPoolFactoryUtil(){//私有的构造器
//获取系统处理器个数,作为线程池数量
int nThreads=Runtime.getRuntime().availableProcessors();
executorService = Executors.newFixedThreadPool(nThreads);
}
//获取本类对象
public static ThreadPoolFactoryUtil getThreadPoolFactoryUtil(){ //公有的、静态的、唯一访问实例对象的方法
return SingletonContainer.util;
}
/**
* 定义1个静态内部类,内部定义静态成员创建外部类实例
* 类级的内部类,即 静态的成员式内部类
* 该内部类的实例与外部类的实例没有绑定关系,而且只有被调用到才会装载,从而实现了【延迟加载】
*/
private static class SingletonContainer{
//静态初始化器,由JVM来保证线程安全
private static ThreadPoolFactoryUtil util = new ThreadPoolFactoryUtil();
}
public ExecutorService getExecutorService(){ //ThreadPoolFactoryUtil的实例化对象的实例方法
return executorService;
}
}

懒汉式的单例模式(延迟加载)

定义

  • 应用刚启动的时候,并不创建实例;
  • 当外部调用该类的实例或者该类实例方法的时候,才创建该类的实例。(时间换空间)

特点

优点 缺点
实例在被使用的时候才被创建,可节省系统资源;
体现了延迟加载的思想。 由于系统刚启动,且未被外部调用时,实例没有创建;
若同一时间有多个线程同时调用 LazySingleton.getLazyInstance() 方法很有可能会产生多个实例
(即 多线程场景下,单例失效故障
  • 重要结论:
  • 传统的懒汉式单例是【非线程安全】的

案例实践

public class Singleton {
private static Singleton instance = null; //【重点】初始时:私有属性 instance 为 null,不实例化(new)对象 private Singleton(){} public static Singleton getInstance(){
if(instance == null){ //非线程安全的一个显著原因是:可能会有多个线程同时进入 if (instance == null) { … } 语句块的情形发生。当这种这种情形发生后,该单例类就会创建出多个实例,违背单例模式的初衷。因此,传统的懒汉式单例是【非线程安全】的。
instance = new Singleton();
}
return instance;
}
}

引申问题:多线程下,懒汉式单例模式的缺陷问题

问题描述

  • 多线程的程序中,多个线程同时访问Singleton类,调用getInstance()方法,会有造成创建多个实例的可能。

问题复现:非线程安全

LazySingleton

public class LazySingleton {
private static int count = 0;
private static LazySingleton lazyInstance=null; //初始时:私有属性instance为null,不实例化(new)对象 //↓为了易于模拟多线程下,懒汉式出现的问题:在创建实例的构造函数里,使当前线程暂停了50毫秒
private LazySingleton(){
try{
Thread.sleep(50);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("生成LazySingleton实例一次!"+(++count));
}
public static LazySingleton getLazyInstance(){
if(lazyInstance ==null){
lazyInstance = new LazySingleton();
}
return lazyInstance;
}
}

Client : 制造多个线程,同时创建单例对象

public class Client {
@Test
public void lazySingletonTest() {
for (int i = 0; i < 10; i++) {//创建10个线程调用
new Thread() {
@Override
public void run() {
LazySingleton.getLazyInstance();
}
}.start();//end Thread
}//end for loop
}//end method
}//end class

out:

  • 单例模式失效,构造器创建/实例化了多个单例类的不同对象!

由此可见,懒汉式的单例模式多线程的并发场景下,会出故障。

问题分析

【分析】

  • 多个线程同时访问上面的懒汉式单例,
  • 现在有两个线程A和B同时访问LazySingleton.getLazyInstance()方法。
  • 假设A先得到CPU的时间切片,A执行到if(lazyInstancenull)时,由于lazyInstance之前并没有实例化,所以lazyInstancenull为true,在还没有执行实例创建的时候此时CPU将执行时间分给了线程B,线程B执行到if(lazyInstancenull)时,由于lazyInstance之前并没有实例化,所以lazyInstancenull为true,线程B继续往下执行实例的创建过程,线程B创建完实例之后,返回。
  • 此时,CPU时间切片分给线程A,线程A接着开始执行实例的创建,实例创建完之后便返回。
  • 由此,看线程A和线程B分别创建了1个实例(存在2个实例了),这就导致了单例的失效

解决办法:怎么修改传统的懒汉式单例,使其线程变得安全?多线程并发环境下,如何利用Java特性实现线程安全的懒汉式单例?

方案1:(同步方法) public static synchronized LazySingleton getLazyInstance()

  • 可在getLazyInstance()方法上加上synchronized使其同步。
// 使用 synchronized 修饰,临界资源的同步互斥访问
public static synchronized LazySingleton getLazyInstance(){ … }

但是这样一来,会降低整个访问的速度,而且每次都要判断。

方案2:(同步代码块) 双重检查加锁/双重锁定

  • 那么,有没有更好的方式来实现呢?可以考虑使用"双重检查加锁"。

"双重检查加锁"的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。

具体代码如下:

public class LazySingleton {
private static int count = 0;
private static LazySingleton lazyInstance=null; //初始时:私有属性instance为null,不实例化(new)对象
//↓为了易于模拟多线程下,懒汉式出现的问题:在创建实例的构造函数里,使当前线程暂停了50毫秒
private LazySingleton(){
try{
Thread.sleep(50);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("生成LazySingleton实例一次!"+(++count));
}
//双重检查加锁 解决懒汉式单例模式的多线程不安全问题
public static LazySingleton getLazyInstance(){
if(lazyInstance==null){//先检查实例是否存在?(第1次) 若不存在时:才进入下面的同步块(即 第一重加锁处理)
//同步块,线程安全地创建实例
synchronized (LazySingleton.class){ //【重点】 使用 synchronized 块,临界资源的同步互斥访问
//再次检查实例是否存在?(第2次)如果不存在才真正地创建实例
if(lazyInstance==null){
lazyInstance=new LazySingleton();
}
}
}
return lazyInstance;
}
}

out:

``java

生成 LazySingleton 实例1次!


#### 方案3:静态内部类(实现 延迟加载和线程安全)
+ <span style="color:red">静态内部类 = 静态的成员式内部类</span>
+ 静态内部类无需依赖于外部类,它可独立于外部对象而存在。
+ 静态内部类,多个外部类的对象可【共享】同一个内部类的对象。
+ 使用**静态内部类**的【好处】: 加强了代码的封装性及提高了代码的可读性。 + 普通内部类不能声明`static`的方法和变量
> 即 `final static` 修饰的属性还是可以的,而静态内部类形似外部类,无任何限制
> 可直接被用外部类名+内部类名获得 + 具体代码:
```java
public class GracefulSingleton {//单例类
private GracefulSingleton() { //私有的构造方法
System.out.println("创建GracefulSingleton实例一次!");
}
public static GracefulSingleton getInstance() {//公有的、静态的、唯一的访问实例对象的方法
return SingletonHolder.instance;
}
/**
* 定义1个静态内部类,内部定义静态成员创建外部类实例
* 类级的内部类,即 静态的成员式内部类
* 该内部类的实例与外部类的实例没有绑定关系,而且只有被调用到才会装载,从而实现【延迟加载】
*/
private static class SingletonHolder {//【重点】
//静态初始化器,由JVM来保证线程安全
private static GracefulSingleton instance = new GracefulSingleton();
}
}

方案4:(单例模式的变种)单例模式 与 ThreadLocal

  • 借助于 ThreadLocal,我们可以实现双重检查模式的变体————核心思想:将临界资源instance线程私有化(局部化)

具体到本例就是: 将双重检测的第一层检测条件 if (instance == null) 转换为线程局部范围内的操作,对应的代码清单如下:

public class Singleton {
// ThreadLocal 线程局部变量,将单例instance线程私有化
private static ThreadLocal<Singleton> threadlocal = new ThreadLocal<Singleton>(); //【重点】
private static Singleton instance; private Singleton() {
} public static Singleton getInstance() {
// 第一次检查:若线程第一次访问,则进入if语句块;否则,若线程已经访问过,则直接返回ThreadLocal中的值
if (threadlocal.get() == null) {
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查:该单例是否被创建
instance = new Singleton();
}
}
threadlocal.set(instance); // 将单例放入ThreadLocal中
}
return threadlocal.get();
}
}

out

Output(完全一致):
1028355155
1028355155
1028355155
1028355155
1028355155
1028355155
1028355155
1028355155
1028355155
1028355155

饿汉式的单例模式(立即加载)

  • 开发中,相较于懒汉式单例模式,饿汉式使用更多。

定义

  • 应用刚启动的时候,不管外部有没有调用该类的实例方法,该类的实例就已经创建好了。(空间换时间

特点

  • 优点
  • 写法简单;
  • 在多线程下,也能保证单例实例的唯一性;
  • 不用同步;
  • 运行效率高
  • 缺点
  • 一直未被调用时,可能存在无意义地消耗系统资源 (资源浪费)

案例实践

public final class Singleton { //final阻止派生类,派生可能会增加实例
//在第1次引用类的任何成员时,就创建单例类实例。
private static Singleton instance=new Singleton(); //【重点】初始时:私有属性instance就实例化(new)对象
private Singleton(){}
public static Singleton getInstance(){//直接返回实例
return instance;
}
}

辨析对比:饿汉式单例 VS 懒汉式单例

总结

  • 速度/性能反应时间角度来讲,饿汉式(又称立即加载)要好一些;
  • 资源利用效率上说,懒汉式(又称延迟加载)要好一些。

案例与源码分析 | Runtime类 : 饿汉式单例

public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
}
  • Runtime类封装了Java运行时的环境
  • 每一个java程序实际上都是启动了一个JVM进程
  • 那么,每个JVM进程都是对应这一个Runtime实例,此实例是由JVM为其实例化的。
  • 每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
  • 由于Java是单进程的。

所以,在一个JVM中,Runtime的实例应该只有一个。

所以,应该使用单例来实现。

  • 饿汉式单例模式
  • Runtime 类第一次被classloader加载的时候,实例就被创建出来了。
  • 一般不能实例化一个 Runtime 对象,应用程序也不能创建自己的 Runtime 类实例;
  • 但可以通过 getRuntime 方法获取当前 Runtime 运行时对象的引用。

案例与源码分析 | JDBC/DriverManager#getConnection(…): 懒汉式单例(双重检查加锁)

  • 回忆一下: 基于 jdbc 获取连接
// 注册 JDBC 驱动
Class clz = Class.forName(JDBC_DRIVER);
// 打开链接
conn = DriverManager.getConnection(DB_URL,USER,PASS);
  • DriverManager
@CallerSensitive
public static Connection getConnection(String url, String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
} // Worker method called by the public getConnection() methods.
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
} if(url == null) { throw new SQLException("The url cannot be null", "08001"); }
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null; for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
} // if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}

案例与源码分析 | Integer.valueOf(int i) :懒汉式单例模式(静态内部类)

  • Integer
public static Integer valueOf(int i) {//懒汉式单例模式
if (i >= IntegerCache.low && i <= IntegerCache.high)//已实例化?是,返回IntegerCache数组的对应实例
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);//尚未实例化?是,返回新实例对象
}
  • IntegerCache

注: IntegerCache 是一个数组

private static class IntegerCache {//私有的静态内部类,用
static final int low = -128;
static final int high;
static final Integer cache[]; //待返回的单例(数组)实例对象(//静态内部类下静态成员,由JVM来保证线程安全)
}

案例与源码分析 | java.awt.Toolkit类#getDefaultToolkit() : 懒汉式单例

  • 不需要事先创建好,只要在第一次真正用到的时候再创建就可以了。
  • 因为很多时候并不常用JavaGUI和其中的对象。
  • 如果使用饿汉单例的话,会影响JVM的启动速度。

Toolkit

public abstract class Toolkit {
private static Toolkit toolkit; public static synchronized Toolkit getDefaultToolkit() {
if (toolkit == null) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
Class<?> cls = null;
String nm = System.getProperty("awt.toolkit");
try {
cls = Class.forName(nm);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
try {
cls = cl.loadClass(nm);
} catch (final ClassNotFoundException ignored) {
throw new AWTError("Toolkit not found: " + nm);
}
}
}
try {
if (cls != null) {
toolkit = (Toolkit)cls.newInstance();
if (GraphicsEnvironment.isHeadless()) {
toolkit = new HeadlessToolkit(toolkit);
}
}
} catch (final InstantiationException ignored) {
throw new AWTError("Could not instantiate Toolkit: " + nm);
} catch (final IllegalAccessException ignored) {
throw new AWTError("Could not access Toolkit: " + nm);
}
return null;
}
});
loadAssistiveTechnologies();
}
return toolkit;
}
}

案例与源码分析 | spring 应用的单例多线程问题

spring 应用的对象创建方式

  • Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。

即 如无特殊配置,Spring框架中默认的、大多数的类实例都是单例对象

例如:Controller层的实例、Service层的实例、DAO层的实例。

  • 单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
  • 当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求多对应的业务逻辑(成员方法)。此时就要注意了,如果该处理逻辑中有对该单例对象状态的修改(体现为该单列的成员属性),则必须考虑线程同步问题。

同步机制的比较

锁的同步机制 : 多个线程访问共享对象时需排队 (以时间换空间)

  • 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。

这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

ThreadLocal : 为每一个线程提供一个独立的变量副本 (以空间换时间)

  • ThreadLocal线程同步机制相比有什么优势呢?
  • ThreadLocal线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
  • ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal
  • 由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化了ThreadLocal的使用
  • 概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring使用ThreadLocal解决线程安全问题

  • 在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

  • 一般的【Web应用】划分为展现层服务层持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。

在一般情况下,从接收请求返回响应所经过的所有程序调用都同属于一个线程。

  • ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

是否存在【线程安全】问题的判断思路

  • 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。
  • 如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
  • 或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。

线程安全问题都是由全局变量静态变量引起的。

  • 若每个线程中对全局变量静态变量只有读操作,而无写操作

一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全

1)【常量】始终是【线程安全】的,因为只存在读操作。 

2)每次调用方法前都新建一个实例是【线程安全】的,因为不会访问共享的资源。

3)【局部变量】是线程安全的。因为每执行一个方法,都会在【独立的空间】创建【局部变量】,它不是【共享的资源】。
局部变量包括方法的参数变量和方法内变量。
  • 有状态就是有数据存储功能。

有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间,不保留任何状态。

  • 无状态就是一次操作,不保存数据。

无状态对象(Stateless Bean),就是没有实例变量的对象。不能保存数据,是不变类,是线程安全的。

参考文献

Strcut2 的线程安全问题

  • 基于Struts2框架(类比 Java Web 框架: Spring MVC)的应用程序的Java对象,默认的实现是Prototype模式。

也就是每个请求都新生成一个Action实例,所以不存在线程安全问题。

  • 需要注意的是,如果由Spring管理action的生命周期,scope要配成prototype作用域

Z FAQ

Q: 与实用类(XxxUtils)的区别?

实用类 单例类
不保存状态 仅提供一些静态方法或静态属性让客户访问 (Eg:Math类) 保存状态
不能用于继承多态 允许有子类继承
一些方法、属性的集合 有着唯一的对象实例

Y 推荐文献

X 参考文献

[设计模式/Java/多线程] 设计模式之单例模式【9】的更多相关文章

  1. java多线程设计模式

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt220 java多线程设计模式 java语言已经内置了多线程支持,所有实现Ru ...

  2. [温故]图解java多线程设计模式(一)

    去年看完的<图解java多线程设计模式>,可惜当时没做笔记,导致后来忘了许多东西,打算再温习下这本书,顺便在这里记录一下~  1.顺序执行.并行.并发 顺序执行:多个操作按照顺序依次执行. ...

  3. Java多线程设计模式(一)

    目录(?)[-] Java多线程基础 Thread类的run方法和start方法 线程的启动 线程的暂时停在 线程的共享互斥 线程的协调 Single Threaded Execution Patte ...

  4. Java多线程设计模式(4)线程池模式

    前序: Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作.它将“委托消息的一端”和“执行消息的一端”用两个不同的线程来实现.该线程模式 ...

  5. Java多线程核心技术(五)单例模式与多线程

    本文只需要考虑一件事:如何使单例模式遇到多线程是安全的.正确的 1.立即加载 / "饿汉模式" 什么是立即加载?立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就是直接 ...

  6. Java多线程设计模式(四)

    目录(?)[-] Future Pattern Two-Phase Termination Pattern Thread-Specific Storage Pattern Active Object ...

  7. Java多线程设计模式(三)

    目录(?)[-] Read-Wirte Lock Pattern Thread-Per-Message Pattern Worker Thread Pattern   Read-Wirte Lock ...

  8. Java多线程设计模式(二)

        目录(?)[-] Guarded Suspension Pattern Balking Pattern Producer-Consumer Pattern   Guarded Suspensi ...

  9. Java多线程设计模式(1)

    1 线程中的几个方法解释 Thread.sleep(long 毫秒) 在指定的时间内让当前线程休眠,将控制权交给其他线程,休眠的线程依旧拥有它所占用的锁, Thread.yield(); 暂停或者放弃 ...

  10. Java多线程设计模式系列

    通过几天的认真阅读,发现这是一本难得一见的好书,为了加深巩固学习成功,我打算将书中的例子全部自己实现一遍,特此记录下来也方便其他朋友学习. 第一章,java语言的线程 单线程程序:打印10000次go ...

随机推荐

  1. .net core2.2版本下载地址

    下载地址: https://download.visualstudio.microsoft.com/download/pr/279de74e-f7e3-426b-94d8-7f31d32a129c/e ...

  2. 如何分析jmeter性能测试数据

    1.jmeter插件 https://blog.csdn.net/weixin_39430584/article/details/80947093 http://www.doc88.com/p-214 ...

  3. WordPress产品导入后内容出现乱码,以及附属一些别的功能

    效果图如下 该插件附带了一个可以把产品描述里面的超链接给去掉,以及有的产品图片点击会在地址栏上面显示图片的路径,在该插件可以进行关闭,并且替换成一个模态窗,还有对产品邮费展示进行了处理,到金额到达包邮 ...

  4. C# as 和 is 运算符区别和用法

    前言 在C#中,as 和 is 关键字都用于处理类型转换的运算符,但它们有不同的用途和行为.本文我们将详细解释这两个运算符的区别和用法. is 运算符 is 运算符用于检查对象是否是某个特定类型,或者 ...

  5. 2024年度Graph+AI开源探索思考

    前记 这篇年度总结其实酝酿了许久,却因诸多原因拖至腊月底,此时赶在春节前发出来,也不失为"农历版"年度总结了.所谓年度总结,一般是"温故而知新",我不太想落入堆 ...

  6. ABB喷涂机器人IRB5400维修保养

    为最大限度地保证ABB机器人正常运行,ABB机器人普遍需要在运行6000小时后进行一次保养,特别是针对在恶劣工况与长时间在负载极限或运行极限下工作的ABB机器人,则需要每年进行一次全面ABB机器人保养 ...

  7. springboot+vue项目:工具箱

    常用账号管理:工作相关账号.游戏账号.各平台账号 加班调休管理:公司没有对应的系统,需要自己记录加班调休情况. 待办事项:方便记录待办,以提醒还有哪些事情没有办理. 待实现功能: 1.点击侧边栏菜单, ...

  8. Keepalived学习,双机主备高可用

    一.主机安装 1.解压 tar -zxvf keepalived-2.0.18.tar.gz 2.解压后进入到解压出来的目录,看到会有configure,那么就可以做配置了 3.使用configure ...

  9. phpinclude-labs做题记录

    Level 1 file协议 payload:?wrappers=/flag Level 2 data协议 去包含data协议中的内容其实相当于进行了一次远程包含,所以data协议的利用条件需要 ph ...

  10. python 代码编写问题

    1.解决控制台不输出问题 2.写代码写一些伪代码,即实现过程.步骤 3.再填充代码到伪代码 4.规则 正常变量 不太推荐使用下划线