单例模式
立即加载(饿汉模式)
立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就是声明的时候直接new实例化。
// 加载类时候创建对象:静态变量
public class A {
private static A a = new A(); // 加载类时候就创建
private A(){}
public static A getInstance(){
return a;
}
}
// 加载类时候创建对象:静态代码块
public class A {
private static A a;
static {
a = new A(); // 加载类时候就创建
}
private A(){}
public static A getInstance(){
return a;
}
}
// 静态内置类实现
public class A() {
private static class AHandler {
private static A a = new A(); // 加载类时候就创建
}
private A(){}
public static A getInstance(){
return AHandler.a;
}
}
// 利用枚举类的构造方法,枚举类构造方法会在加载时候被自动调用
public class A() {
// 这么封装是为了避免AEnum没有特别作用,他只是封装A内部作为单例的实现
private enum AEnum {
bFactory;
private B b;
private A() {
b = new B();
}
public static B getB() {
return b;
}
}
public static B getB() {
return AEnum.bFactory.getB();
}
}
延迟加载(懒汉模式)
延迟加载就是在要首次使用类的时候才进行对象的创建,常见的实现办法就是在get()或者getInstance()方法中进行new实例化。
public class A {
private static ObjectA a;
private A(){}
public static A getInstance(){
if (a == null) {
a = new A();
}
return a;
}
}
延迟加载的同步问题
延迟加载由于可能出现多个线程同时进入a = new A();
的代码块,所以a可能被多次创建,就不符合单例模式的设计,那么可以通过以下几个方法进行解决:
同步方法
给获取实例的方法加锁,所有进入方法的线程都要排队进入。但是这种方式效率比较低下,一次只能由一个对象进入,即使在初始化完对象之后。
public synchronized static A getInstance(){}
同步代码块
效果等于同步方法
public static A getInstance(){
synchronized(A.class) {
if (a == null) {
a = new A();
}
return a;
}
}
DCL双检查锁机制
这种方式解决了当a初始化完成之后,还需要不断进入同步代码块的情况,当a初始化完成之后,就直接返回对象,不会进入同步块,同时呢,保证a在初始化过程中,进入同步代码块会再判断一次,避免多个线程同时通过第一层判空的情况。
public static A getInstance(){
if (a == null) {
synchronized(A.class) {
if (a == null) {
a = new A();
}
}
}
return a;
}
线程状态
NEW
至今尚未启动的线程,线程刚刚创建完;RUNNABLE
正在JVM中执行的线程,(包含就绪态和执行态,与其他线程轮流使用CPU时间);BLOCKED
被阻塞并等待某个锁,等待synchronized
或者Lock
;WAITING
调用了wait()方法,等待唤醒,一般是调用了wait()
或join()
方法,等待唤醒或子线程执行完成;TIMED_WAITING
有限时间内等待其他线程的唤醒,一般是调用了wait(long)
、join(long)
、sleep(long)
等等待时间段过去,自动唤醒的线程状态;IERMINATED
已退出,执行完成,已经退出的线程状态。
线程组
可以将线程归属到某一个线程组中,批量的管理线程或者线程组对象,有效的进行组织。线程组中可以包含线程或者线程组。
使用方法
public class Run {
public static void main(String[] args) {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
ThreadGroup group = new ThreadGroup("threadGroupA");
Thread aThread = new Thread(group, a);
Thread bThread = new Thread(group, b);
group.start(); // 批量启动
// group.interrupt(); // 批量终止
}
}
SimpleDateFormat在多线程条件下不安全
多线程环境中使用SimpleDateFormat比较容易出现时间混乱的情况。
一般有下几种解决方式:
- 每个时间的解析都new一个新的
SimpleDateFormat
对象,确保线程之间不共享SimpleDateFormat
对象; - 采用ThreadLocal类,确保一个
SimpleDateFormat
对象只在同一时间被一个线程所使用;
线程的异常处理
处理可以通过set异常处理器来解决,优先级别为:
- Thread对象的
setUncaughtExceptionHandler()
方法; - Thread类的
setDefaultUncaughtExceptionHandler()
方法;