多线程、单例模式的学习

第三十二章 多线程、单例模式


提纲

  • 32.1 多线程
    • 32.1.1 什么是并行和并发
    • 32.1.2 什么是多进程
    • 31.1.3 什么是多线程
    • 31.1.4 实现线程的两种方式
    • 31.1.5 线程同步
    • 31.1.6 线程的生命周期
  • 32.2 单例模式
    • 32.2.1 什么是单例模式
    • 32.2.2 为什么使用单例模式
    • 32.2.3 单例模式的特点
    • 32.2.4 单例模式的写法
    • 32.2.5 总结

32.1 多线程

  • 32.1.1 什么是并行和并发

    • 并行:指在某一个时间段内同时运行多个程序。
    • 并发:指在某一个时间点同时运行多个程序。
  • 32.1.2 什么是多进程:以Windows系统为例,Windows操作系统是多任务操作系统,它以进程为单位。系统可以分配给每个进程一段有限的使用CPU的时间(也可以称为CPU时间片),CPU在这段时间中执行某个进程,然后下一个时间片又跳至另一个进程中去执行。由于CPU转换较快,所以使得每个进程好像是同时执行一样。所以,Windows系统中,进程是并行的,即在某一个时间点只能执行一个进程。

  • 31.1.3 什么是多线程:一个线程是进程中的执行流程,一个进程可以同时包含多个线程,多个线程共享一个进程的资源。每个线程也可以得到一小段程序的执行时间,但是多线程也是并行而不是并发的,所以一个时间点也只能运行一个线程。

  • 31.1.4 实现线程的两种方式:继承java.lang.Thread类与实现java.lang.Runnable接口。

    • 继承java.lang.Thread类
      1. 构造方法:
        • Thread():分配新的 Thread 对象。
        • Thread(String name):创建一个名为name的线程对象。
        • Thread(Runnable target):通过Runnable接口分配新的 Thread 对象。
        • Thread(Runnable target, String name):通过Runnable接口创建一个名为name的线程对象。
      2. 常用方法:
        • run():如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。返回值:void。
        • start():使该线程开始执行;Java 虚拟机调用该线程的 run 方法。返回值:void。
        • sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。返回值:static void。
        • join():等待该线程终止。返回值:void。
        • join(long millis):等待该线程终止的时间最长为 millis 毫秒。返回值:void。
        • interrupt():中断线程。返回值:void。
        • interrupted() :测试当前线程是否已经中断。返回值:static boolean。
        • currentThread() :返回对当前正在执行的线程对象的引用。返回值:static Thread。
        • getId():返回该线程的标识符。返回值:long。
        • getName():返回该线程的名称。返回值:String。
        • getPriority():返回线程的优先级。返回值:int。
        • setName(String name):改变线程名称,使之与参数 name 相同。返回值:void。
        • setPriority(int newPriority):更改线程的优先级。返回值:void。
        • notify():唤醒在此对象监视器上等待的单个线程。返回值:void。
        • notifyAll(): 唤醒在此对象监视器上等待的所有线程。返回值:void。
        • wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。返回值:void。
        • wait(long timeout):在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。返回值:void。
      3. 字段摘要
        • MAX_PRIORITY:线程可以具有的最高优先级。变量类型:static int。
        • MIN_PRIORITY:线程可以具有的最低优先级。变量类型:static int。
        • NORM_PRIORITY:分配给线程的默认优先级。变量类型:static int。
      4. 使用步骤举例
        • 实现步骤:

          1. 继承Thread 类
          2. 重写run方法 在run方法写上自己想执行的代码 因为线程执行的代码都在run方法里面
          3. 创建线程类对象,
            • 这里是(MyThread mt = new MyThread();)
            • 然后将mt交给Thread(Thread tr = new Thread(mt);)
          4. 启动线程
        • 实例:

            //创建线程
            public class MyThread extends Thread{
            	public void run() {
            		Thread th = Thread.currentThread();//获取当前线程
            		//Thread.currentThread().getName()获取当前线程的名字
            		System.out.println("当前线程的名字:"+th.getName());
            		th.setName("MyThread的线程");//设置当前线程的名称
            		System.out.println("设置线程名字后:"+th.getName());
            		System.out.println("线程的标识符:"+th.getId());
            		System.out.println("线程的优先级:"+th.getPriority());
            		System.out.println("最高优先级:"+Thread.MAX_PRIORITY);
            		System.out.println("最低优先级:"+Thread.MIN_PRIORITY);
            		System.out.println("默认优先级:"+Thread.NORM_PRIORITY);
            		for (int i = 0; i < 10; i++) {
            			try {
            				Thread.sleep(500);//线程等待,参数传毫秒数
            				System.out.print(i+" ");
            			} catch (InterruptedException e) {
            				e.printStackTrace();
            			}
            		}
            	}
            }
            //测试线程
            public static void main(String[] args) {
            	MyThread mt = new MyThread();
            	mt.start();//启动线程
            }
            //执行结果
            当前线程的名字:Thread-0
            设置线程名字后:MyThread的线程
            线程的标识符:11
            线程的优先级:5
            最高优先级:10
            最低优先级:1
            默认优先级:5
            0 1 2 3 4 5 6 7 8 9 
          
    • 实现java.lang.Runnable接口
      1. 里面只有一个方法:run():使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。

      2. 使用步骤:

        1. 实现Runnable接口
        2. 重写run方法
        3. 创建线程类对象,这里是(MyRunnable mr = new MyRunnable();)
        4. 创建Thread类对象,然后将(这里是mr)参数传入。(Thread tr = new Thread(mr);)
        5. 启动线程
      3. 实例

         //重新编写继承Thread类
         public class MyThread extends Thread{
         	//实现线程 分两种
         	//线程在一个时间点只能运行一个
         	//第一种(步骤):
         	//1、继承Thread 类
         	//2、重写run方法 在run方法写上自己想执行的代码 因为线程执行的代码都在run方法里面
         	//3、创建线程类对象,这里是(MyThread mt = new MyThread();)
         	//4、启动线程
         	//第二种:
         	//1、实现Runnable接口
         	//2、重写run方法
         	//3、a.创建线程类对象,这里是(MyRunnable mr = new MyRunnable();)
         	//	 b.创建Thread类对象,然后将(这里是mr)参数传入。(Thread tr = new Thread(mr);)
         	//4、启动线程
         	public void run() {
         		Thread th = Thread.currentThread();//获取当前线程
         		//Thread.currentThread().getName()获取当前线程的名字
         		System.out.println("当前线程的名字:"+th.getName());
         		th.setName("MyThread的线程");//设置当前线程的名称
         		System.out.println(th.getName() + ":" + "设置线程名字后:"+th.getName());
         		System.out.println(th.getName() + ":" + "线程的标识符:"+th.getId());
         		System.out.println(th.getName() + ":" + "线程的优先级:"+th.getPriority());
         		System.out.println(th.getName() + ":" + "最高优先级:"+Thread.MAX_PRIORITY);
         		System.out.println(th.getName() + ":" + "最低优先级:"+Thread.MIN_PRIORITY);
         		System.out.println(th.getName() + ":" + "默认优先级:"+Thread.NORM_PRIORITY);
         		for (int i = 0; i < 10; i++) {
         			try {
         				Thread.sleep(1000);//线程等待,参数传毫秒数
         				System.out.println(th.getName() + ":" + i);
         			} catch (InterruptedException e) {
         				e.printStackTrace();
         			}
         		}
         	}
         }
         //编写实现Runnable接口类
         public class MyRunnable implements Runnable {
        
         	public void run() {
         		Thread th = Thread.currentThread();// 获取当前线程
         		// Thread.currentThread().getName()获取当前线程的名字
         		System.out.println("当前线程的名字:" + th.getName());
         		th.setName("MyRunnable的线程");// 设置当前线程的名称
         		System.out.println(th.getName() + ":" + "设置线程名字后:" + th.getName());
         		System.out.println(th.getName() + ":" + "线程的标识符:" + th.getId());
         		System.out.println(th.getName() + ":" + "线程的优先级:" + th.getPriority());
         		System.out.println(th.getName() + ":" + "最高优先级:" + Thread.MAX_PRIORITY);
         		System.out.println(th.getName() + ":" + "最低优先级:" + Thread.MIN_PRIORITY);
         		System.out.println(th.getName() + ":" + "默认优先级:" + Thread.NORM_PRIORITY);
         		for (int i = 10; i < 20; i++) {
         			try {
         				Thread.sleep(1000);
         				System.out.println(th.getName() + ":" + i);
         			} catch (Exception e) {
         				e.printStackTrace();
         			}
         
         		}
         	}
         }
         //执行结果
         当前线程的名字:Thread-0
         MyThread的线程:设置线程名字后:MyThread的线程
         MyThread的线程:线程的标识符:11
         MyThread的线程:线程的优先级:5
         MyThread的线程:最高优先级:10
         MyThread的线程:最低优先级:1
         MyThread的线程:默认优先级:5
         当前线程的名字:Thread-1
         MyRunnable的线程:设置线程名字后:MyRunnable的线程
         MyRunnable的线程:线程的标识符:12
         MyRunnable的线程:线程的优先级:5
         MyRunnable的线程:最高优先级:10
         MyRunnable的线程:最低优先级:1
         MyRunnable的线程:默认优先级:5
         MyThread的线程:0
         MyRunnable的线程:10
         MyThread的线程:1
         MyRunnable的线程:11
         MyThread的线程:2
         MyRunnable的线程:12
         MyThread的线程:3
         MyRunnable的线程:13
         MyThread的线程:4
         MyRunnable的线程:14
         MyThread的线程:5
         MyRunnable的线程:15
         MyThread的线程:6
         MyRunnable的线程:16
         MyThread的线程:7
         MyRunnable的线程:17
         MyRunnable的线程:18
         MyThread的线程:8
         MyThread的线程:9
         MyRunnable的线程:19
        
  • 31.1.5 线程同步:synchronized。同步关键字,修饰方法或者代码块,保证这个代码块或者方法每次只有一个线程在执行。

    • 同步方法举例:

        //取钱类
        public class Account {
        	static double money = 1000000;
        	/**
        	 * 取钱
        	 * @param qMoney 每次取多少钱
        	 */
        	
        	public synchronized void getMoney(double qMoney){
        		money -= qMoney;
        	}
        }
        //取钱的线程
        public class AccountThread extends Thread{
        	private Account account;
        	public AccountThread(Account account) {
        		this.account = account;
        	}
        	
        	public void run() {
        		//取50000次
        		for (int i = 0; i < 50000; i++) {
        			account.getMoney(10);
        		}
        	}
        }
        //测试类
        public class AccountTest {
        	public static void main(String[] args) {
        		Account account = new Account();
        		AccountThread at1 = new AccountThread(account);
        		AccountThread at2 = new AccountThread(account);
        		at1.start();
        		at2.start();
        		try {
        			Thread.sleep(2000);
        		} catch (InterruptedException e) {
        			e.printStackTrace();
        		}
        		System.out.println("剩下的钱:"+Account.money);
        	}
        }
      
    • 同步代码块举例:

      • 举例1:

          public class ThreadSafeTest extends Thread{
          	public void run() {
          		//其中this代表创建的本线程对象
          		synchronized (this) {
          			for (int i = 0; i < 2; i++) {
          				try {
          					Thread.sleep(1000);
          				} catch (InterruptedException e) {
          					e.printStackTrace();
          				}
          				System.out.println(Thread.currentThread().getName()+":"+i);
          			}
          		}
          	}
          	public static void main(String[] args) {
          		ThreadSafeTest tst = new ThreadSafeTest();
          		Thread t1 = new Thread(tst, "Thread1");
          		Thread t2 = new Thread(tst, "Thread2");
          		t1.start();
          		t2.start();
          	}
          }
          //执行结果
          Thread1:0
          Thread1:1
          Thread2:0
          Thread2:1
        

    结论:执行结果中,可能是Thread1先执行,也可能是Thread2先执行,所以线程之间也是在抢CPU的资源。谁先抢到谁先执行。
    - 举例2:如果将调用方式修改一下

      		public static void main(String[] args) {
      			ThreadSafeTest tst1 = new ThreadSafeTest();
      			ThreadSafeTest tst2 = new ThreadSafeTest();
      			Thread t1 = new Thread(tst1, "Thread1");
      			Thread t2 = new Thread(tst2, "Thread2");
      			t1.start();
      			t2.start();
      		}
      		//执行结果
      		Thread2:0
      		Thread1:0
      		Thread1:1
      		Thread2:1
      结论:如果创建了两个ThreadSafeTest则会按照两个不同的线程进行同步,即例中的this是不同的。所以会同时运行。
    
  • 31.1.6 线程的生命周期

    • 线程的生命周期图
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kx80lOb0-1571042519388)(课上图片/线程的生命周期.png)]
    • 操作线程的常用方法:
      1. 线程的休眠:sleep(long millis)方法。millis为毫秒数,当调用这个方法后会使线程中的代码经过millis毫秒之后继续执行后续代码。

      2. 线程等待:wait()方法。即先将线程暂时挂起。然后让别的线程执行。

      3. 线程唤醒:notify()方法。即将挂起的线程随机唤醒一个,成为就绪状态,继续执行。

         //例:使用线程实现功能:在程序运行后,要求达到:
         //1.如果会议室为未满,则继续让人进去。
         //2.如果会议室满了之后,让人从会议室走出。3.无限重复1和2的过程。
         /**
          * 人类
          */
         public class Person {
         	static int pCount = 0;//会议室当前人数,会议室能坐20人
         	/**
         	 * 人入场
         	 */
         	public synchronized void in(){
         		if (pCount < 20) {
         			pCount += 1;//每次进来一个人
         			System.out.println("会议室未坐满,继续入场:"+pCount);
         		} else {
         			System.out.println("会议室人数已满,开会了N个小时,准备离场");
         			try {
         				this.notify();//【随机】通知另一个线程
         				this.wait();//挂起,线程等待
         			} catch (InterruptedException e) {
         				e.printStackTrace();
         			}
         		}
         	}
         	/**
         	 * 人离场
         	 */
         	public synchronized void leave(){//synchronized同步的关键字
         		if (pCount > 0) {
         			pCount -= 1;
         			System.out.println("会已开完,开始离场:"+pCount);
         		} else {
         			System.out.println("人员已全部离场,准备开始进场");
         			try {
         				this.notify();
         				this.wait();
         			} catch (InterruptedException e) {
         				e.printStackTrace();
         			}
         		}
         	}
         }
         /**
          * 入场的线程
          */
         public class InThread extends Thread{
         	private Person person;
         	public InThread(Person person) {
         		this.person = person;
         	}
         
         
         	public void run() {
         		while(true){
         			try {
         				Thread.sleep(1000);
         				person.in();//入场方法
         			} catch (InterruptedException e) {
         				e.printStackTrace();
         			}
         		}
         	}
         }
         /**
          * 离场的线程
          */
         public class LeaveThread extends Thread{
         	private Person person;
         	public LeaveThread(Person person) {
         		this.person = person;
         	}
         
         	public void run() {
         		while(true){
         			try {
         				person.leave();//为了让离场线程先运行
         				Thread.sleep(1000);
         			} catch (InterruptedException e) {
         				e.printStackTrace();
         			}
         		}
         	}
         }
         /**
          * 测试类测试线程
          */
         public class PersonTest {
         	public static void main(String[] args) {
         		Person person = new Person();
         		InThread it = new InThread(person);
         		it.start();
         		LeaveThread lt = new LeaveThread(person);
         		lt.start();
         	}
         }
        
      4. 线程的加入:join()方法:当某个线程使用join()方法加入到另一个线程时,另一个线程会等待该线程执行完毕后再继续执行。

         //创建线程ThreadA
         public class ThreadA extends Thread{
         	private ThreadB threadB;
         	public ThreadA(ThreadB threadB) {
         		this.threadB = threadB;
         	}
         
         	public void run() {
         		for (int i = 0; i < 5; i++) {
         			try {
         				Thread.sleep(1000);
         				System.out.println(Thread.currentThread().getName()+":"+i);
         				if (i == 2) {//i=2时,加入B线程
         					threadB.start();
         					threadB.join();//在打印一次后加入ThreadB线程
         				}
         			} catch (InterruptedException e) {
         				e.printStackTrace();
         			}
         		}
         	}
         }
         //创建线程ThreadB
         public class ThreadB extends Thread{
         	public void run() {
         		Thread.currentThread().setName("ThreadB");
         		for (int i = 'A'; i < 'F'; i++) {
         			try {
         				Thread.sleep(1000);
         			} catch (InterruptedException e) {
         				e.printStackTrace();
         			}
         			System.out.println(Thread.currentThread().getName()+":"+(char)i);
         		}
         	}
         }
         //测试
         public class TestAB {
         	public static void main(String[] args) {
         		Thread threadA = new Thread(new ThreadA(new ThreadB()), "threadA");
         		threadA.start();
         	}
         }
        
      5. 线程的中断:interrupt()方法

         public class InterruptedThread extends Thread {
         	public void run() {
         		for (int i = 0; i < 5; i++) {
         			try {
         				System.out.println(Thread.currentThread().getName()+":"+i);
         				Thread.sleep(1000);
         			} catch (InterruptedException e) {
         				System.out.println("当前线程被中断");
         				break;
         			}
         		}
         	}
         	public static void main(String[] args) {
         		InterruptedThread it = new InterruptedThread();
         		Thread thread = new Thread(it, "it");
         		thread.start();
         		thread.interrupt();
         	}
         }
         //执行结果
         it:0
         当前线程被中断
        
      6. 线程的优先级(注意:优先级越大,最先的几率越大!):

         //线程
         public class PriorityThread implements Runnable{
        
         	@Override
         	public void run() {
         		try {
         			for (int i = 0; i < 10; i++) {
         				System.out.println(Thread.currentThread().getName()+":"+i);
         				Thread.sleep(1000);
         			}
         		} catch (InterruptedException e) {
         			e.printStackTrace();
         		}
         	}
         }
         //测试类
         public class PriorityTest {
         	public static void setPriority(String threadName, int priority, Thread t){
         		t.setPriority(priority);
         		t.setName(threadName);
         		t.start();
         	}
         	public static void main(String[] args) {
         		Thread threadA = new Thread(new PriorityThread());
         		Thread threadB = new Thread(new PriorityThread());
         		Thread threadC = new Thread(new PriorityThread());
         		Thread threadD = new Thread(new PriorityThread());
         		PriorityTest.setPriority("threadA", Thread.MAX_PRIORITY, threadA);
         		PriorityTest.setPriority("threadB", Thread.MIN_PRIORITY, threadB);
         		PriorityTest.setPriority("threadC", Thread.NORM_PRIORITY, threadC);
         		PriorityTest.setPriority("threadD", 3, threadD);
         	}
         }
        

