类加载的时候创建实例
//此处要求singleton 类只能有一个实例
//饿汉模式,在类加载的时候进行创建实例:被JVM加载到内存得到类对象时:类似为程序启动
class Singleton {
//加上static 之后,当前的成员变为类属性,在类对象上,类对象只有一个实例
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
//单例模式最关键的要点,禁止构造方法被外部使用
private Singleton() {
}
}
public class Demo1 {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
}
}饿汉模式在多线程中并没有安全问题,因为是多个线程进行读取。
第一次使用的时候创建实例
//懒汉模式
class SingletonLazy{
private static SingletonLazy instance = null;
//懒汉模式关键:创建时机推迟到第一次使用的时候创建
public static SingletonLazy getInstance(){
if(instance == null){
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy(){
}
}
public class Demo2 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 == s2);
}
}此时的设计在多线程中是有问题的,只在第一次使用时会出现线程安全问题。可以进行加锁操作。
//懒汉模式
class SingletonLazy{
private static SingletonLazy instance = null;
private static Object locker = new Object();
//懒汉模式关键:创建时机推迟到第一次使用的时候创建
public static SingletonLazy getInstance(){
synchronized (locker){
if(instance == null){
instance = new SingletonLazy();
}
}
return instance;
}
private SingletonLazy(){
}
}但是此处一进入方法就会加锁,而线程安全问题只在实例化之前存在线程安全问题,一旦实例化完毕之后,线程安全问题就没有了,所以实例化之后仍然加锁多个线程调用就会产生阻塞,效率产生影响,因此还可以进行改进。
//懒汉模式
class SingletonLazy{
private static SingletonLazy instance = null;
private static Object locker = new Object();
//懒汉模式关键:创建时机推迟到第一次使用的时候创建
public static SingletonLazy getInstance(){
//判断是否需要加锁
if(instance == null){
synchronized (locker){
//判断是否需要创建实例
if(instance == null){
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy(){
}
}那么最终保证线程安全的代码为:
//懒汉模式
class SingletonLazy{
private volatile static SingletonLazy instance = null;
private static Object locker = new Object();
//懒汉模式关键:创建时机推迟到第一次使用的时候创建
public static SingletonLazy getInstance(){
//判断是否需要加锁
if(instance == null){
synchronized (locker){
//判断是否需要创建实例
if(instance == null){
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy(){
}
}阻塞队列是一种特殊的队列,也遵守“先进先出”的原则,会在队列已满或者队列为空时进入阻塞,阻塞队列是一种线程安全的数据结构,并且具有以下特性:
阻塞队列的典型应用场景是" 生产者消费者模型 ",产生的作用:
使用阻塞队列的例子:
public class Demo4 {
//简单的生产者消费者模型
//一个线程表示生产者,一个线程表示消费者
//搞一个阻塞队列,生产者往阻塞队列中放一个整数,消费者进行获取
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Long> queue = new ArrayBlockingQueue<>(1000);
//生产者
Thread t1 = new Thread(() -> {
long n = 0;
while (true){
try {
queue.put(n);
System.out.println("生产了:" + n);
n++;
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
//消费者
Thread t2 = new Thread(() -> {
while (true){
try {
long n = queue.take();
System.out.println("消费了:" + n);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
t2.start();
}
}其中,生产者t1线程生产一个值,消费者 t2线程才会消费一个值。
//只保存Sting类型
class MyBlockingQueue {
private String[] data;
String val = null;
//[head,tail)构成循环队列的有效区间
private int head = 0;
private int tail = 0;
//size解决满
private int size = 0;
private Object locker = new Object();
public MyBlockingQueue(int capacity){
data = new String[capacity];
}
public void put(String elem) throws InterruptedException {
synchronized(locker){
while (size == data.length){
//如果满了则进行等待唤醒
locker.wait();
}
data[tail] = elem;
tail++;
if(tail >= data.length){
tail = 0;
}
size++;
locker.notify();
}
}
public String take() throws InterruptedException {
synchronized (locker){
while (size == 0){
locker.wait();
}
//取出head元素
String result = data[head];
head++;
if(head >= data.length){
head = 0;
}
size--;
locker.notify();
return result;
}
}
}
public class Demo5 {
public static void main(String[] args) {
MyBlockingQueue queue = new MyBlockingQueue(1000);
Thread t1 = new Thread(() -> {
long n = 0;
while (true){
try {
queue.put(n + "");
System.out.println("生产了:" + n);
n++;
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread t2 = new Thread(() -> {
while (true){
try {
String n = queue.take();
System.out.println("消费了:" + n);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
t2.start();
}
}