多线程案例
一.单例模式(是常见的设计模式)
**
设计模式:代码里面程序猿水平参差不齐,写的代码也各有好坏,所以有些大佬根据一些场景需求,整理出对应的应对方法,这样可以提高程序猿的整体代码水平,整理出的这些对应方法就叫做设计模式.
-
定义
单例模式:单个的实例或者线程.本质上就是借助编程语言自身的语法特性,强行限制某个类,不能创建多个实例. -
用static关键字则可以限制只能创建一个类
1.当属性变成类属性,已经是单个实例.更具体的的说是类对象属性,类对象是通过jvm加载(.class)文件,而此时类对象在jvm中也是实例.(jvm针对某个.class文件只会加载一次,也就只有一个类对象,类对象上面的成员static修饰的也只有一份)
2.static应用
在C语言中:
1.static修饰局部变量,改变变量生命周期
2.static修饰全局变量,改变变量作用域
3.static修饰方法,改变函数作用域
在Java语言中:
1.当static修饰类里面的成员变量
二.实现单例模式代码
**
- 饿汉模式
- 懒汉模式(创建实例更迟,带来效率更高)
懒汉操作是不安全的
如果t2线程进行load读操作时,t1还没有修改完,t2读的还是旧值,t2仍然能进入new操作,此时就会导致出现多个实例
如何保证懒汉模式线程安全—>加锁(希望t2读的是t1修改后的值)
实例创建之前线程是不安全需要加锁,实例创建之后,线程就安全不需要加锁
假设这两个线程同时调用getinstance,第一个线程进入锁以后进入第2个if开始new对象.
这时底层会有3个操作:
1.申请内存,得到内存首地址
2.调用勾走方法,初始化实例
3.把内存首地址赋给instance引用
而这样可能会导致编译器出现"指令重排序"优化
假设触发指令重排序,t1先执行申请内存和把内存地址赋给instance引用,此时得到不完全对象,只是内存,并没有内存上的数据,当t2线程调用getinstance就会认为这个实例为非空,就直接返回,并且在后面也会对instance进行解引用操作.
怎样能禁止指令重排序-----使用(volatile)
三.阻塞队列
-
阻塞队列是一种特殊队列,遵循先进先出原则
-
线程是安全的
-
带有阻塞功能
-
如果队列满,继续入队列,入队就会阻塞,直到有其他线程从队列里面取出元素
-
如果队列空,继续出队列,出队就会阻塞,直到有其他线程给队列里面放入元素
-
应用场景:实现生产者,消费者模型(多线程协同工作一种方式)
-
优势:
1:使用阻塞队列有利于代码解耦合,使得生产者和消费者之间没有太多的关联性,生产者生产完数据直接扔给阻塞队列,消费者需要数据直接从阻塞队列里面拿,就不需要经过生产者了.
2.阻塞队列相当于一个缓冲区,平衡了消费者和生产者.(服务器每处理一个请求,都是需要消耗硬件资源,同一时刻请求越多,消耗资源就会越多.一台主机上资源是有限的,某个资源耗尽,机器也就直接停止运转了.) -
阻塞队列具体应用
-标准库阻塞队列----BlockingQueue
在这里插入代码片
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class Demo21 {
public static void main(String[] args) throws InterruptedException {
BlockingDeque<Integer>blockingDeque=new LinkedBlockingDeque<>(100);
//带有阻塞功能入队
blockingDeque.put(1);
blockingDeque.put(2);
blockingDeque.put(3);
//带有阻塞功能的出队列
Integer ret=blockingDeque.take();
System.out.println(ret);
ret=blockingDeque.take();
System.out.println(ret);
}
}
阻塞队列没有提供带有阻塞功能的取队首元素
- 自己实现一个阻塞队列(用数组)
思想: 1.先实现一个普通队列,基于数组循环队列 2.加上线程安全 3.加上阻塞功能
在这里插入代码片
class MyBlockingQueue{
private int[] nums=new int[100];
private volatile int head=0;//头指针
private volatile int tail=0;//尾指针
private volatile int size=0;//计数器
//入队
public void put(int num) throws InterruptedException {
synchronized (this) {
while (size >= nums.length) {
//满了
this.wait();//满了就会出现锁竞争
}
nums[tail] = num;
tail++;
if (head >= nums.length) {
tail = 0;
}
size++;
this.notify();
}
}
//出队
public Integer take() throws InterruptedException {
synchronized (this){
while (size==0){
this.wait();//出队列空了也会出现锁竞争
}
int ret=nums[head];
head++;
if (head>=nums.length){
head=0;
}
size--;
this.notify();
return ret;
}
}
}
public class Demo22 {
public static void main(String[] args) {
MyBlockingQueue myBlockingQueue=new MyBlockingQueue();
Thread t1=new Thread(()->{
int n=1;
while (true){
try {
myBlockingQueue.put(n);
System.out.println("生产元素"+n);
n++;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread t2=new Thread(()->{
while (true){
try {
int n=myBlockingQueue.take();
System.out.println("消费元素"+n);
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
t2.start();
}
}