实例化对象在JVM中实际上包含如下指令:

  1. 分配内存空间
  2. 初始化对象
  3. 将instance指向刚才分配好的内存空间地址

上述步骤在单线程中是没问题的。JVM在指令重排序也不会影响到单线程的执行顺序,但是在多线程环境下就会因为重排序导致出现使用未被初始化完成的对象,指令会被重排序为:

  1. 分配内存空间
  2. 将instance指向刚分配好的内存空间地址
  3. 初始化对象

如果线程1执行到了上述的步骤2,线程2执行就会使用未被初始化完成的对象。

  • 带双重检查的延迟初始化
public class DoubleCheckedInstance {
    private volatile static Instance instance;
    public static Instance getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedInstance.class) {
                if (instance == null) {
                    instance = new Instance();
                }
            }
        }

        return instance;
    }
}

关键点:

private volatile static Instance instance;

如果没有volatile关键字,则在实例化Instance对象时会出现指令重排序,从而导致在多线程环境下使用了未被初始化完成的对象。

  • 基于类延迟初始化
public class InstanceFactory {
    private static class InstanceHolder {
        public static Instance instance = new Instance();
    }
    
    public static Instance getInstance() {
        return InstanceHolder.instance;
    }
}

JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。

两个线程并发执行的示意图

参考资料:

  1. 方腾飞,魏鹏,程晓明.《Java并发编程的艺术》机械工业出版社 ISBN:9787111508243

在Ubuntu 18.04系统中,在安装有docker-compose时,将无法正常登录。

详情如下:

docker login 
Password: 
Error saving credentials: error storing credentials - err: exit status 1, out: `Cannot autolaunch D-Bus without X11 $DISPLAY`

解决方法,移除掉docker-compose:

sudo apt-get autoremove --purge docker-compose

再执行:

docker login 
Password: 
Login Succeeded

参考:https://github.com/docker/compose/issues/6023

从大类上分,虚拟化技术可分为基于硬件的虚拟化和基于软件的虚拟化

其中,真正意义上的基于硬件的虚拟化技术不多见,少数如网卡中的单根多IO虚拟化(Single Root I/OⅤirtualizationand Sharing Specification,SR-IOⅤ)等技术,也超出了本书的讨论范畴。基于软件的虚拟化从对象所在的层次,又可以分为应用虚拟化和平台虚拟化(通常说的虚拟机技术即属于这个范畴)。其中,前者一般指的是一些模拟设备或Wine这样的软件。后者又可以细分为如下几个子类:

  • 完全虚拟化。虚拟机模拟完整的底层硬件环境和特权指令的执行过程,客户操作系统无需进行修改。例如ⅤMware Workstation、ⅤirtualBox、QEMU等。
  • 硬件辅助虚拟化。利用硬件(主要是CPU)辅助支持(目前x86体系结构上可用的硬件辅助虚拟化技术包括Intel-ⅤT和AMD-Ⅴ)处理敏感指令来实现完全虚拟化的功能,客户操作系统无需修改,例如ⅤMware Workstation、Xen、KⅤM。
  • 部分虚拟化。只针对部分硬件资源进行虚拟化,客户操作系统需要进行修改。现在有些虚拟化技术的早期版本仅支持部分虚拟化。
  • 超虚拟化(Paravirtualization)。部分硬件接口以软件的形式提供给客户机操作系统,客户操作系统需要进行修改,例如早期的Xen。
  • 操作系统级虚拟化。内核通过创建多个虚拟的操作系统实例(内核和库)来隔离不同的进程。容器相关技术即在这个范畴。

Docker以及其他容器技术都属于操作系统的虚拟化这个范畴。

Docker和常见的虚拟机方式的差异比较:

Docker和传统的虚拟机方式的不同之处–传统方式是在硬件层面实现虚拟化,需要有额外的虚拟机管理应用和虚拟机操作系统层