单例模式(保证线程安全)

一 单例模式的定义

单例模式是一种经典的设计模式;设计模式是针对一些常见场景,总结出来的代码编写套路.

单例 → 单个实例对象:一个程序中,某个类只创建一个实例(对象),不能创建多个对象

java中的单例模式,借助java语法,保证某个类只能够创建出一个实例,而不能new多次

二 饿汉模式(急迫)

饿汉模式线程是安全的.

只是文件读一小部分,把当前屏幕填充上,如果用户翻页了,再读其他文件内容,如果不翻页就省下了

Singleton:单例:

package Threading;

//把这个类设定成单例
class Singleton{
    //唯一实例
    private static Singleton instance = new Singleton();
    //被static修饰,说明是改属性是类的属性
    //(JVM中每个类的类对象只有唯一一份)
    获取到实例的方法
    public static Singleton getInstance(){
        return instance;
    }
    //把该类的构造方法设为private即可   禁止外部new实例操作
    private Singleton(){
    }
}
public class ThreadDemo17 {
    public static void main(String[] args) {
        /*
        此时s1 和s2是同一个对象!!
         */
        Singleton S1 = Singleton.getInstance();
        Singleton S2 = Singleton.getInstance();

//     报错 Singleton s3 = new Singleton();//如何禁止到new操作
    }
}

三 懒汉模式(从容)

        线程不安全,会出现创建多个对象的情况有读也有写:

        1.加锁(把if和new变成原子操作)

        2.双重if,减少不必要的加锁操作

        3.适用volatile禁止指令重排序,保证后续能够拿到的是完整的对象

把文件所有的内容都读到内存中,并显示

核心思想:非必要不创建实例.

package Threading;
class SingletonLazy{
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance(){
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }
    private SingletonLazy(){}
}
public class ThreadDemo18 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

四 懒汉模式--多线程版

通过加锁可以解决该问题

 保证判定和new是一个原子操作

加锁其实是一个比较低效的操作(加锁就可能涉及到阻塞状态),因此非必要不加锁

任何使用getlnstance都会触发锁竞争

package Threading;
class SingletonLazy{
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        synchronized (SingletonLazy.class) {
            if (instance == null) {
                instance = new SingletonLazy();
            }
            return instance;
        }
    }
    private SingletonLazy(){}
}
public class ThreadDemo18 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

此处的线程不安全,只出现在首次创建对象这里.一旦对象new好了,后续调用getlnstance,就只是单传的读操作,就没有线程安全问题,就没必要再加锁了

 两个一样的判定条件,两条件的目的不同.两个if条件看着一样但执行实际差别不同

外层条件判定是否要加锁

里层条件是判定是否要创建对象

package Threading;

class SingletonLazy{
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        //这个条件,判定是否要加锁,如果对象已经有了,就不要加锁了,此时本身就是线程安全的
        if(instance == null){
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
                return instance;
            }
        }
    }
    private SingletonLazy(){}
}

public class ThreadDemo18 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);

    }
}

4.1 还存在指令重排序的问题

 1.创建内存  2.调用构造方法.    3.把内存地址,付给调用

加上volatile

 

package Threading;

class SingletonLazy{
    volatile private static SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        //这个条件,判定是否要加锁,如果对象已经有了,就不要加锁了,此时本身就是线程安全的
        if(instance == null){
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
                return instance;
            }
        }
    }
    private SingletonLazy(){}
}

public class ThreadDemo18 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);

    }
}

五 阻塞队列

队列:先进先出(最朴素,最简单的队列)

阻塞队列:带有阻塞特性   线程安全

1.如果队列空,尝试出队列,就会阻塞等待,等待到队列不空为止

2.如果队列满,尝试入队列,,也会阻塞等待,等待到队列不满为止

5.1 java标准库提供的阻塞队列的使用

package Threading;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ThreadDemo19 {
    public static void main(String[] args) throws InterruptedException {
        //    BlockingQueue<String> queue = new ArrayBlockingQueue<>();//基于数组实现的类
        BlockingQueue<String> queue = new LinkedBlockingQueue<>();//基于链表实现的类
        //阻塞队列核心方法,主要有两个
        //1.put入队列
        queue.put("hello1");
        queue.put("hello2");
        queue.put("hello3");
        queue.put("hello4");
        queue.put("hello5");
        //2.take 出队列
        String result = null;
        result = queue.take();
        System.out.println(result);
        result = queue.take();
        System.out.println(result);
        result = queue.take();
        System.out.println(result);
        result = queue.take();
        System.out.println(result);
        result = queue.take();
        System.out.println(result);
        result = queue.take();
        System.out.println(result);
    }
}

 在上述代码中,put了5次,take6次,前5次take都很顺利,第六次take就阻塞了

编写一个"生产者消费者模式"多线程只用阻塞队列

该模式是服务器开发中一种常见的代码写法

生产者和消费者这样的角色,是针对摸个资源来说的.

生产者和消费者之间交互数据,就需要用到一个交易场所,就相当于一个阻塞队列

生产者消费者模型的作用:1.可以上下游模块之间进行更好的"解耦合"(高内聚,低耦合)阻塞队列服务器        2.可以削峰填谷

基于阻塞队列,写个生产者消费者模型:

package Threading;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ThreadDemo20 {
    public static void main(String[] args) {
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();
        //消费者
        Thread t1 = new Thread(()->{
            while(true){
                try {
                    int value = blockingQueue.take();
                    System.out.println("消费元素:" + value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        //生产者
        Thread t2 = new Thread(()->{
            int value = 0;
            while(true){
                try {
                    System.out.println("生产元素:" + value);
                    blockingQueue.put(value);
                    value++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
        //上述代码,让生产者,每隔一秒生产一个元素
        //让消费者则直接消费,不做限制
    }
}

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值