JAVA多线程编程核心技术

文章目录

(一)、java多线程

在这里插入图片描述
线程进入Runnable状态的情况:

  1. 调用sleep()方法之后,时间超过了指定的休眠时间
  2. 执行阻塞IO已经完成,阻塞方法已经完成。
  3. 线程获取了试图同步的监视器
  4. 线程等待通知,其他线程发出了通知
  5. 处于挂起状态的线程调用了resume()方法

线程出现block状态的情况:

  1. 线程调用sleep(),主动放弃CPU资源
  2. 执行阻塞IO,在方法返回前,处于阻塞状态
  3. 线程试图获取同步的监视器,但该监视器被其他线程占有
  4. 线程等待某个通知
  5. 线程调用suspend()方法挂起。此方法容易导致死锁,尽量不使用

1、进程和线程

进程: 操作系统分配资源的最小单元。
线程: 程序执行的最小单元。

2、多线程编程

1、继承Thread类

public class Thread implements Runnable {

缺点: 使用Thread方法创建新线程的时候,最大的局限就是不支持多继承。

2、实现Runable接口

public interface Runnable {

3、实例变量和线程安全

自定义线程类中的实例变量针对其他线程有共享非共享之分。

1、非共享实例变量
//自定义线程类
public class MyThread extends Thread {

    private int myInt = 6;

    public MyThread(String name){
        super();
        this.setName(name);

    }

    @Override
    public void run(){
        while(myInt >0){
            myInt --;
            System.out.println(Thread.currentThread().getName()+":"+myInt);
        }
    }
}
public static void main(String args[]) throws InterruptedException {

		//创建了三个线程对象
        Thread thread = new MyThread("A");
        Thread thread1 = new MyThread("B");
        Thread thread2 = new MyThread("C");

        thread.start();
        thread1.start();
        thread2.start();


    }

输出:

A:5
A:4
A:3
A:2
A:1
A:0
B:5
C:5
B:4
C:4
B:3
C:3
B:2
C:2
B:1
C:1
C:0
B:0
2、共享实例变量
public class MyThread extends Thread {

    private int myInt = 6;


    @Override
    public void run(){
        myInt --;
        System.out.println(Thread.currentThread().getName()+":"+myInt);
    }
}
public static void main(String args[]) throws InterruptedException {

		//只创建一个自定义线程类
       Thread myThread = new MyThread();

       Thread thread1 = new Thread(myThread,"A");
       Thread thread2 = new Thread(myThread,"B");
       Thread thread3 = new Thread(myThread,"C");
       Thread thread4 = new Thread(myThread,"C");
       Thread thread5 = new Thread(myThread,"C");

       thread1.start();
       thread2.start();
       thread3.start();
       thread4.start();
       thread5.start();

   }

输出:

A:4
B:4
C:3
C:2
C:1

在这样的情况下就产生了非线程安全的问题。
解决的方法就是在会产生非线程安全的方法或代码块上添加Synchronized 进行修饰。

public class MyThread extends Thread {

    private int myInt = 6;


    @Override
    synchronized public void run(){
        myInt --;
        System.out.println(Thread.currentThread().getName()+":"+myInt);
    }
}

在多个线程执行run方法的时候,run方法上有synchronized修饰词,那么线程排队执行,当线程获得该方法的锁的时候,才能执行,否则就要等待其他线程执行完之后释放锁,再获取到锁的时候才能开始执行。加锁的的这个区域叫做 “临界区” 或者 "互斥区"

3、Thread.currentThread().isAlive()和this.isAlive()的区别
Thread.currentThread().isAlive()this.isAlive()
当前的线程是否处于就绪状态或者正在执行状态这个线程类是否处于就绪状态或者正在执行状态

下面举个例子:

package com.fang.main;
/**
** 自定义线程类
**/
public class MyThread extends Thread {

    private int myInt = 6;