32.2 单例模式

  • 32.2.1 什么是单例模式:Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在。一些管理器和控制器常被设计成单例模式。
  • 32.2.2 为什么使用单例模式: 单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间;能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。
  • 32.2.3 单例模式的特点:
    1. 单例类只能有一个实例。
    2. 单例类必须自己创建自己的唯一实例。
    3. 单例类必须给所有其他对象提供这一实例。
  • 32.2.4 单例模式的写法:单例模式有很多种写法,大部分写法都或多或少有一些不足。下面将分别对这几种写法进行介绍:
    1. 饿汉模式(常用)
      • 代码:

          public class Singleton{  
              private static Singleton instance = new Singleton();  
              private Singleton(){
          	
          	}  
              public static Singleton newInstance(){  
                  return instance;  
              }  
          }
        
      • 优缺点:

        • 优点:
          1. 从代码中我们看到,类的构造函数定义为private的,保证其他类不能实例化此类,然后提供了一个静态实例并返回给调用者。
          2. 饿汉模式是最简单的一种实现方式。
          3. 只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。
        • 缺点:即使这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
        • 适用场合:这种实现方式适合单例占用内存比较小,在初始化时就会被用到的情况。但是,如果单例占用的内存比较大,或单例只是在某个特定场景下才会用到,使用饿汉模式就不合适了。
    2. 懒汉模式
      • 代码:
        • 普通模式(线程不安全):
          • 代码:

              public class Singleton{  
                  private static Singleton instance = null;  
                  private Singleton(){}  
                  public static Singleton newInstance(){  
                      if(null == instance){  
                          instance = new Singleton();  
                      }  
                      return instance;  
                  }  
              }
            
          • 优缺点:

            • 优点:懒汉模式中单例是在需要的时候才去创建的,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。
            • 缺点:但是这里的懒汉模式并没有考虑线程安全问题,在多个线程可能会并发调用它的getInstance()方法,将导致创建多个实例。
            • 使用场合:如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建,这个时候使用懒汉模式就是一个不错的选择。
        • synchronized关键字同步模式(线程安全):
          • 代码:

              public class Singleton{  
                  private static Singleton instance = null;  
                  private Singleton(){}  
                  public static synchronized Singleton newInstance(){  
                      if(null == instance){  
                          instance = new Singleton();  
                      }  
                      return instance;  
                  }  
              }  
            
          • 优缺点:

            • 优点:加锁的懒汉模式看起来即解决了线程并发问题,又实现了延迟加载。
            • 缺点:synchronized修饰的同步方法比一般方法要慢很多,如果多次调用getInstance(),累积的性能损耗就比较大了。
    3. 双重校验锁模式:
      • 未禁止指令重排序优化模式:
        • 代码:

            public class Singleton {  
                private static Singleton instance = null;  
                private Singleton(){}  
                public static Singleton getInstance() {  
                    if (instance == null) {  
                        synchronized (Singleton.class) {  
                            if (instance == null) {//2  
                                instance = new Singleton();  
                            }  
                        }  
                    }  
                    return instance;  
                }  
            }
          
        • 优缺点:

          • 优点:
            1. 可以看到上面在同步代码块外多了一层instance为空的判断。
            2. 由于单例对象只需要创建一次,如果后面再次调用getInstance()只需要直接返回单例对象。因此,大部分情况下,调用getInstance()都不会执行到同步代码块,从而提高了程序性能。
            3. 不过还需要考虑一种情况,假如两个线程A、B,A执行了if (instance == null)语句,它会认为单例对象没有创建,此时线程切到B也执行了同样的语句,B也认为单例对象没有创建,然后两个线程依次执行同步代码块,并分别创建了一个单例对象。为了解决这个问题,还需要在同步代码块中增加if (instance == null)语句,也就是上面看到的代码2。
          • 缺点:这个问题的关键就在于由于指令重排优化的存在,导致初始化Singleton和将对象地址赋给instance字段的顺序是不确定的。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。(所谓指令重排优化是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快。JVM中并没有规定编译器优化相关的内容,也就是说JVM可以自由的进行指令重排序的优化。)
      • 禁止指令重排序优化模式:(常用)
        • 代码:

            public class Singleton {  
                private static volatile Singleton instance = null;  
                private Singleton(){}  
                public static Singleton getInstance() {  
                    if (instance == null) {  
                        synchronized (Singleton.class) {  
                            if (instance == null) {  
                                instance = new Singleton();  
                            }  
                        }  
                    }  
                    return instance;  
                }  
            }
          
        • 优缺点:

          • 优点:保证了instance变量被赋值的时候对象已经是初始化过的,从而避免了上面说到的问题。
    4. 静态内部类模式:
      • 代码:

          public class Singleton{  
              private static class SingletonHolder{  
                  public static Singleton instance = new Singleton();  
              }  
              private Singleton(){}  
              public static Singleton newInstance(){  
                  return SingletonHolder.instance;  
              }  
          } 
        
      • 优缺点:

        • 优点:只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
  • 32.2.5 总结:四种Java中实现单例的方法,其中前两种都不够完美,双重校验锁和静态内部类的方式可以解决大部分问题,平时工作中使用的最多的也是这两种方式。枚举方式虽然很完美的解决了各种问题,但是这种写法多少让人感觉有些生疏。个人的建议是,在没有特殊需求的情况下,使用第三种和第四种方式实现单例模式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值