阻塞队列
class MyBlockingQueue{
private String[]elems=null;
private volatile int head=0;
private volatile int size=0;
private volatile int tail=0;
public MyBlockingQueue(int capacity){
elems=new String[capacity];
}
void put(String elem) throws InterruptedException {
synchronized (this) {
if (size >= elems.length) {
this.wait();
return;
}
elems[tail] = elem;
tail++;
if (tail >= elem.length()) {
tail = 0;
}
size++;
this.notify();
}
}
String take() throws InterruptedException {
synchronized (this) {
if (size ==0) {
this.wait();
}
String result= elems[head];
head++;
if (head >= elems.length) {
head = 0;
}
size--;
this.notify();
return result;
}
}
public class Demo1 {
public static void main(String[] args) {
}
}
wait不仅仅会被notify和notifyAll唤醒也可能被其他的操作唤醒比如interrupt。
优化再用wait方法的时候,应该搭配while判断条件的方式。这样的话就可以在被唤醒的时候再次确认,条件是否能再次成立,防止他覆盖到原来的哪些元素,当然这里的while循环对资源的调度是很小的,所以在执行了很多次以后就会忽略,防止了循环过多次而造成的死循环。
当然wait也是可以被interrupt打断的。
线程池
并发编程,一般会使用多进程,但是如果进程的任务很小,那么就会频繁的创建和销毁线程,这样就会消耗很多资源,这样就会降低效率。
直接创建/销毁线程,是需要用户态+内核态配合完成的工作。
线程池/协程,创建销毁,只通过用户态即可,不需要内核态的配合。如果使用线程池,提前把线程都创建好,放到用户态代码中写的数据结构里面后面用的时候,随时从池子里取,用完了放回池子里去这个过程,完全是用户态代码,不需要和内核进行交互。这将大大提高效率。
为什么调用内核态会大大降低效率呢?
是由于内核态不止会只有线程创建与销毁的任务,他还有其他的任务,但是任务的优先级都是大同小异的,所以一般线程的任务会等待,导致效率的降低。
核心线程数
int corePoolSize
最大线程数
int maximumPoolSize
最大线程数=核心线程数+非核心线程数。
动态扩展一个线程池,刚被创建出来的时候,里面就包含核心线程数这么多的线程,
此时,线程池里就是包含4个线程线程池会提供一个submit方法,往里面添加任务.每个任务都是一个Runnable对象,如果当前添加的任务比较少,4个线程就足以能够处理,就只有4个线程在工作了,如果添加的任务比较多,4个线程处理不过来了.(有很多的任务在排队等待执行)这个时候,线程池就会自动创建出新的线程,来支撑更多的任务创建出来的线程总数,不能超过最大线程数过了一段时间之后,任务没那么多了,线程清闲了,部分线程就会被释放掉(回收了)回收只是把非核心线程回收掉,至少会保证线程池中线程数目不少于核心线程数。
就像公司招聘,如果公司压力大,那么就会招实习生,但是压力小的时候,就会辞退临时工。
线程数的设置,是根据具体情况来定的。但是大体可以分为两大类:
1.cpu密集型程序.
你的代码完成的逻辑,都是要通过cpu干活来完成的.
public class Demo2 {
Thread thread=new Thread(()->{
int count=0;
for(int i=0;i<10000;i++){
count++;
System.out.println(count);
}
});
}
这样的代码就会使cpu疯狂工作。
2.IO密集型程序.
你的代码大部分时间在等待IO(等待IO是不消耗CPU,不参与调度)
Thread thread1=new Thread(()->{
Scanner scanner=new Scanner(System.in);
while (true){
int a=scanner.nextInt();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
协程(纤程)和线程池的功能是相同的,但是方法的实现方式不同。
非核心线程,孕育最大空闲时间, 时间单位(s,min,h,ms).
long keepAliveTime ,TimeUnit unit
非核心线程,在线程池不忙的时候,就会回收。
线程池的任务队列
BlockingQueue<Runnable> workQueue
是一个阻塞队列, 它可以用来存放任务。常用的实现类有`LinkedBlockingQueue`()、`ArrayBlockingQueue(数组)
工厂模式
不再使用构造方法创建对象给构造方法包装一层
ThreadFactory threadFactory
这个线程工厂,主要是为了批量的给要创建的线程设置一些属性啥的线程工厂,在它工厂方法中,把线程的属性提前初始化好了,主要是解决,基于构造方法创建对象太坑了的问题,
当出现重载无法表示的问题的时候,就像
public void zuobiao(int a,int b){
}
public void zuobiao(int r,int a){
}
这样用笛卡尔坐标系和极坐标系的方式,这样的话就会造成重载(构造方法名字固定,类型和变量多少不一定)无法表示的方法这时候就需要用到工厂方法,虽然工厂方法不常用。
public static A makexy(int x,int y){
A a=new A();
a.setX();
a.setY();
return a;
}
public static A makera(int r,int a){
A a=new A();
a.setr();
a.seta();
return a;
}
异常处理方式
RejectedExecutionHandler handler
1.ThreadPoolExecutor.AbortPolicy,
直接抛出异常.(让程序猿快速的知道,任务处理不过来了,代码罢工了)
2.ThreadPoolExecutor.CallerRunsPolicy,
你给我这个线程池添加任务,不好意思,我这满了,你自己来执行谁负责添加任务,谁负责执行任务.线程池本身,不管了.
submit内部要做的事情不仅仅是入队列如果发现,队列满&&当前使用的这个拒绝策略,就会在submit内部自己去执行Runnable的run方法
3.ThreadPoolExecutor.DiscardoldestPolicy,
丢弃掉最老的任务.让新的任务去队列中排队
任务队列中队首的元素舍弃掉
4.ThreadPoolExecutor.DiscardPolicy
丢弃最新的任务,还是按照原有的节奏来执行
正在submit这个任务就不要了.