案例一.单例模式
单例模式是一种设计模式;类似于棋谱,有固定套路,针对一些特定场景可以给出一些比较好的解决方案;
只要按照设计模式来写代码,就可以保证代码不会太差,保证了代码的下限;
-------------------------------------------------------------------------------------------------------------------------------
补充:
设计模式是针对编写代码过程中的软性约束: 不是强制的,可以遵守也可以不遵守;
框架是针对编写代码过程中的硬性约束: 针对一些特定的问题场景,大佬们把基本的代码和大部分逻辑已经写好了,留下一些空位,让你在空位上自定义一些逻辑;
库虽然也是别人写好的,但是代码的主体还是由你来完成,你可以决定调用或者不调用;
-------------------------------------------------------------------------------------------------------------------------------
开发过程中,希望有的类在一个进程中不应该存在多个实例(对象),此时就可以使用单例模式,限制某个类只有一个实例;
饿汉模式
饿的意思是"迫切": 在类被加载的时候就会创建出单例的实例;
class Singleton {
private static Singleton instance = new Singleton();
//static修饰将instance变成类成员;
//类成员的初始化就是在Singleton这个类被加载的时候;
public static Singleton getInstance() {
return instance;
}
}
public class demo20 {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
//true
只要不再其他代码中,new这个类,每次需要使用时都通过getInstance()来获取实例,那么此时这个类就是单例的了;
主要要解决的问题:防止别人new这个类的对象
单例模式的核心:将构造方法设为私有的
意味着在类的外面,就无法再构造新的实例了;
private Singleton() {}
通过反射/序列化反序列化等非常规手段还是可以打破单例模式的;
懒汉模式
计算机的"懒"是褒义词: 意思是效率会更高;
推迟了创建实例的时机,第一次使用的时候,才会创建实例;
class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if(instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {}
}
public class demo21 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 == s2);
}
}
//true
思考:如果在多线程环境下,调用getInstance,是否会有问题呢?
饿汉模式没有线程安全问题,但是懒汉模式存在线程安全问题;
原因:多个线程针对一个变量修改可能会产生线程安全问题,但是如果只是读取,则没有问题;
而饿汉模式中的getInstance方法中只有读操作,而懒汉模式中的getInstance方法则有判断和修改赋值操作,故会出现线程安全问题;
解决办法:加锁!
private static Object locker = new Object();
public static SingletonLazy getInstance() {
synchronized (locker) {
if (instance == null) {
instance = new SingletonLazy();
}
}
return instance;
}
当前代码的写法,只要调用getInstance,都会触发加锁操作,虽然没有线程安全问题了,但是Instance new出来了之后就都是读操作,此时也会因为加锁,产生阻塞,影响性能;
优化:
public static SingletonLazy getInstance() {
if(instance == null) {
synchronized (locker) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
-------------------------------------------------------------------------------------------------------------------------------
补充:
如果是一个局部变量,每个线程的局部变量都有自己的一份.但是如果是new出来的对象,可以共享;
少数局部变量在线程中不能共用是java自身的限制,在C++,系统原生api中则没有这样的限制;
创建出的局部变量,处于JVM内存的"栈"区域中;
new出来的变量,处于JVM内存的"堆'区域中;
整个JVM进程中,堆只有一份,是线程之间大家共用的;而栈则是每个线程有自己独立的栈;
正因为变量的共享是常态,所以就容易触发多个线程修改同一个变量导致线程安全问题;
-------------------------------------------------------------------------------------------------------------------------------
instance = new SingletonLazy();可以分为三个步骤
1.分配内存空间;(买房)
2.执行构造方法;(装修)
3.内存空间的地址,赋值给引用变量;(收房拿到钥匙)
编译器可能按照123的顺序也可能按照132的顺序来执行;对于单线程来说是没有区别的;
在多线程中按照132的顺序执行可能会出现问题:
若按照此顺序,线程A执行到步骤3时(此时instance的地址就不为null了),线程B进行了第一个if语句的判断并返回了instance,注意此时的instance指向了一个没有初始化的空的内存,故可能会产生线程安全问题;
解决方法:给instance加上volatile关键字;
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 多线程代码案例:单例模式/阻塞队列/线程池/定时器
发表评论 取消回复