文章目录
(一)、java多线程
线程进入Runnable状态的情况:
- 调用sleep()方法之后,时间超过了指定的休眠时间
- 执行阻塞IO已经完成,阻塞方法已经完成。
- 线程获取了试图同步的监视器
- 线程等待通知,其他线程发出了通知
- 处于挂起状态的线程调用了resume()方法
线程出现block状态的情况:
- 线程调用sleep(),主动放弃CPU资源
- 执行阻塞IO,在方法返回前,处于阻塞状态
- 线程试图获取同步的监视器,但该监视器被其他线程占有
- 线程等待某个通知
- 线程调用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());
}
通过以下输出,我们可以清楚的看到:
- 自定义线程类的构造函数是被main线程调用
- 因为是通过构造函数传参给Thread类,再调用start()之后,可以看到this.isAlive()是false,说明自定义线程类是没有在就绪态和运行态的。
- 其实通过
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");
}
}
- 先初始化SynchronizedObject(后面简称SO)对象
- 调用start()开启线程,通过线程MyThread(简称MY)类去修改SO对象的成员变量
- 主线程sleep(),MY类运行
- 调用SO的printObect()
- 修改完name之后sleep(),主线程开始执行stop()
- 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、线程优先级
在操作系统中,线程可以划分优先级,
- 优先级高的占有CPU资源较多,也就是CPU优先执行高优先级的线程中的任务。
- 线程优先级可以继承,比如A线程启动B线程,A、B线程的优先级是一样的。
- 线程优先级和代码的执行顺序无关,优先级高的线程不一定优先执行完。
设置线程优先级使用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
修饰的方法,一定是排队执行的。只有共享的读写访问才进行同步操作,如果不是共享资源,就没必要进行同步操作。
- 当A线程调用O对象有关键词
sychronized
修饰的方法X的时候,A线程就获得了O对象对应的锁,所以其他线程必须等线程A执行完之后才能调用X方法,但是其他线程可以调用其他非sychronized修饰的方法。 - 当A线程调用O对象有关键词
sychronized
修饰的方法X的时候,如果B线程访问O对象带有sychronized
修饰符的方法的时候,就必须等待A对象执行完方法X,释放对象锁,B线程才可以调用。
5、sychronized锁重入(自己可以再次获取自己的内部锁)
关键词schronized
拥有重入功能,当一个线程获得了一个对象的线程锁之后,再次请求该对象锁的时候是可以再次获取到的。这也证明在一个sychronized
方法/块的内部调用该类中其他sychronized
方法/块的时候,是永远可以获得锁的。
6、出现异常,锁自动释放
7、同步不具有继承性
8、sychronized同步语句块
- 不在
sychronized
同步代码块异步执行,在sychronized
同步代码块中同步执行 - 当一个线程访问
sychronized
同步代码块的时候,其他线程对同一个对象的其他sychronized
同步代码块的访问将被阻塞,这也说明sychronized
使用的"对象监视器"是一个。
9、sychronized总结
- 当多个线程访问
sychronized(非this对象X){}
同步代码块的时候呈同步效果, - 多个线程访问
sychronized
同步方法的时候呈同步效果。 - 多个线程访问
sychronized(this)
同步代码块的时候呈同步效果 - 线程还是可以异步调用无sychronized修饰的方法。
sychronized
加在static静态方法上是对Class上锁,而加在非静态方法上是给对象上锁。sychronized(Class)
和sychronized static
是一样的,都是对Class上锁- 多个线程持有相同对象锁,他们就是同步操作,如果是持有不同锁对象,那么他们是异步操作。
(2)、volatile关键词:使变量在多个线程可见:强制从公共堆栈中获取变量的值,而不是获取线程私有变量的值。缺点:不支持原子性。
1、线程的私有堆栈
2、volatile读取公共内存
3、sychronized和volatile的比较
sychonized | volatile |
---|---|
性能较差 | 线程同步的轻量级实现,性能比较好 |
修饰方法和代码块 | 只能修饰变量 |
多线程访问会发生阻塞 | 多线程访问不会发生阻塞 |
可以保证原子性,也可以间接保证数据可见性,因为他会将私有内存和公共内存中的数据同步 | 能保证数据的可见性,不能保证原子性 |
解决的是多个线程访问资源的同步性 | 解决的是变量在多个线程中的可见性 |
4、线程安全
线程安全包括原子性和可见性两个方面,java的同步机制都是围绕这两个方面来确保线程安全的。
5、关键词volatile主要的使用场景
- 关键词volatile主要的使用场景是多个线程可以感知变量的值进行改变了,进而可以获取到更新过后的变量值,也就是用多线程获取共享变量时可以获得最新的值使用。
- volatile提醒线程每次从共享内存中获取值,而不是私有内存中读取,这样就保证了同步数据的可见性。
变量在内存中的工作图:
- read和load阶段,从主存中复制变量到当前线程工作内存
- user和assign阶段,执行代码,改变共享变量值
- store和write阶段,用工作内存数据刷新主存对应变量值
- 多线程环境中,use和asign是多次出现的,但这一操作不是原子性的,在read和load之后,主内存的count值改变的时候,线程工作内存由于已经加载完成了,不会产生对应的变化,也就是私有内存和公共内存的变量不一致,所以计算结果和预期结果不相同,就导致了非线程安全问题。
- 对于使用volatile修饰的变量,JVM只保证从主内存读入私有内存的值是最新的,例如线程1和线程2在进行read和load操作的时候,发现主内存中的count都是5,那么他们就把这个最新的值读入私有内存中。
- volatile关键词解决的是关键词读取时候的可见性问题,而无法保证原子性,要保证原子性还是必须进行同步处理。
6、sychronized总结(外练互斥,内修可见)
关键词sychronized可以保证同一时刻,同步方法和同步代码块只由一个线程执行,它具有两个特性:互斥性和可见性。sychronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步代码块和同步方法的每一个线程,都可以看到由同一个锁保护之前的所有修改效果。
(三)、线程间通信
(一)、等待/通知机制
1、什么是等待/通知
前面的多个线程也可以实现通信,原因是多个线程访问共享变量,但是那种机制不是"等待/通知",每个线程都是主动式的去获取共享变量,而获取到的变量是不是想要的,并不能完全确定。
2、等待/通知机制的实现
wait()方法让执行该方法的线程进入等待状态,wait()是Object类的方法,该方法让线程进入"预执行队列"中,并且在wait处停止执行,直到接受的通知或者终止。
- 在调用wait()时,应该获得该对象的对象级别锁,即只有同步方法和同步代码块中调用wait(),执行完wait()之后,当前线程释放锁。如果调用wait()没有对应的锁,那么就抛出
IllegalMoniterStateException()
异常 - notify()的调用也要在同步方法和同步代码块中,那么对应的也要获取到对应对象的锁。如果调用notify()没有对应的锁,那么也就抛出
IllegalMoniterStateException()
异常。该方法用来通知可能等待该对象的对象锁的其他线程,如果有多个线程等待,那么由线程规划器选择出一个呈wait态的线程,对其发出notity(),使它等待获取该对象的对象锁。 在执行完notify()方法之后,当前线程不会立马释放对象锁,而等待获取该对象锁的线程也不会立马获得对象锁,要等待执行notify()的线程执行完之后,也就是退出sychronized,才会释放对象锁,而呈等待态的线程也就可以获取到该对象锁。
3、生产者/消费者模式
等待/通知的模式对经典的案例就是生产者/消费者模式,虽然有几种变形,但都基于wait/notify。
4、通过管道进行线程中通信:字节流
java提供了很多输入输出流Stream,其中管道流(pipeStream)是一种特殊的流,用于在不同线程之间通信,而无需借助临时文件之类的东西。
- PipedInputStream和PipedOutputStream
- PipedReader和PipedWriter
2、方法join()
- 作用:使所属线程的对象x正常执行run()方法,而使当前线程z处于无限期的阻塞,等待线程x销毁后再执行z线程。
- 与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
- 从打印结果来看,当前线程打印完毕后将锁释放之后,其他线程才开始打印。
- 线程打印的数据是分组打印的,因为当前线程已经获得锁了,而线程之间的打印顺序是随机的。
2、Condition实现等待/通知
- Condition有更好的灵活性,比如可以实现多路通知功能,也就是可以在Lock对象里面可以创建多个Condition(对象监视器)实例,线程对象可以注册到指定的Condition上,从而可以有选择的进行线程通知,在调度线程上更加灵活。
- 使用notity()和notifyAll()进行通知时,被通知的线程是JVM随机选中的,但使用ReentrantLock和Condition是可以实现"选择性通知"的。
- 而sychronized就相当于整个Lock对象只有一个单一的Condition对象,所有线程都注册在这一个对象身上。线程调用notifyAll(),需要通知所有的WAITING线程,没有选择权,性能就会受到很大的影响。
3、Object和Condition比较
Object | condition |
---|---|
wait(long timeout) | await(long time , TimeUnit unit) |
notify() | signal() |
notifyAll() | signalAll() |
(5)、定时器
(6)、单例模式和多线程
1、立即加载/ “饿汉模式”
在调用方法前,实例就已经被创建。
2、延迟加载/ “懒汉模式”
在调用方法的时候,实例才被创建
解决"懒汉模式"不是单例的情况:
- 对创建实例的方法进行sychronized处理(缺点:效率低下,是同步运行的)
- 使用同步代码块进行处理(缺点:效率低下)
- 针对创建实例的代码进行单独的同步处理(缺点:效率得到提神,但不能保证多线程安全性)
- 使用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