    MyThread(){
        System.out.println("Thread-----Start");
        System.out.println("Thread.currentThread().getName()---"+Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()---"+Thread.currentThread().isAlive());
        System.out.println("this.getName()---"+this.getName());
        System.out.println("this.isAlive()---"+this.isAlive());
        System.out.println("Thread-----End");
    }


    @Override
    public void run(){
        System.out.println("Run-----Start");
        System.out.println("Thread.currentThread().getName()---"+Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()---"+Thread.currentThread().isAlive());
        System.out.println("this.getName()---"+this.getName());
        System.out.println("this.isAlive()---"+this.isAlive());
        System.out.println("Run-----End");

    }
}

(1)、第一种情况:将自定义线程类通过构造函数传递给Thread类,调用start()

public static void main(String args[]) throws InterruptedException {

	   MyThread myThread = new MyThread();
	   Thread thread = new Thread(myThread);
	   System.out.println("main---Start---"+thread.isAlive());
	   thread.setName("A");
	   thread.start();
	   System.out.println("main---End---"+thread.isAlive());

   }

通过以下输出,我们可以清楚的看到:

  1. 自定义线程类的构造函数是被main线程调用
  2. 因为是通过构造函数传参给Thread类,再调用start()之后,可以看到this.isAlive()是false,说明自定义线程类是没有在就绪态和运行态的。
  3. 其实通过Thread.currentThread().getName()this.getName()的输出就可以看出Thread.currentThread().isAlive()和this.isAlive()调用的不是同一个类。
Thread-----Start
Thread.currentThread().getName()---main
Thread.currentThread().isAlive()---true
this.getName()---Thread-0
this.isAlive()---false
Thread-----End
main---Start---false
main---End---true
Run-----Start
Thread.currentThread().getName()---A
Thread.currentThread().isAlive()---true
this.getName()---Thread-0
this.isAlive()---false
Run-----End

(2)、直接调用自定义线程类的start()方法。

public static void main(String args[]) throws InterruptedException {

     MyThread myThread = new MyThread();
     //Thread thread = new Thread(myThread);
     System.out.println("main---Start---"+myThread.isAlive());
     myThread.setName("A");
     myThread.start();
     System.out.println("main---End---"+myThread.isAlive());

 }

通过以下输出可以看出,直接调用自定义线程类的start()方法,Thread.currentThread().getName()this.getName()的输出就可以轻易看出是同一个线程类。

Thread-----Start
Thread.currentThread().getName()---main
Thread.currentThread().isAlive()---true
this.getName()---Thread-0
this.isAlive()---false
Thread-----End
main---Start---false
main---End---true
Run-----Start
Thread.currentThread().getName()---A
Thread.currentThread().isAlive()---true
this.getName()---A
this.isAlive()---true
Run-----End
4、interrupted()和isInterrupted()
1、interrupted()

该方法测试线程是否中断,线程的中断状态由它来清除。

public static void main(String args[]) throws InterruptedException {

    Thread.currentThread().interrupt();
    System.out.println(Thread.interrupted());
    System.out.println(Thread.interrupted());

}

输出为下:

true
false
2、isInterrupted()

该方法测试线程是否中断,但不清除线程状态。

public static void main(String args[]) throws InterruptedException {

    MyThread thread = new MyThread();
    thread.start();
    thread.interrupt();
    System.out.println(thread.isInterrupted());
    System.out.println(thread.isInterrupted());

}

以下为输出

true
true
5、stop()

该方法已被舍弃,因为强行停止一个线程会导致清理性工作得不到正常的完成,或者对于锁定的对象进行解锁,导致数据得不到同步处理。

创建一个实体类,是数据同步操作的对象

public class SynchronizedObject {

    private String username = "a";
    private String password = "aa";

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    @Override public String toString() {
        return "SynchronizedObject{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}';
    }

    public void printObject(String name,String password){
        try {
            this.username = name;
            Thread.sleep(5000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class MyThread extends Thread {

    public SynchronizedObject syObject;

    MyThread(SynchronizedObject o){
        this.syObject = o;
    }
    @Override
    public void run(){
        syObject.printObject("b","bb");
    }
}
  1. 先初始化SynchronizedObject(后面简称SO)对象
  2. 调用start()开启线程,通过线程MyThread(简称MY)类去修改SO对象的成员变量
  3. 主线程sleep(),MY类运行
  4. 调用SO的printObect()
  5. 修改完name之后sleep(),主线程开始执行stop()
  6. SO的printObject()还没开始执行修改password的时候被停止。
 public static void main(String args[]) throws InterruptedException {

     SynchronizedObject o = new SynchronizedObject();
     MyThread thread = new MyThread(o);
     thread.start();
     Thread.sleep(1000);
     thread.stop();
     System.out.println(o.toString());

 }

输出为下

SynchronizedObject{username='b', password='aa'}
6、suspend()和resume()
public class MyThread extends Thread {

    private long i = 0;

    public long getI() {
        return i;
    }

    public void setI(long i) {
        this.i = i;
    }


    @Override
    public void run(){
        while(true){
            i++;
        }
    }
    
}
public static void main(String args[]) throws InterruptedException {

    MyThread thread = new MyThread();
    thread.start();
    Thread.sleep(2000);

    //A部分
    thread.suspend();
    System.out.println(thread.getI());
    Thread.sleep(2000);
    System.out.println(thread.getI());

    //B部分
    thread.resume();
    Thread.sleep(2000);

    //C部分
    thread.suspend();
    System.out.println(thread.getI());
    Thread.sleep(2000);
    System.out.println(thread.getI());


}

输出为下: 从输出结果可以看出suspend()和resume()是可以进行线程的暂停和恢复原来的状态的。

1106032227
1106032227
2369042974
2369042974
7、suspend()和resume()的问题:独占
public class MyThread extends Thread {

   private long i = 0;

   @Override
   public void run(){
       while(true){
           System.out.println(i);
           i++;
       }
}
  public static void main(String args[]) throws InterruptedException {

      MyThread thread = new MyThread();
      thread.start();
      Thread.sleep(1000);
      thread.suspend();
      System.out.println("mian--end");
  }

Mythread一直运行print(),之后suspend(),print()的同步锁一直没有释放,导致 System.out.println("mian--end"); 一直没法输出。

112071
112072
112073
112074
112075
112076
112077
 public void println(String x) {
     synchronized (this) {
         print(x);
         newLine();
     }
 }
8、suspend()问题:不同步
package com.fang.main;

public class MyThread  {
    private String username = "1";
    private String password = "11";

    public void setValue(String username,String password){
        this.username = username;
        if (Thread.currentThread().getName().equals("a")){
            System.out.println("停止a线程!");
            Thread.currentThread().suspend();
        }
        this.password = password;
    }

    public void printString(){
        System.out.println("username:"+username+"====="+"password:"+password);
    }
}
 public static void main(String args[]) throws InterruptedException {

        MyThread thread = new MyThread();
        Thread thread1 = new Thread(){
            @Override public void run() {
                thread.setValue("a","aa");
            }
        };
        thread1.setName("a");
        thread1.start();
        Thread.sleep(1000);
        Thread thread2 = new Thread(){
            @Override public void run() {
                thread.printString();
            }
        };
        thread2.start();
    }

输出为下,suspend()导致线程未同步

停止a线程!
username:a=====password:11
9、yeild():放弃当前CPU资源,让给其他任务去占用CPU执行时间。
package com.fang.main;

public class MyThread extends Thread{

    @Override public void run() {
        long startTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i <5000000 ; i++) {
            //Thread.yield();
            count+= i;
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }
}
 public static void main(String args[]) throws InterruptedException {

     MyThread thread = new MyThread();
     thread.start();
 }

注释了Thread.yield();之后输出为:6
取消注释之后输出为:3862

10、线程优先级

在操作系统中,线程可以划分优先级,

  1. 优先级高的占有CPU资源较多,也就是CPU优先执行高优先级的线程中的任务。
  2. 线程优先级可以继承,比如A线程启动B线程,A、B线程的优先级是一样的。
  3. 线程优先级和代码的执行顺序无关,优先级高的线程不一定优先执行完。

设置线程优先级使用yield(),JDK代码如下:

 public final void setPriority(int newPriority) {
     ThreadGroup g;
     checkAccess();
     if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
         throw new IllegalArgumentException();
     }
     if((g = getThreadGroup()) != null) {
         if (newPriority > g.getMaxPriority()) {
             newPriority = g.getMaxPriority();
         }
         setPriority0(priority = newPriority);
     }
 }

在JAVA中,线程优先级分为1~10级,如果小于1或者大于10,则抛出异常:throw new IllegalArgumentException()

JDK中有以下三个常量设置优先级:

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;
11、守护线程

java中有两种线程:用户线程和守护线程。
任何一个守护线程都是整个JVM中非守护线程的"保姆",只要JVM中还有非守护线程没有结束,守护线程就还在工作,只有当最后一个非守护线程结束工作的时候,守护线程才会伴随JVM一同结束工作。典型的就是垃圾回收器(GC)。

(二)、对象及变量并发访问

(1)、synchronized同步方法

非线程安全:多个线程对同一个对象的变量进行并发访问,就会产生"脏读"问题,也就是数据被更改过。
线程安全:获得的实例变量的值是经过同步处理的,就不会发生脏读现象。

1、方法内的变量为线程安全

“非线程安全"一般出现在"实例变量”,而方法内的变量为私有变量,就不会出现线程安全问题。

2、实例变量非线程安全
3、多个对象多个锁

关键词sychronized取得的是对象的锁,而不是一段代码或者一个方法。哪个线程执行到了sychronized关键词的方法上,哪个线程就拥有该方法上的锁,其他对象只能是等待状态,前提是:多个线程访问的是同一个对象。如果是多个线程访问多个不同的对象,那么就会产生多个锁。

4、sychronized关键词

调用sychronized修饰的方法,一定是排队执行的。只有共享的读写访问才进行同步操作,如果不是共享资源,就没必要进行同步操作。

  1. 当A线程调用O对象有关键词sychronized修饰的方法X的时候,A线程就获得了O对象对应的锁,所以其他线程必须等线程A执行完之后才能调用X方法,但是其他线程可以调用其他非sychronized修饰的方法。
  2. 当A线程调用O对象有关键词sychronized修饰的方法X的时候,如果B线程访问O对象带有sychronized修饰符的方法的时候,就必须等待A对象执行完方法X,释放对象锁,B线程才可以调用。
5、sychronized锁重入(自己可以再次获取自己的内部锁)

关键词schronized拥有重入功能,当一个线程获得了一个对象的线程锁之后,再次请求该对象锁的时候是可以再次获取到的。这也证明在一个sychronized方法/块的内部调用该类中其他sychronized方法/块的时候,是永远可以获得锁的。

6、出现异常,锁自动释放
7、同步不具有继承性
8、sychronized同步语句块
  1. 不在sychronized同步代码块异步执行,在sychronized同步代码块中同步执行
  2. 当一个线程访问sychronized同步代码块的时候,其他线程对同一个对象的其他sychronized同步代码块的访问将被阻塞,这也说明sychronized使用的"对象监视器"是一个。
9、sychronized总结
  1. 当多个线程访问sychronized(非this对象X){}同步代码块的时候呈同步效果,
  2. 多个线程访问sychronized同步方法的时候呈同步效果。
  3. 多个线程访问sychronized(this)同步代码块的时候呈同步效果
  4. 线程还是可以异步调用无sychronized修饰的方法。
  5. sychronized加在static静态方法上是对Class上锁,而加在非静态方法上是给对象上锁
  6. sychronized(Class)sychronized static是一样的,都是对Class上锁
  7. 多个线程持有相同对象锁,他们就是同步操作,如果是持有不同锁对象,那么他们是异步操作。
(2)、volatile关键词:使变量在多个线程可见:强制从公共堆栈中获取变量的值,而不是获取线程私有变量的值。缺点:不支持原子性。
1、线程的私有堆栈

线程的私有堆栈

2、volatile读取公共内存

在这里插入图片描述

3、sychronizedvolatile的比较
sychonizedvolatile
性能较差线程同步的轻量级实现,性能比较好
修饰方法和代码块只能修饰变量
多线程访问会发生阻塞多线程访问不会发生阻塞
可以保证原子性,也可以间接保证数据可见性,因为他会将私有内存和公共内存中的数据同步能保证数据的可见性,不能保证原子性
解决的是多个线程访问资源的同步性解决的是变量在多个线程中的可见性
4、线程安全

线程安全包括原子性和可见性两个方面,java的同步机制都是围绕这两个方面来确保线程安全的。

5、关键词volatile主要的使用场景
  1. 关键词volatile主要的使用场景是多个线程可以感知变量的值进行改变了,进而可以获取到更新过后的变量值,也就是用多线程获取共享变量时可以获得最新的值使用。
  2. volatile提醒线程每次从共享内存中获取值,而不是私有内存中读取,这样就保证了同步数据的可见性。

变量在内存中的工作图:

  1. read和load阶段,从主存中复制变量到当前线程工作内存
  2. user和assign阶段,执行代码,改变共享变量值
  3. store和write阶段,用工作内存数据刷新主存对应变量值

在这里插入图片描述

  1. 多线程环境中,use和asign是多次出现的,但这一操作不是原子性的,在read和load之后,主内存的count值改变的时候,线程工作内存由于已经加载完成了,不会产生对应的变化,也就是私有内存和公共内存的变量不一致,所以计算结果和预期结果不相同,就导致了非线程安全问题。
  2. 对于使用volatile修饰的变量,JVM只保证从主内存读入私有内存的值是最新的,例如线程1和线程2在进行read和load操作的时候,发现主内存中的count都是5,那么他们就把这个最新的值读入私有内存中。
  3. volatile关键词解决的是关键词读取时候的可见性问题,而无法保证原子性,要保证原子性还是必须进行同步处理。
6、sychronized总结(外练互斥,内修可见)

关键词sychronized可以保证同一时刻,同步方法和同步代码块只由一个线程执行,它具有两个特性:互斥性和可见性。sychronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步代码块和同步方法的每一个线程,都可以看到由同一个锁保护之前的所有修改效果。

(三)、线程间通信

(一)、等待/通知机制

1、什么是等待/通知

前面的多个线程也可以实现通信,原因是多个线程访问共享变量,但是那种机制不是"等待/通知",每个线程都是主动式的去获取共享变量,而获取到的变量是不是想要的,并不能完全确定。

2、等待/通知机制的实现

wait()方法让执行该方法的线程进入等待状态,wait()是Object类的方法,该方法让线程进入"预执行队列"中,并且在wait处停止执行,直到接受的通知或者终止。

  1. 在调用wait()时,应该获得该对象的对象级别锁,即只有同步方法和同步代码块中调用wait(),执行完wait()之后,当前线程释放锁。如果调用wait()没有对应的锁,那么就抛出IllegalMoniterStateException()异常
  2. notify()的调用也要在同步方法和同步代码块中,那么对应的也要获取到对应对象的锁。如果调用notify()没有对应的锁,那么也就抛出IllegalMoniterStateException()异常。该方法用来通知可能等待该对象的对象锁的其他线程,如果有多个线程等待,那么由线程规划器选择出一个呈wait态的线程,对其发出notity(),使它等待获取该对象的对象锁。 在执行完notify()方法之后,当前线程不会立马释放对象锁,而等待获取该对象锁的线程也不会立马获得对象锁,要等待执行notify()的线程执行完之后,也就是退出sychronized,才会释放对象锁,而呈等待态的线程也就可以获取到该对象锁。

3、生产者/消费者模式

等待/通知的模式对经典的案例就是生产者/消费者模式,虽然有几种变形,但都基于wait/notify。

4、通过管道进行线程中通信:字节流

java提供了很多输入输出流Stream,其中管道流(pipeStream)是一种特殊的流,用于在不同线程之间通信,而无需借助临时文件之类的东西。

  1. PipedInputStream和PipedOutputStream
  2. PipedReader和PipedWriter

2、方法join()

  1. 作用:使所属线程的对象x正常执行run()方法,而使当前线程z处于无限期的阻塞,等待线程x销毁后再执行z线程。
  2. 与sychronized的区别:join内部使用wait进行等待,而sychronized使用"对象监视器"原理作为同步

1、join(long)和sleep(long)

join释放锁,而sleep不释放锁。

2、ThreadLocal对象

类ThreadLocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值是可以放入ThreadLocal中保存的。

(四)、Lock的使用

1、ReentrantLock类

package com.fang.main;

import java.util.concurrent.locks.ReentrantLock;

public class MyService {

    ReentrantLock lock = new ReentrantLock();

    public void testMe(){
        lock.lock();
        for (int i= 0 ; i<5 ; i++){
            System.out.println(Thread.currentThread().getName()+"-----"+i);
        }
        lock.unlock();
    }

}
package com.fang.main;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread extends Thread {

    MyService service;
    MyThread(MyService service){
        this.service = service;
    }

    @Override public void run() {
        service.testMe();
    }
}
public static void main(String args[]) throws Exception {

    MyService myService = new MyService();
    MyThread myThread = new MyThread(myService);
    MyThread myThread2 = new MyThread(myService);
    MyThread myThread3 = new MyThread(myService);
    MyThread myThread4 = new MyThread(myService);
    MyThread myThread5 = new MyThread(myService);
    MyThread myThread6 = new MyThread(myService);
    myThread.start();
    myThread2.start();
    myThread3.start();
    myThread4.start();
    myThread5.start();
    myThread6.start();


}
Thread-0-----0
Thread-0-----1
Thread-0-----2
Thread-0-----3
Thread-0-----4
Thread-2-----0
Thread-2-----1
Thread-2-----2
Thread-2-----3
Thread-2-----4
Thread-1-----0
Thread-1-----1
Thread-1-----2
Thread-1-----3
Thread-1-----4
Thread-3-----0
Thread-3-----1
Thread-3-----2
Thread-3-----3
Thread-3-----4
Thread-5-----0
Thread-5-----1
Thread-5-----2
Thread-5-----3
Thread-5-----4
Thread-4-----0
Thread-4-----1
Thread-4-----2
Thread-4-----3
Thread-4-----4
  1. 从打印结果来看,当前线程打印完毕后将锁释放之后,其他线程才开始打印。
  2. 线程打印的数据是分组打印的,因为当前线程已经获得锁了,而线程之间的打印顺序是随机的。

2、Condition实现等待/通知

  1. Condition有更好的灵活性,比如可以实现多路通知功能,也就是可以在Lock对象里面可以创建多个Condition(对象监视器)实例,线程对象可以注册到指定的Condition上,从而可以有选择的进行线程通知,在调度线程上更加灵活。
  2. 使用notity()和notifyAll()进行通知时,被通知的线程是JVM随机选中的,但使用ReentrantLock和Condition是可以实现"选择性通知"的。
  3. 而sychronized就相当于整个Lock对象只有一个单一的Condition对象,所有线程都注册在这一个对象身上。线程调用notifyAll(),需要通知所有的WAITING线程,没有选择权,性能就会受到很大的影响。

3、Object和Condition比较

Objectcondition
wait(long timeout)await(long time , TimeUnit unit)
notify()signal()
notifyAll()signalAll()

(5)、定时器

(6)、单例模式和多线程

1、立即加载/ “饿汉模式”

在调用方法前,实例就已经被创建。

2、延迟加载/ “懒汉模式”

在调用方法的时候,实例才被创建

解决"懒汉模式"不是单例的情况:

  1. 对创建实例的方法进行sychronized处理(缺点:效率低下,是同步运行的)
  2. 使用同步代码块进行处理(缺点:效率低下)
  3. 针对创建实例的代码进行单独的同步处理(缺点:效率得到提神,但不能保证多线程安全性)
  4. 使用DCL双检查锁机制

3、解决"懒汉模式"不是单例的情况

1、使用DCL双检查锁机制
package com.fang.main;

public class MyObject {

    private volatile static MyObject myObject;

    MyObject(){
    }

    public static MyObject getInstance(){
        try {
            if (myObject != null){

            }else {
                Thread.sleep(3000);
                synchronized (MyObject.class){
                    if (myObject == null){
                        myObject = new MyObject();
                    }
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }
        return myObject;
    }

}
package com.fang.main;

public class MyThread extends Thread {

    private MyObject myObject;
    MyThread(MyObject myObject){
        this.myObject = myObject;
    }

    @Override public void run() {
        System.out.println(myObject.getInstance().hashCode());
    }
}
 public static void main(String args[]) throws Exception {

     MyObject myObject = new MyObject();
     MyThread myThread = new MyThread(myObject);
     MyThread myThread2 = new MyThread(myObject);
     MyThread myThread3 = new MyThread(myObject);
     myThread.start();
     myThread2.start();
     myThread3.start();


 }

控制台输出:

790066332
790066332
790066332
2、使用静态内置类实现单例模式
package com.fang.main;

public class MyObject {

    public static class MyObjectHandler{
        private static MyObject myObject = new MyObject();
    }

    public MyObject getObject(){
        return MyObjectHandler.myObject;
    }

}
package com.fang.main;

public class MyThread extends Thread {

    private MyObject myObject;
    MyThread(MyObject myObject){
        this.myObject = myObject;
    }

    @Override public void run() {
        System.out.println(myObject.getObject().hashCode());
    }
}
  public static void main(String args[]) throws Exception {

      MyObject myObject = new MyObject();
      MyThread myThread = new MyThread(myObject);
      MyThread myThread2 = new MyThread(myObject);
      MyThread myThread3 = new MyThread(myObject);
      myThread.start();
      myThread2.start();
      myThread3.start();

  }

输出:

1227738304
1227738304
1227738304
3、使用静态代码块实现单例模式

静态代码块中的代码在使用类之前已经执行

package com.fang.main;

public class MyObject {

   public static MyObject object = null;

   MyObject(){}

   static {
       object = new MyObject();
   }

   public MyObject getInstence(){
       return object;
   }

}
package com.fang.main;

public class MyThread extends Thread {

    private MyObject myObject;
    MyThread(MyObject myObject){
        this.myObject = myObject;
    }

    @Override public void run() {
        for (int i=0 ; i<5 ; i++){
            System.out.println(myObject.getInstence().hashCode());
        }
    }
}
 public static void main(String args[]) throws Exception {

      MyObject myObject = new MyObject();
      MyThread myThread = new MyThread(myObject);
      MyThread myThread2 = new MyThread(myObject);
      MyThread myThread3 = new MyThread(myObject);
      myThread.start();
      myThread2.start();
      myThread3.start();

  }
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
1227738304
4、使用enum枚举类实现

枚举类和静态代码块的性质类似,使用枚举类的时候,构造方法被自动调用

package com.fang.main;


public enum MyObject{

    object;
    private  MyService myService;
    MyObject(){
        myService = new MyService();
    }

    public MyService getInstence(){
        return myService;
    }
}
package com.fang.main;

public class MyThread extends Thread {

    @Override public void run() {
        for (int i=0 ; i<5 ; i++){
            System.out.println(MyObject.object.getInstence().hashCode());
        }
    }
}
 public static void main(String args[]) throws Exception {

     //MyObject myObject = new MyObject();
     MyThread myThread = new MyThread();
     MyThread myThread2 = new MyThread();
     MyThread myThread3 = new MyThread();
     myThread.start();
     myThread2.start();
     myThread3.start();

 }
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
2002802984
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值