多线程案例(单例模式、阻塞式队列、定时器及线程池)_多线程会创建多个实例吗

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

//用过Singleton来实现单例模式,保证Singleton这个类有唯一实例
    //饿汉模式
class Singleton{
 
    //static修饰的成员---“类成员”-》“类属性/方法”
    //1.使用static来创建一个实例,并且立即进行实例化
    //这个instance对应的实例,就是该类的唯一实例
    private static Singleton instance = new Singleton();
    //2.为了防止在其他地方new这个Singleton,就可以把这个Singleton设为私有的
    private Singleton(){
 };
    //构造一个方法,让外面能够拿到唯一实例
    public static Singleton getInstance(){
 
        return instance;
    }
}
public class Test06 {
 
    public static void main(String[] args) {
 
        Singleton instance = Singleton.getInstance();
    }
}

饿汉模式中getlnstance,仅仅是读取了变量的内容。如果多个线程只是读同一个变量,不修改,此时仍然是线程安全的。

1.2 懒汉模式

类加载的时候不创建实例. 第一次使用的时候才创建实例

  • 懒汉模式-单线程版
class Singleton1{
 
    private static Singleton1 in = null;
    private Singleton1(){
 };
    public static Singleton1 getInstance(){
 
    //不是原子的,既包含读,又包含修改
        if(in == null){
 
            in = new Singleton1();
        }
        return in;
    }
}

懒汉模式中,既包含了读,又包含了修改.而且这里的读和修改,还是分成两个步骤的(不是原子的)存在线程安全问题

  • 懒汉模式-多线程版

线程安全问题发生在首次创建实例时. 如果在多个线程中同时调用 getInstance 方法, 就可能导致创建出多个实例.
加锁操作,可以改变这里的线程安全问题。使用这里的类对象作为锁对象(类对象在一个程序中只有唯一一份,就能保证多个线程调用getInstance的时候都是针对同一个对象进行的加锁)。

class Singleton1{
 
    private static Singleton1 instance= null;
    private Singleton1(){
 };
    public static Singleton1 getInstance(){
 
        synchronized (Singleton1.class){
 
            if(instance== null){
 
                instance= new Singleton1();
            }
        }
        return instance;
    }
}

  • 懒汉模式-多线程版(改进)

当前虽然加锁之后,线程安全问题得到解决了,但是又有了新的问题 :对于刚才这个懒汉模式的代码来说。线程不安全是发生在instance被初始化之前的.未初始化的时候,多线程调用getinstance,就可能同时涉及到读和修改.但是一旦instance被初始化之后(一定不是nul, if条件一定不成立了),getInstance操作就只剩下两个读操作也就线程安全了。
而按照上述的加锁方式,无论代码是初始化之后,还是初始化之前,每次调用 getinstance方法都会进行加锁.也就意味着即使是初始化之后(已经线程安全了)仍然存在大量的锁竞争

以下代码在加锁的基础上, 做出了进一步改动:

  1. 使用双重 if 判定, 降低锁竞争的频率。

改进方案: 让getInstance初始化之前,才进行加锁,初始化之后,就不再加锁了。在加锁这里再加上一层条件判定即可.条件就是当前是否已经初始化完成 (instance == null)。

在使用了双重if判定之后,当前这个代码中还存在一个重要的问题:如果多个线程,都去调用这里的getlnstance 方法,就会造成大量的读instance内存的操作,这样可能会让编译器把这个读内存操作优化成读寄存器操作
—旦这里触发了优化,后续如果第一个线程已经完成了针对instance的修改,那么紧接着后面的线程都感知不到这个修改,仍然把 instance当成null 。所以这里需要给 instance 加上了 volatile。

  1. 给 instance 加上了 volatile
class Singleton2{
 
    //不是立即初始化实例
    //volatile 保证内存可见性
    private static volatile Singleton2 instance = null;
    private Singleton2(){
 };
    //只有在真正使用这个实例的时候,才会真正的去创建这个实例
    public static Singleton2 getInstance(){
 
        //使用这里的类对象作为锁对象,类对象在一个程序中只有一份,
        //判定的是是否要加锁。降低了锁竞争
        if(instance == null){
 
        //加锁操作,保证了线程安全
            synchronized (Singleton2.class){
 
            //判定的是是否要创建实例
                if(instance == null){
 
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}
public class Test07 {
 
    public static void main(String[] args) {
 
        Singleton2 instance = Singleton2.getInstance();
    }
}

二、阻塞式队列

阻塞队列是什么?

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值