云计算百科
云计算领域专业知识百科平台

JavaEE初阶 多线程:单例模式 | 阻塞队列

目录

Singleton 单例模式

饿汉模式,运行即创建

懒汉模式,需要时再创建

线程安全问题

阻塞队列

阻塞队列实现

阻塞队列的使用

阻塞队列作用


Singleton 单例模式

单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例。

单例模式最常⻅的实现⽅的是"饿汉"和"懒汉"两种

饿汉模式,运行即创建

class Singleton{
private static Singleton instance=new Singleton();
private static Singleton getinstance(){
return instance;
}
//构造方法私有化,仅限在Singleton类中实例化,确保单例化
private Singleton(){
}
}

懒汉模式,需要时再创建

class Singleton{
private static Object locker=new Object();
//volatile修饰instance
private static volatile Singleton instance=null;
public static Singleton getinstance(){
if (instance==null){
synchronized (locker){
if (instance==null){
instance=new Singleton();
}
}
}
//实例化时原子操作
return instance;
}
//构造方法私有化,仅限在Singleton类中实例化,确保单例化
private Singleton(){
}
}

线程安全问题

饿汉模式不存在线程安全问题,懒汉模式当多个线程调用getInstance⽅法,就可能导致创建 出多个实例,后面的实例会覆盖前面实例,资源浪费。

⼀旦实例已经创建好了,后⾯再多线程环境调⽤getInstance就不再有线程安全问题了(不再修改 instance 了)

使用锁保证原子性

//判断是否需要加锁,已经创建实例后减少资源浪费
if (instance==null){
synchronized (locker){
//判断是否需要实例化
if (instance==null){
instance=new Singleton();
}
}
}

通过volatile关键字修饰

但线程在加锁操作时仍会被调度,导致t1线程中instance使用了未初始化的实例,因此需要加入volatile避免重排序

对数据库使用单例模式

class DBUtil {
private static volatile DataSource dataSource = null;

public static DataSource getInstance() {
if (dataSource == null) {
synchronized (DBUtil.class) {
if (dataSource == null) {
MysqlDataSource ds = new MysqlDataSource();
ds.setUrl("jdbc:mysql://127.0.0.1:3306/java117?characterEncoding=utf8&useSSL=false");
ds.setUser("root");
ds.setPassword("2222");
dataSource = ds;
}
}
}
return dataSource;
}
private DBUtil{}
}

阻塞队列

阻塞队列是⼀种特殊的队列.也遵守"先进先出"的原则,具有线程安全阻塞队列

1.当队列满的时候,继续⼊队列就会阻塞,直到有其他线程从队列中取⾛元素.

 2.当队列空的时候,继续出队列也会阻塞,直到有其他线程往队列中插⼊元素.

阻塞队列实现

class Queue{
private int[] Arrayqueue;
int tail=0;
int head=0;
int size=0;
public Queue(int capacity){
Arrayqueue=new int[capacity];
}
Object locker=new Object();
public void put(int n) throws InterruptedException {
//判满返回
//线程安全
synchronized (locker){
/*if(size==Arrayqueue.length){
//阻塞等待
locker.wait();
}*/ //wait被唤醒时会重新检擦size是否符合要求
while (size==Arrayqueue.length){
locker.wait();
}
Arrayqueue[head]=n;
head=(head+1)%Arrayqueue.length;
size++;
locker.notifyAll();
}
}
public int take() throws InterruptedException {
synchronized (locker){
/*if (size==0){
locker.wait();
}*/
while (size==0){
locker.wait();
}
int n= Arrayqueue[tail];
tail=(tail+1)% Arrayqueue.length;
size–;
locker.notifyAll();
return n;
}
}
}

阻塞的队列的应用场景:生产消费者模型

生产者线程用来生产,产出放入阻塞队列,消费者线程从阻塞队列取出产品消费

阻塞队列的使用

在Java标准库中内置了阻塞队列.如果我们需要在⼀些程序中使⽤阻塞队列,直接使⽤标准库中的即 可.

1.BlockingQueue是⼀个接⼝.真正实现类包括LinkedBlockingQueue/ArrayBlockingQueue/PriorityBlockingQueue.

2.put⽅法⽤于阻塞式的⼊队列,take⽤于阻塞式的出队列.

3.BlockingQueue也有offer,poll,peek等⽅法,但是这些⽅法不带有阻塞特性

public static void main(String[] args) {
// BlockingDeque<Integer> queue=new LinkedBlockingDeque<>();
// BlockingQueue<Integer> queue=new ArrayBlockingQueue<>(100);
//BlockingDeque<Integer> queue= new PriorityBlockingQueue<Integer>();
Queue queue=new Queue(100);
Thread t0=new Thread(()->{
int n=0;
while (true){
try {
queue.put(n);
System.out.println("生产了: " + n);
n++;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

}
});
Thread t1=new Thread(()->{
//int n=0;
while (true){
//int n= 0;
try {
int n = queue.take();
System.out.println("消费了: " + n);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

}
});
t0.start();
t1.start();
}

阻塞队列作用

1.减少资源竞争,生产消费者各司其职

2.解耦合

浏览器下单通过网关传递服务器a,再通过服务器b处理返回,如果b出现问题直接影响a处理

通过阻塞队列 a与队列耦合,b与队列耦合

3.削峰填谷

当某个时间段访问量激增,阻塞队列会先接受访问量,等待服务器处理,减缓服务器压力

赞(0)
未经允许不得转载:网硕互联帮助中心 » JavaEE初阶 多线程:单例模式 | 阻塞队列
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!