1.单例模式(Singleton pattern))
单例模式保证某个类在进程中只创建一个实例
1.1饿汉模式
类加载的同时立即创建实例
class SingleHungry {
//只创建了这一个唯一的实例
private static SingleHungry instance = new SingleHungry();
public static SingleHungry getInstance() {
return instance; //饿汉模式只进行读操作,并未修改
}
//使用private修饰禁止外部new一个实例
private SingleHungry() {};
}
public class TheadDemo {
public static void main(String[] args) {
SingleHungry singleHungry = SingleHungry.getInstance();
}
}
1.2懒汉模式
等到第一次使用的时候再开始创建一个实例
public class SingleLazy {
//new可能引起指令重排序使用volatil修饰
volatile public static SingleLazy instance = null;
public static SingleLazy getInstance() {
//这一个if()判断第一个线程创建完实例实后,保证后面的线程就不需要进行重复加锁操作
if(instance == null) {
synchronized(SingleLazy.class) {
if(instance == null) {
instance = new SingleLazy();
}
}
}
return instance;
}
private SingleLazy() {}; //使用private修饰禁止外部new一个实例
public static void main(String[] args) {
SingleLazy s1 = SingleLazy.getInstance();
SingleLazy s2 = SingleLazy.getInstance();
//是相同的一个
System.out.println(s1 == s2);
}
}
小概率情况可能出现指令重排序
2.阻塞队列(BlockingQueue)
阻塞队列是一种特殊的队列,使得线程安全的一种数据结构
也遵循"先进先出"原则
特性:
当列队满的时候,再次压入队列,就会进行阻塞等待,直到其他线程从列队中取走取走元素
而当列队为空时,再次弹出列队,就会进行阻塞等待,直到其他线程从列对中存入元素.
2.1阻塞队列的简单使用
代码实现
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ThreadDemo2 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
//阻塞列队使用put方法进行入对操作
blockingQueue.put("1");
blockingQueue.put("2");
blockingQueue.put("3");
blockingQueue.put("4");
String res = null;
//阻塞列队使用take方法进行出队操作
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
}
}
结果显示
再多弹出一次看看
String res = null;
//阻塞列队使用take方法进行出队操作
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
res = blockingQueue.take();
System.out.println(res);
并未结束发现进入阻塞等待状态
2.2使用阻塞队列编写生产者消费者模型
为什么会有这种模型?
首先我们理解一下耦合 内聚的概念
两个模块之间关联越强,耦合越高,反之耦合越低;(代码之间追求低耦合,防止之间相互影响)
相关联的代码无序摆放,低内聚; 相关联的代码归类的摆放,高内聚;
1.使得上下模块之间"解耦合"(高内聚,低耦合);
2.消峰填谷
某些时候服务器1的请求比平时好几倍的激增, 如果没有阻塞队列缓冲,就可能导致服务器2超出峰值直接跪掉了
使用了队列,当服务器1激增的时候,队列的数据也会增,
就会帮助服务器2 以之前的速率获取数据
保障了系统的稳定性
代码简单实现
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class blockingQueue {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
Thread consumer = new Thread(() -> {
while (true) {
try {
int val = queue.take();
System.out.println("消费"+val);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
consumer.start();
Thread producer = new Thread(() -> {
int val = 0;
while (true) {
try {
System.out.println("生产"+val);
queue.put(val);
val++;
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
producer.start();
}
}
结果显示
2.3自己创建一个阻塞队列实现"消费者生产者"模型(*)
1.实现一个队列
2.加锁(synchronized关键字)
3.加上阻塞功能(wait,notify)
如何区分队列空或满
1.舍弃一个空间区分
2.记录元素个数
代码实现
public class MyBlockingQueue {
private int[] items = new int[500];
volatile int head = 0;
volatile int tail = 0;
//记录元素个数
volatile int size = 0;
//入队列
synchronized public void put(int val) throws InterruptedException {
//判断是否为队列满
if(size == items.length) {
//为满等待其他线程取走元素
this.wait();
}
//采用尾加
items[tail] = val;
tail++;
//采用循环队列,tail达到尾巴,重头开始
if(tail == items.length) {
tail = 0;
}
//或者使用取模tail = tail % items.length;
size++;
就存入了一个元素,可以通知上次为空的结束等待
this.notify();
}
出队列
synchronized public int take() throws InterruptedException {
//判断是否为队列空
if(size == 0) {
//为空等待其他线程填入元素
this.wait();
}
int val = items[head];
head++;
if(head == items.length) {
head = 0;
}
size--;
//就取走了一个元素,可以通知上次为满的结束等待
this.notify();
return val;
}
public static void main(String[] args) {
MyBlockingQueue queue = new MyBlockingQueue();
Thread t1 = new Thread(() -> {
while (true) {
try {
int val = queue.take();
System.out.println("消费"+val);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
int val = 0;
while (true) {
try {
System.out.println("生产" + val);
queue.put(val);
val++;
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t2.start();
}
}
当wait的等待条件还不满足时
wait可能被外部interrupt方法唤醒
这样我们就可以等待wait唤醒后在判定一定条件 如果不满足继续wait;
结果显示
3.定时器
构造方法
普通方法
我们从官方文档可以了解到TimerTask 实现了Runnable接口需要重新run方法
java-api的中文文档有需要的小伙伴自取
3.1有关定时器的简单使用
import java.util.Timer;
import java.util.TimerTask;
public class Time {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("good!");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("so");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("are");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("You");
}
}, 1000);
}
}
结果显示
3.2自己实现一个简单定时器(*)
代码实现
import java.util.PriorityQueue;
class MyTask implements Comparable<MyTask>{
//我们可以从官方文档可以看到Runnable不仅是接口,还可以作为一个类使用
public Runnable runnable;
//毫秒时间戳
long time;
public MyTask(Runnable runnable, long time) {
this.runnable = runnable;
//初始化当前系统的时间加上要运行的时间
this.time = System.currentTimeMillis() + time;
}
//要注意实现任务之间的比较方法,这里比较的是最小时间
@Override
public int compareTo(MyTask o) {
return (int) (this.time - o.time);
}
}
class MyTimer {
//定时器的构成:一个带优先级的阻塞队列
PriorityQueue<MyTask> queue = new PriorityQueue<>();
//创建一个锁对象
private Object block = new Object();
public void schedule(Runnable runnable, long time) {
synchronized (block) {
MyTask myTask = new MyTask(runnable, time);
queue.offer(myTask);
block.notify();
}
}
//这里构造线程,负责实现具体的任务
public MyTimer() {
Thread t1 = new Thread(() -> {
while (true) {
synchronized (block) {
while (queue.isEmpty()) {
try {
block.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
MyTask myTask = queue.peek();
long curTime = System.currentTimeMillis();
//到时间就可以执行run方法了
if(curTime >= myTask.time) {
queue.poll();
myTask.runnable.run();
} else {
try {
//时间没得,等待剩余的时间
block.wait(myTask.time - curTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
t1.start();
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("good!");
}
},4000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("so");
}
},3000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("are");
}
},2000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("you");
}
},1000);
}
}
结果显示
4.线程池
构造方法
如果当前的任务数过多,线程池就会创建一些临时线程
标准库提供的四种拒绝策略
4.1线性池的简单使用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo1 {
public static void main(String[] args) {
//创建一个线程池,线程的数量为三
ExecutorService pool = Executors.newFixedThreadPool(3);
//把任务放到线性池的队列里
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("you are so good");
}
});
}
}
4.2自己实现一个线性池
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class MyThreadPool {
//使用阻塞队列存放任务
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue();
//把任务放到线性池的队列里
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
public MyThreadPool(int n) {
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
try {
while (true) {
Runnable runnable = queue.take();
runnable.run();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t.start();
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(20);
for (int i = 0; i < 600; i++) {
int n = i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("n=" + n);
}
});
}
}
}