关于java多线程的概念以及基本用法:java多线程基础
6,单例模式与多线程
如何使单例模式遇到多线程是安全的这是下面要讨论的内容
6.1,立即加载
立即加载就是在使用类的时候已经将对象创建完毕,例如String s = new String(”hello world!”);这里就是直接将对象实例化了
MyObject类:
public class MyObject {
private static MyObject myObject = new MyObject();
private MyObject() {
}
public static MyObject getInstance() {
return myObject;
}
}
测试类:
public class Run extends Thread{
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
public static void main(String[] args) {
Run run1 = new Run();
Run run2 = new Run();
Run run3 = new Run();
run1.start();
run2.start();
run3.start();
}
}
结果:
1141021789
1141021789
1141021789
打印的hashCode是同一个值,说明对象时同一个
6.2,延迟加载
延迟加载就是在调用get()方法时实例才被创建,常见的是在
get()方法中进行new实例化
MyObject类:
public class MyObject{
public static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
if(myObject != null) {
}else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myObject = new MyObject();
}
return myObject;
}
}
测试类:
public class Run extends Thread{
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
public static void main(String[] args) {
Run run1 = new Run();
Run run2 = new Run();
Run run3 = new Run();
run1.start();
run2.start();
run3.start();
}
}
结果:
1796851831
141757079
1721087309
延迟加载显然不是单例,那么如何让延迟加载变成单例呢,可以用synchronized关键字和同步代码块
1,延迟加载同步方案–synchronized关键字
上面的代码不变,
public static MyObject getInstance()->synchronized public static MyObject getInstance()
运行结果显示hashcode值都是一样的,所以这种方法是可行的
2,延迟加载同步方案–同步代码块
public static MyObject getInstance()改为:
public static MyObject getInstance() {
synchronized(MyObject.class) {
if(myObject != null) {
}else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myObject = new MyObject();
}
}
return myObject;
}
这样做也可以实现单例,但是这两种方法都有一个问题就是,效率比较低,都把整个方法给锁住了,下面尝试对部分代码同步
3,延迟加载同步方案–针对重要代码单独同步
public static MyObject getInstance()改为:
public static MyObject getInstance() {
if(myObject != null) {
}else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//部分代码被上锁,但存在非线程安全问题
synchronized (MyObject.class) {
myObject = new MyObject();
}
}
return myObject;
}
结果:
1721087309
141757079
1796851831
对部分代码实现同步,运行效率得到了提高,但是单例却实现不了了
4,延迟加载同步方案–使用DCL双检查锁机制
DCL双检查锁机制是实现多线程环境中延迟加载单例设计模式
public class MyObject{
private volatile static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
if(myObject != null) {
}else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//部分代码被上锁,但存在非线程安全问题
synchronized (MyObject.class) {
if(myObject ==null) {
myObject = new MyObject();
}
}
}
return myObject;
}
}
结果:
141757079
141757079
141757079
实现了单例
4,延迟加载同步方案–静态内部类
之前做一个安卓-RFID读卡器实现打卡的App,当时做到数据获取时,老是拿不到数据,因为当时的程序并不是单例,而用静态内部类可以很好的实现,单例模式
public class Myobject{
private static class MyObjectHandler{
private static MyObject myObject = new MyObject();
}
private MyObject() {
}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}
结果当然是可以实现单例的
5,序列化与反序列化的单例模式实现
静态内置类可以实现线程安全,但是如果遇到了序列化对象,使用默认的方式运行的结果则会是多例
新建MyObject类:
public class MyObject implements Serializable{
private static final long serialVersionUID = 888L;
//内部类方式
private static class MyObjectHandler{
private static final MyObject myObject = new MyObject();
}
private MyObject() {}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
/*protected Object readResolve() throws ObjectStreamException{
System.out.println("111");
return MyObjectHandler.myObject;
}*/
}
SaveAndRead类:
public class SaveAndRead {
public static void main(String[] args) {
try {
MyObject myObject = MyObject.getInstance();
FileOutputStream fosRef =
new FileOutputStream(new File("myObjectFile.txt"));
ObjectOutputStream oosRef = new ObjectOutputStream(fosRef);
oosRef.close();
fosRef.close();
System.out.println(myObject.hashCode());
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}
try {
FileInputStream fisRef =
new FileInputStream(new File("myObjectFile.txt"));
ObjectInputStream iosRef = new ObjectInputStream(fisRef);
MyObject myObject = (MyObject) iosRef.readObject();
iosRef.close();
fisRef.close();
System.out.println(myObject.hashCode());
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e1) {
e1.printStackTrace();
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}
这样运行得到的结果不是同一个对象,解决办法就是去掉注释使用readResolve()方法
6,使用static代码块实现单例模式
由于静态代码块是在使用类时就执行了的,所以借助这一特性,可以实现单例
新建MyObject类:
public class MyObject {
private static MyObject instance = null;
private MyObject() {}
static {
instance = new MyObject();
}
public static MyObject getInstance() {
return instance;
}
}
测试类:
public class Run extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(MyObject.getInstance().hashCode());
}
}
public static void main(String[] args) {
Run run1 = new Run();
Run run2 = new Run();
Run run3 = new Run();
run1.start();
run2.start();
run3.start();
}
}
结果:
1721087309
1721087309
1721087309
。。。。
。。。。
。。。。
7,使用enum枚举数据类型实现单例模式
新建MyObject类:
public class MyObject {
private enum MyEnumSingleton{
INSTANCE;
private Resource resource;
private MyEnumSingleton(){
resource = new Resource();
}
public Resource getResource(){
return resource;
}
}
public static Resource getResource(){
return MyEnumSingleton.INSTANCE.getResource();
}
}
测试类:
public class Run {
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.getResource().hashCode());
}
}
}
public static void main(String[] args) {
Run.MyThread t1 = new Run().new MyThread();
Run.MyThread t2 = new Run().new MyThread();
Run.MyThread t3 = new Run().new MyThread();
t1.start();
t2.start();
t3.start();
}
}
结果当然是单例啦