一 单例模式的定义
单例模式是一种经典的设计模式;设计模式是针对一些常见场景,总结出来的代码编写套路.
单例 → 单个实例对象:一个程序中,某个类只创建一个实例(对象),不能创建多个对象
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();
//上述代码,让生产者,每隔一秒生产一个元素
//让消费者则直接消费,不做限制
}
}