线程池
标准库中的线程池
ExecutorService service=Executors.newCachedThreadPool();//根据任务数目,自动进行扩容
Executors.newFixedThreadPool(4);//创建固定线程数目的线程池
Executors.newSingleThreadExecutor();//创建一个只包含单个线程的线程池
Executors.newScheduledThreadPool(4);//创建一个固定的线程数,但是任务演示执行的线程池。
线程池的应用
public static void main(String[] args) {
ExecutorService service=Executors.newCachedThreadPool();//根据任务数目,自动进行扩容
for (int i=0;i<10000;i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println( i+ Thread.currentThread().getName());
}
});
}
上述中 i 存在问题,这是变量捕获问题,i一直在改变不能在print中使用,所以更改代码如下
public static void main(String[] args) {
ExecutorService service=Executors.newCachedThreadPool();//根据任务数目,自动进行扩容
for (int i=0;i<10000;i++) {
int id=i;
service.submit(new Runnable() {
@Override
public void run() {
System.out.println( id+ Thread.currentThread().getName());
}
});
}
创建线程池
一个线程池要包含哪些东西?
1)有若干个线程
2)有任务队列.(使用Runnable即可)
3)提供submit方法
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
class MyThreadPool{
private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);
public MyThreadPool(int n){
for (int i=0;i<n;i++){
Thread t=new Thread(()->{
while (true) {
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
void subimt(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
}
public class Test1 {
public static void main(String[] args) {
MyThreadPool threadPool=new MyThreadPool(10);
for(int i=0;i<1000;i++){
int id=i;
try {
threadPool.subimt(new Runnable() {
@Override
public void run() {
System.out.println( id+ Thread.currentThread().getName());
}
});
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
main线程的任务是先是创建对象,通过调用MyThreadPoll的构造方法来创建n个线程,然后各个线程进入无限循环,然后等到有任务进入,然后通过sbimt方法添加到任务队列queue,然后到任务队列,任务队列将其分布到各个线程最后达到线程池的作用。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
class MyThreadPool2{
private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);
private int maxPoolSize=50;
private List<Thread> threadList=new ArrayList<>();
public MyThreadPool2(int n,int maxPoolSize){
this.maxPoolSize=maxPoolSize;
for (int i=0;i<n;i++){
Thread t=new Thread(()->{
while (true) {
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
threadList.add(t);
}
}
void subimt(Runnable runnable) throws InterruptedException {
//此处进行判定,判定说当前任务队列的元素个数,是否比较长
//如果队列元素比较长,说明已有的线程,不太能处理过来了、创建新的线程即可,
// 如果队列不是很长,没必要创建新的线程.
queue.put(runnable);
if(queue.size()>=500&& threadList.size()<maxPoolSize ){
Thread t=new Thread(()->{
while (true) {
try {
Runnable take = queue.take();
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
}
public class Test2 {
public static void main(String[] args) {
MyThreadPool2 threadPool2=new MyThreadPool2(20.10);
for(int i=0;i<1000;i++){
int id=i;
try {
threadPool2.subimt(new Runnable() {
@Override
public void run() {
System.out.println( id+ Thread.currentThread().getName());
}
});
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
可以将线程数加入对应的队列,然后就可以实现最大线程的限制,但是最大线程的限制条件在submit函数中应用
实现拒绝策略的核心,在submit这里
2)submit的时候,先判定当前任务队列元素是否已经比较多了(自行设定一个阈值,比如,900)
3)如果确实比较多了,就根据刚才构造时指定的拒绝策略,执行不同的逻辑,
a)直接抛出异常,
b)直接在submit中调用Runnable的run.
c)删除队列的队首元素 (可以一次删多个,也不一定是一个)
d)丢弃当前元素,不添加到队列中.
定时器
定时器,就是“闹钟"效果
指定一个任务(Runnable),并且指定一个时间(3000ms),此时这个任务不会立即执行,而是在时间到达之后,再去执行。
定时器的应用
第一个参数是一个抽象类,本质上还是实现了Runnable.就把它当做Runnable来使用即可.
import java.util.Timer;
import java.util.TimerTask;
public class Test3 {
public static void main(String[] args) {
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(3000);
}
},3000);
}
}
线程不会结束,由于Timer中包含了前台宪政,阻止了进程的结束。
(TimerTasktask,longdelay)
表示"多长时间之后”执行.以当前执行schedule的时刻为基准,继续等delay时间之后再去进一步的执行.
创建定时器
需求:
1)能够延时执行任务/指定时间执行任务
2)能够管理多个任务
1.定义一个类,表示一个任务(Runnable)
class MyTimerTask{
private Runnable runnable;
private long time;
public MyTimerTask(Runnable runnable){
this.runnable=runnable;
this.time=System.currentTimeMillis()+time;
}
public void run(){
runnable.run();
}
public long getTime(){
return time;
}
}
2.通过一定的数据结构,保存多个任务,
class MyTimer{
private List<MyTimerTask> taskList=new ArrayList<>();
public void schedule(Runnable runnable,long delay){
MyTimerTask timerTask=new MyTimerTask(runnable,delay);
taskList.add(timerTask);
}
}
这样会造成不停的扫描操作,这样会消耗太多资源,可以应用优先级队列。
可以把这些任务通过优先级队列保存起来.按照时间作为优先级的先后的标准就可以做到,队首元素就是时间最靠前的任务
class MyTimer{
private PriorityQueue<MyTimerTask> taskList=new PriorityQueue<>();
public void schedule(Runnable runnable,long delay){
MyTimerTask timerTask=new MyTimerTask(runnable,delay);
taskList.add(timerTask);
}
}
3.还需要有一个线程,来负责执行这里的任务.(在指定的时间去执行)
构造方法中,创建一个线程,用新的线程来执行队列中的任务,
public MyTimer(){
Thread thread=new Thread(()->{
while (true){
if(taskList.size()==0){
continue;
}
MyTimerTask timerTask=taskList.peek();
long curTime=System.currentTimeMillis();
if(curTime>=timerTask.getTime()){
taskList.poll();
}else {
continue;
}
}
});
}
优化,会有线程安全所以要加锁,要不然会出现重复扫描的问题。
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
class MyTimerTask{
private Runnable runnable;
private long time;
public MyTimerTask(Runnable runnable, long delay){
this.runnable=runnable;
this.time=System.currentTimeMillis()+delay;
}
public void run(){
runnable.run();
}
public long getTime(){
return time;
}
}
class MyTimer{
Object o=new Object();
private PriorityQueue<MyTimerTask> taskList=new PriorityQueue<>();
public void schedule(Runnable runnable,long delay){
synchronized (o) {
MyTimerTask timerTask = new MyTimerTask(runnable, delay);
taskList.add(timerTask);
}
}
public MyTimer(){
Thread thread=new Thread(()->{
while (true) {
synchronized (o) {
if (taskList.size() == 0) {
continue;
}
MyTimerTask timerTask = taskList.peek();
long curTime = System.currentTimeMillis();
if (curTime >= timerTask.getTime()) {
taskList.poll();
} else {
continue;
}
}
}
});
}
}
}
现在存在忙等状态
上述代码就会短时间内循环很多次.上述操作都是在王"空转”一直在持续消耗cpu,但是没有真正执行任务。
try {
o.wait(curTime-timerTask.getTime());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
此处应用wait方法不用sleep
1.notify唤醒wait,属于常规编程手法(处理正常业务流程)通过interrupt唤醒sleep,非常规手法.(处理异常业务流程)
2.sleep会抱锁,wait不会抱锁
这个代码中,不太适合直接使用PriorityBlockingQueue
只能处理,队列为空时候的阻塞,如果使用上述阻塞版本,还需要通过额外的锁对象和wait来实现针对时间未到的情冰
比较肯定要写构造方法:如下
最终写出的个人task如下:
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
class MyTimerTask implements Comparable<MyTimerTask>{
private Runnable runnable;
private long time;
public MyTimerTask(Runnable runnable, long delay){
this.runnable=runnable;
this.time=System.currentTimeMillis()+delay;
}
public void run(){
runnable.run();
}
public long getTime(){
return time;
}
@Override
public int compareTo(MyTimerTask o) {
return (int) (this.time-o.time);
}
}
class MyTimer{
Object o=new Object();
private PriorityQueue<MyTimerTask> taskList=new PriorityQueue<>();
public void schedule(Runnable runnable,long delay){
synchronized (o) {
MyTimerTask timerTask = new MyTimerTask(runnable, delay);
taskList.add(timerTask);
}
}
public MyTimer(){
Thread thread=new Thread(()->{
while (true) {
synchronized (o) {
if (taskList.size() == 0) {
continue;
}
MyTimerTask timerTask = taskList.peek();
long curTime = System.currentTimeMillis();
if (curTime >= timerTask.getTime()) {
taskList.poll();
} else {
try {
o.wait(curTime-timerTask.getTime());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
}
}
}
public class Test4 {
public static void main(String[] args) {
}
}