文章目录
特点:
- 一个类只能有一个对象实例
步骤
- 构造器私有化 – 外部不能创建对象
- 声明一个私有的本类的静态属性对象
- 创建一个公共的静态方法访问本类的静态属性对象
1. 懒汉式
1.1 懒汉式 – 需要在生成单例对象 – 线程不安全
class Threa1 extends Thread {
@Override
public void run() {
System.out.println(Student.getStudent());
}
}
class Student {
private static Student student;
private Student() {}
public static Student getStudent() {
if(student == null) {
return ( student = new Student() );
}
return student;
}
}
//-----------------------------
//测试代码
public static void main() {
System.out.println("线程不安全 - 多线程");
Thread getObject1 = new Threa1();
Thread getObject2 = new Threa1();
getObject1.start();
getObject2.start();
System.out.println("\n\n");
System.out.println("线程不安全 - 单线程");
System.out.println(Student.getStudent());
System.out.println(Student.getStudent());
}
1.2 懒汉式 – 需要在生成单例对象 – 线程安全–单检查
class Threa1 extends Thread {
@Override
public void run() {
System.out.println(Student.getStudent());
}
}
class Student {
private static Student student;
private Student() {}
// 这句跟下面的注释代码是一样的,都是静态同步方法都是锁 Student.class对象
public synchronized static Student getStudent() {
if(student == null) {
return ( student = new Student() );
}
return student;
}
/* public static Student getStudent() {
synchronized( Student.class ) {
if(student == null) {
return ( student = new Student() );
}
return student;
}
}
*/
}
//-----------------------------
//测试代码
public static void main() {
System.out.println("线程安全 - 多线程");
Thread getObject1 = new Threa1();
Thread getObject2 = new Threa1();
getObject1.start();
getObject2.start();
System.out.println("\n\n");
System.out.println("线程安全 - 单线程");
System.out.println(Student.getStudent());
System.out.println(Student.getStudent());
}
1.3 懒汉式 – 需要在生成单例对象 – 线程安全–双重检查
注意: 对象获取锁非常耗资源,我们尽可能减少不必要的获取锁
class Student {
private static Student student;
private Student() {}
public static Student getStudent1() {
synchronized( Student.class ) {
if(student == null) {
return ( student = new Student() );
}
return student;
}
}
public static Student getStudent2() {
if( student == null ) {
synchronized( Student.class ) {
if(student == null) {
return ( student = new Student() );
}
}
}
return student;
}
}
推导为什么 getStudent2() 的性能比 getStudent1() 性能高 ?
1. 假设有 100个线程同时进行访问 静态getStudentn() 方法2. 当线程run, 都调用getStudent1() – 则100个线程在运行的时候都需要获得同步锁。
3. 当线程run, 都调用getStudent2() – 则可能有几个线程在运行的时候获得锁,而其他因为知道已经有单例对象,而直接返回单例对象,无需在获得同步锁。
2. 饿汉式 – 已经是线程安全
2.1 使用时单例加载
class Student2 {
private static final Student2 student = new Student2();
private Student2() {
}
public Student2 getStudent() {
return student;
}
}
2.2 调用getStudent()时单例才加载
注意类只有使用的时候才加载二进制文件进入内存
class Student3 {
private static class Inner{
private static Student3 student = new Student3();
}
private Student3() {}
// 1. 当有线程调用该方法时,才会由静态内部类Inner创建初始化单例对象
public Student3 getStudent() {
return Inner.student;
}
}