- 欢迎光临 ^ V ^
文章目录
- JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式
- 1. 定时器
- 1.1 定时器Timer的使用
- 1.1.1 核心方法schedule
- 1.1.2 定时器管理多个线程
- 1.1.3 定时器的使用场景
- 1.2 自己实现一个定时器
- 1.2.1 属性
- 1.2.2 建立一个MyTask对象
- 1.2.3 schedule方法
- 1.2.4 构造方法初步设计
- 1.2.5 构造方法最终设计
- 1.3 测试MyTimer
- 1.4 补充
- 1.4.1 例子1
- 1.4.2 例子2
- 1.5 顺带一题
- 1.5.1 后者
- 1.5.2 前者
- 2. 线程池
- 2.1 用户态和内核态
- 2.2 标准库线程池类ExecutorService
- 2.3 工厂模式
- 2.3.1 开[A的构造厂]
- 2.3.2 开[A的比较器厂]
- 2.3.3 测试
- 2.4 ExecutorService的属性和方法
- 2.4.1 通过工厂类构造
- 2.4.2 submit方法
- 2.4.3 ThreadPoolExecutor类的属性
- 2.4.4 线程池的拒绝策略
- 2.5 模拟实现线程池
- 2.6 线程池的固定线程数的确定(理论)
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("好耶 ^ v ^");
}
}
},1000);
System.out.println("不好耶 T . T");
}
TimerTask实现了Runnable
而在Timer的schedule方法内部,则将这个线程保存起来,定时后执行~
原因是:
public class Test {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期一好耶 ^ v ^");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期二好耶 ^ v ^");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期三好耶 ^ v ^");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期四好耶 ^ v ^");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期五好耶 ^ v ^");
}
},5000);
System.out.println("今天不好耶 T . T");
}
}
应用场景特别多
而这个任务等待,不应该是无期限的
定时器可以强制终止请求:浏览器内部都有一个定时器,发送请求后,定时器就开始定时;若在规定时间内,响应数据没有返回,就会强制终止请求
这个方法一般在任务的run方法中调用,确定是否及时
是不是触动你的DNA了?
即,定时器底层就是一个阻塞优先级队列! ===> PriorityBlockingQueue
class MyTask {
public Runnable runnable;
public long time;
}
public class MyTimer {
private PriorityBlockingQueue<MyTask> tasks = new PriorityBlockingQueue<>();
}
阻塞优先级队列中的元素应该有如下两个信息:
System.currentTimeMillis()
class MyTask {
public Runnable runnable;
public long time;
//绝对时间戳~
//方便判断~
//这个不是定时时间
public MyTask(Runnable runnable, long delay) {
this.runnable = runnable;
this.time = delay + System.currentTimeMillis();
}
}
public void schedule(Runnable runnable, long delay) {
MyTask myTask = new MyTask(runnable, delay);
tasks.put(myTask);
}
public MyTimer() {
Thread t = new Thread(() -> {
try {
MyTask myTask = tasks.take();
long nowTime = System.currentTimeMillis();
if(myTask.time <= nowTime) {
//启动
}else {
//不能启动
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
对于1. 比较规则:
只需要让MyTask实现比较接口
当然也可以传比较器~(lambda表达式)
两种方式都OK~
左减右大于0
如果代表此对象大于该对象代表升序排列 ===> 小根堆
如果代表此对象小于该对象代表降序排列 ===> 大根堆
对于2. “没有等待”以及“盲目等待”
wait等待,唤醒起来比较方便安全
“盲目等待” 代表,这里放回去后,计算器又会判断是否可启动
那么我们只需要在schedule时唤醒一下,让他才判断一次就行了~
大大减少判断次数!
public void schedule(Runnable runnable, long delay) {
MyTask myTask = new MyTask(runnable, delay);
tasks.put(myTask);
synchronized (locker) {
locker.notify();
}
}
private Object locker = new Object();
public MyTimer() {
Thread t = new Thread(() -> {
while(true) {
synchronized (locker) {
try {
MyTask myTask = tasks.take();
long nowTime = System.currentTimeMillis();
if(myTask.time <= nowTime) {
myTask.runnable.run();
}else {
tasks.put(myTask);
locker.wait(myTask.time - nowTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
用MyTimer替换之前的Timer
TimeTask也可替换为Runnable,不过没关系,向上转型~
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期一好耶 ^ v ^");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期二好耶 ^ v ^");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期三好耶 ^ v ^");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期四好耶 ^ v ^");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期五好耶 ^ v ^");
}
},5000);
System.out.println("今天不好耶 T . T");
}
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期一好耶 ^ v ^");
try {
Thread.sleep(5000);
System.out.println("已过去五秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期二好耶 ^ v ^");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期三好耶 ^ v ^");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期四好耶 ^ v ^");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期五好耶 ^ v ^");
}
},5000);
System.out.println("今天不好耶 T . T");
}
}
注意:
问:wait的同步锁的位置不同,结果会怎么样?
提高效率还能提高轻量化线程“协程”,Java标准库还不支持
而线程池是一个重要的途径~
需要内核支持,才能运行的应用程序~
即,内核给那么多人服务,那么就不一定及时
举个栗子:
也就是说,我们在申请线程时
当然,线程的诞生,还是要内核态申请
Java标准库实现了一个接口,ExecutorService,在进程中服务线程执行~
但是这个接口不是通过new子类对象去实例化的,而是用一个静态方法去实例化~
“工厂”
而工厂模式其实就是,把一个类/接口的构造方法,交给一个“工厂类”去定义
Executors工厂:
你也可以自己“开个厂”
public class A {
int a1;
int a2;
int a3;
int a4;
int a5;
int a6;
}
public static A createA1(int a) {
//匿名内部类优先捕获全局性质变量,这里在代码块内,a1就为全局性变量~
return new A() {
{
this.a1 = a;
}
};
}
public static A createA2(int a) {
return new A() {
{
this.a2 = a;
}
};
}
public static A createA3(int a) {
return new A() {
{
this.a3 = a;
}
};
}
public static A createA4(int a) {
return new A() {
{
this.a4 = a;
}
};
}
public static A createA5(int a) {
return new A() {
{
this.a5 = a;
}
};
}
public static A createA6(int a) {
return new A() {
{
this.a6 = a;
}
};
}
}
class CreateComparatorA {
public static Comparator<A> createA1() {
return ((o1, o2) -> {
return o1.a1 - o2.a1;
});
}
public static Comparator<A> createA2() {
return ((o1, o2) -> {
return o1.a2 - o2.a2;
});
}
public static Comparator<A> createA3() {
return ((o1, o2) -> {
return o1.a3 - o2.a3;
});
}
public static Comparator<A> createA4() {
return ((o1, o2) -> {
return o1.a4 - o2.a4;
});
}
public static Comparator<A> createA5() {
return ((o1, o2) -> {
return o1.a5 - o2.a5;
});
}
public static Comparator<A> createA6() {
return ((o1, o2) -> {
return o1.a6 - o2.a6;
});
}
}
public class A {
int a1;
int a2;
int a3;
int a4;
int a5;
int a6;
//参数列表相同无法特定构造特定成员~
@Override
public String toString() {
return "A{" +
"a1=" + a1 +
", a2=" + a2 +
", a3=" + a3 +
", a4=" + a4 +
", a5=" + a5 +
", a6=" + a6 +
'}' + '\n';
}
public static void main(String[] args) {
PriorityQueue<A> queue1 = new PriorityQueue<>(CreateComparatorA.createA1());
PriorityQueue<A> queue2 = new PriorityQueue<>(CreateComparatorA.createA2());
PriorityQueue<A> queue3 = new PriorityQueue<>(CreateComparatorA.createA3());
PriorityQueue<A> queue4 = new PriorityQueue<>(CreateComparatorA.createA4());
PriorityQueue<A> queue5 = new PriorityQueue<>(CreateComparatorA.createA5());
PriorityQueue<A> queue6 = new PriorityQueue<>(CreateComparatorA.createA6());
queue1.offer(createA.createA1(2));
queue1.offer(createA.createA1(1));
queue1.offer(createA.createA1(4));
queue1.offer(createA.createA1(3));
queue1.offer(createA.createA1(5));
System.out.println(queue1);
}
}
当然,工厂当然不只可以生产构造方法:
最重点的一个:
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(() -> {
System.out.println("好耶 ^ v ^ ");
});
}
下面是Java的官方文档的内容:
线程池会在任务少的空闲期,根据这些参数进行线程调整,把一些临时线程给销毁了~
keepAliveTime 为临时线程存活时间~
unit ==> 时间单位
public class MyThreadPool {
private BlockingQueue<Runnable> pool = new LinkedBlockingQueue<>();
public void submit(Runnable runnable) throws InterruptedException {
pool.put(runnable);
}
//实现固定线程数的线程池
//不是容量,是确确实实的线程数
public MyThreadPool(int number) {
for (int i = 0; i < number; i++) {
Thread thread = new Thread(() -> {
Runnable runnable = null;
try {
while(true) {
runnable = pool.take();
runnable.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
}
}
我们的简单实现,不涉及2.4.3的属性~
注意:这里的线程数,是工作人数,定量
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool(10);
for (int i = 1; i <= 1000; i++) {
int id = i; //线程id,变量捕获~
myThreadPool.submit(() -> {
System.out.println("好耶^ v ^ " + id);
});
}
线程池中如何体现,“用户态拿”:
注意:
至于线程池固定线程数,设置为多少合适?
cpu密集型,主要做一些计算工作,要在cpu上运行~
IO密集型,主要等待一些IO操作(读写硬盘/读写网卡),不怎么吃cpu
而实际情况不会这么极端,所以这个线程数一定是要看实际情况的
文章到此结束!谢谢观看 !
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭!多线程初阶已经结束~ 后续会出线程进阶的博客!
敬请期待吧~