Java 并发编程(JUC 上)
大家好,我是Java小羽,今天我们聊聊Java并发编程。
1.线程和进程
java可以开启线程吗?
不能开启,start()方法调用的是本地方法start0();
public synchronized void start() { / * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */ } }}//调用本地方法private native void start0();
其中native主要是用来加载其他语言的动态链接库的,被native加载的方法是本地方法,由于Java不能直接调用计算机硬件相关的操作,所以通过native关键字修饰,调用c/c++的库来实现效果。
并行和并发
- 并行:单核cpu处理多个线程时,在多个线程之间来回跳转操作,看起来就像多个线程同时运行一样。
- 并发:只有在多核cpu下才能实现,cpu对多个线程同时进行操作。
买票例子,使用传统的 synchronized锁实现
package JUC.demo1;public class SaleTaskDemo02 { public static void main(String[] args) {//使用lambam表达式 Ticket ticket=new Ticket(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"C").start(); }}class Ticket{ //属性,方法 private int number=50; public synchronized void sale(){ if (number>0){ System.out.println(Thread.currentThread().getName()+"出售第"+number--+"张票,还剩"+number+"票"); } }}
2.Lock锁
什么是Lock锁?
Lock锁的功能和Synchronized锁的功能一样,是用来保证数据唯一性的。
Lock锁又分为:
-
公平锁:先进来的线程先执行,不可以插队。
-
非公平锁:可以插队。默认为非公平锁。
public class SaleTaskDemo03 { public static void main(String[] args) { Ticket ticket=new Ticket(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"C").start(); }}class Ticket1{ //属性,方法 private int number=50; Lock lock=new ReentrantLock();//创建可重用锁 public void sale(){ lock.lock();//开锁 try { if (number>0){ System.out.println(Thread.currentThread().getName()+"出售第"+number--+"张票,还剩"+number+"票"); } }catch (Exception e){ }finally { lock.unlock();//关锁 } }}
3.生产者和消费者问题
生产者和消费者synchronized版本解决
package JUC.demo1.pc;public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); }}//等待,业务代码,唤醒class Data{ private static int number=0; //++1 public synchronized void increment() throws InterruptedException { if (number!=0){ this.wait();//线程等待 } number++; System.out.println(Thread.currentThread().getName()+"->"+number); if(number==1){ this.notifyAll();//唤醒所有线程 } } //--1 public synchronized void decrement() throws InterruptedException { if (number!=1){ this.wait();//线程等待 } number--; System.out.println(Thread.currentThread().getName()+"->"+number); if(number==0){ this.notifyAll();//唤醒所有线程 } }}
A B C D 四个线程,防止虚假唤醒!
package JUC.demo1.pc;public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); }}//等待,业务代码,唤醒class Data{ private static int number=0; //++1 public synchronized void increment() throws InterruptedException { while (number!=0){ this.wait();//线程等待 } number++; System.out.println(Thread.currentThread().getName()+"->"+number); if(number==1){ this.notifyAll();//唤醒所有线程 } } //--1 public synchronized void decrement() throws InterruptedException { while (number!=1){ this.wait();//线程等待 } number--; System.out.println(Thread.currentThread().getName()+"->"+number); if(number==0){ this.notifyAll();//唤醒所有线程 } }}
JUC版的生产者和消费者问题
package JUC.demo1.pc;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class B { public static void main(String[] args) { Data1 data = new Data1(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); }}//等待,业务代码,唤醒class Data1{ private static int number=0; Lock lock=new ReentrantLock(); Condition condition= lock.newCondition();//创建监视器对象 //++1 public void increment() throws InterruptedException { lock.lock(); try { while (number!=0){ condition.await();//线程等待 } number++; System.out.println(Thread.currentThread().getName()+"->"+number); if(number==1){ condition.signalAll();//唤醒所有线程 } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } //--1 public void decrement() throws InterruptedException { lock.lock(); try { while (number!=1){ condition.await();//线程等待 } number--; System.out.println(Thread.currentThread().getName()+"->"+number); if(number==0){ condition.signalAll();//唤醒所有线程 } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}
condition实现精准线程唤醒
package JUC.demo1.pc;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class C { public static void main(String[] args) { Data2 data2=new Data2(); new Thread(()->{ for (int i = 0; i < 20; i++) { data2.printA(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 20; i++) { data2.printB(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 20; i++) { data2.printC(); } },"C").start(); }}class Data2{ //A-》B-》C private Lock lock=new ReentrantLock(); private Condition conditionA=lock.newCondition();//创建监视器。 private Condition conditionB=lock.newCondition(); private Condition conditionC=lock.newCondition(); private int num=1; public void printA(){ lock.lock(); try { //写业务代码 while(num!=1){//这里使用while而不是if防止自旋现象的发生。 conditionA.await();//A线程等待 } num=2; System.out.println(Thread.currentThread().getName()+"->AAAAAA"); conditionB.signal();//唤醒B线程 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB(){ lock.lock(); try { //写业务代码 while(num!=2){ conditionB.await();//B线程等待 } num=3; System.out.println(Thread.currentThread().getName()+"->BBBBB"); conditionC.signal();//唤醒C线程 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC(){ lock.lock(); try { //写业务代码 while(num!=3){ conditionC.await();//B线程等待 } num=1; System.out.println(Thread.currentThread().getName()+"->CCCCC"); conditionA.signal();//唤醒A线程 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}
4.八锁现象
package JUC.Lock8;import java.util.concurrent.TimeUnit;//1.标准情况下两个线程先打印 发短信还是打电话? 1/发短信 2/打电话//2.sendSms()增加延时的情况下情况下两个线程先打印 发短信还是打电话? 1/发短信 2/打电话public class Tast01 { public static void main(String[] args) throws InterruptedException { Telephone telephone=new Telephone(); new Thread(()->{//发短信 telephone.sendSms(); },"A").start(); TimeUnit.SECONDS.sleep(1);//休眠一秒 new Thread(()->{//打电话 telephone.call(); },"B").start(); }}class Telephone{//synchronized 锁的对象是方法的调用者。 //两个方法用的是同一个锁,谁先拿到谁执行 public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(1);//休眠一秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"发短信"); } public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"打电话"); }}
package JUC.Lock8;import java.util.concurrent.TimeUnit;//5.增加两个静态同步方法,一个对象,是先打印发短信还是打电话? 发短信。//6.增加两个静态同步方法,两个对象,是先打印发短信还是打电话? 发短信。public class Tast03 { public static void main(String[] args) throws InterruptedException { Telephone02 telephone1=new Telephone02(); Telephone02 telephone2=new Telephone02(); new Thread(()->{//发短信 telephone1.sendSms(); },"A").start(); TimeUnit.SECONDS.sleep(1);//休眠一秒 new Thread(()->{//打电话 //telephone1.call(); telephone2.call(); },"B").start(); }}//Telephone02是唯一的Class对象,一个类只有唯一的一个Class对象class Telephone02{ //static是类加载的时候就存在的,static锁的是Class。 //synchronized锁的是方法的调用者 public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4);//休眠4秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"发短信"); } public static synchronized void call(){ System.out.println(Thread.currentThread().getName()+"打电话"); }}
package JUC.Lock8;import java.util.concurrent.TimeUnit;//7.一个静态同步方法,一个普通同步方法,一个对象,是先打印发短信还是打电话? 打电话//8.一个静态同步方法,一个普通同步方法,两个对象,是先打印发短信还是打电话? 打电话public class Tast04 { public static void main(String[] args) throws InterruptedException { Telephone03 telephone1=new Telephone03(); Telephone03 telephone2=new Telephone03(); new Thread(()->{//发短信 telephone1.sendSms(); },"A").start(); TimeUnit.SECONDS.sleep(1);//休眠一秒 new Thread(()->{//打电话 //telephone1.call(); telephone2.call(); },"B").start(); }}class Telephone03{ //static锁的是Class模板 public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4);//休眠4秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"发短信"); } //synchronized锁的是调用者的对象,是两把锁互不影响。 public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"打电话"); }}
5.集合类不安全
list不安全
package JUC.unsafe;import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;//java.util.ConcurrentModificationException//并发修改异常public class Task01 { public static void main(String[] args) { //并发下ArrayList不安全吗? /* 解决方案 1.List list=new Vector(); 2.List list= Collections.synchronizedList(new ArrayList()); 3.List list = new CopyOnWriteArrayList(); */ //CopyOnWrite写入时复制 cow 计算机程序设计领域的一种优化策略 //多个线程调用的时候,list,读取的时候,固定的写入覆盖。 //在写入时避免覆盖,造成数据问题 //CopyOnWriteArrayList 比Vector 好在哪里? //1.CopyOnWriteArrayList底层使用的是Lock锁,Vector使用的是synchronized。 //2.CopyOnWriteArrayList 比Vector的效率高 List<String> list = new CopyOnWriteArrayList<>(); for (int i = 1; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },String.valueOf(i)).start(); } }}
CopyOnWriteArrayList :写入时复制,只有在要修改list数据的时候,程序会从原来的list数据复制一份,在复制的一份list上进行修改操作,修改完成后覆盖原来的数据。
set不安全
public class ListSet { public static void main(String[] args) { Set<String> set=new CopyOnWriteArraySet<>(); for (int i = 1; i <10 ; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(set); },String.valueOf(i)).start(); } }}
CopyOnWriteArraySet和CopyOnWriteArrayList的原理一致,只是一个是list集合一个是set集合。
HashSet底层是什么?
HashSet底层是创建了一个HashMap集合,HashSet使用的是HashMap的Key值,HashMap中的Key值是不可重复的。
一下是源码:
public HashSet() { map = new HashMap<>();}public boolean add(E e) { return map.put(e, PRESENT)==null;}//PRESENT是一个常量private static final Object PRESENT = new Object();
Map不安全
在多线程下Map集合是不安全的,可以使用ConcurrentHashMap(并发哈希Map)
public class DemoHashMap { public static void main(String[] args) { Map<String,String> map=new ConcurrentHashMap<>(); for (int i = 1; i <10 ; i++) { new Thread(()->{ map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5)); System.out.println(map); },String.valueOf(i)).start(); } }}
6.Callable接口
Callable接口和Runnable接口一样是一种实现线程的接口,但是Callable接口重写的是call方法有返回值,而Runnable接口是重写的run方法没有返回值。
package JUC.callable;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.Future;import java.util.concurrent.FutureTask;public class CallableTast { public static void main(String[] args) throws ExecutionException, InterruptedException { MyThread myThread=new MyThread();//创建对象 FutureTask task=new FutureTask(myThread);//适配类,用于接收运算的返回值 new Thread(task,"A").start();//启动线程 new Thread(task,"B").start();//再次启动线程,结果会被缓存,效率高。 String s= (String) task.get();//这个get方法可能会阻塞,把他放在最后 System.out.println(s); }}class MyThread implements Callable<String>{ @Override public String call() throws Exception { System.out.println("call()"); return "aaa"; }}
细节:
1.Callable有缓存
2.get方法可能会阻塞。
7.常用辅助类
7.1CountDownLatch
CountDownLatch减法计数器
package JUC.callable;import java.util.concurrent.CountDownLatch;public class CountDownLachDemo { public static void main(String[] args) throws InterruptedException { //创建倒计时计数器对象,从6开始倒计时 CountDownLatch count=new CountDownLatch(6); for (int i = 1; i <=6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+"-》GO"); //计数器-1 count.countDown(); },String.valueOf(i)).start(); } count.await();//倒计时结束,执行等待。 System.out.println("Over"); }}
7.2CyclicBarrier
CyclicBarrier加法计数器
package JUC.callable;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo { public static void main(String[] args) throws BrokenBarrierException{ //计时器加载到 7 的时候执行 召唤神龙 CyclicBarrier barrier=new CyclicBarrier(7,()->{ System.out.println("召唤神龙"); }); for (int i = 1; i <=7; i++) { final int temp=i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"第"+temp+"个"); try { barrier.await();//等待 } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } }}
7.Semaphore
Semaphore信号量,就是一种用来控制并发的控制器,就像厕所有3个坑位,来一个人占一个坑,直到三个坑都占满,再来的人就等待。直到一个人出来了,下一个人才能出去。
package JUC.callable;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class SemaphoreDemo { public static void main(String[] args) { //三个停车位 Semaphore semaphore=new Semaphore(3); for (int i = 1; i <=6 ; i++) { new Thread(()->{ try { //获取线程 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"获取停车位"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"离开停车位"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //达到信号量3时释放线程。 semaphore.release(); } },String.valueOf(i)).start(); } }}
semaphore.acquire();
获取线程,如果已经满了,则等待,直到释放线程。
semaphore.release();
会将当前的信号量释放+1,唤醒等待的线程。
作用:1.限流的时候使用。
2.共享资源互斥的时候使用。
8.读写锁
ReentrantReadWriteLock可重用读写锁
package JUC.rw;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReentrantReadWriteLockDemo { public static void main(String[] args) { MyCacheLock myCacheLock=new MyCacheLock(); //创建10个线程写入 for (int i = 1; i <=10 ; i++) { final int temp=i; new Thread(()->{ myCacheLock.put(temp+"",temp+""); },String.valueOf(i)).start(); } //创建10个线程读取 for (int i = 1; i <=100 ; i++) { final int temp=i; new Thread(()->{ myCacheLock.get(temp+""); },String.valueOf(i)).start(); } }}class MyCacheLock{ private volatile Map<String,String> map=new HashMap<>(); private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();//定义可重用读写锁 public void put(String s,String v){ //上写锁 lock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"写入"+s); map.put(s,v); System.out.println(Thread.currentThread().getName()+"写入成功"); } catch (Exception e) { e.printStackTrace(); } finally { //解锁 lock.writeLock().unlock(); } } public void get(String s){ //上读锁 lock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"读取"+s); Object o=map.get(s); System.out.println(Thread.currentThread().getName()+"读取成功"); } catch (Exception e) { e.printStackTrace(); } finally { //解锁 lock.readLock().unlock(); } }}
总结:
读的时候可以被多个线程读。(共享锁)lock.readLock()
写的时候只能被一个一个线程写。(独占锁)lock.writeLock()
9.阻塞队列
阻塞队列结构图
什么情况下我们会使用阻塞队列?
- 多线程并发处理,线程池!
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer(,) |
移除 | remove() | poll() | take() | poll(,) |
判断队列首部 | element() | peek() | - | - |
package JUC.Queue;import java.util.concurrent.ArrayBlockingQueue;/*抛出异常*/public class ArrayQueue { public static void main(String[] args) { task01(); } public static void task01(){ ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue<>(3); //抛异常IllegalStateException: Queue full 队列已满异常 //System.out.println(arrayBlockingQueue.add("d")); System.out.println(arrayBlockingQueue.add("a")); System.out.println(arrayBlockingQueue.add("b")); System.out.println(arrayBlockingQueue.add("c")); //NoSuchElementException 队列没有元素异常 //System.out.println(arrayBlockingQueue.remove()); System.out.println(arrayBlockingQueue.remove()); System.out.println(arrayBlockingQueue.remove()); System.out.println(arrayBlockingQueue.remove()); }}
public static void task02(){ /* 不抛异常,有返回值 */ ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue<>(3); //添加元素成功返回true,失败返回false arrayBlockingQueue.offer("a"); arrayBlockingQueue.offer("a"); System.out.println(arrayBlockingQueue.offer("a")); System.out.println(arrayBlockingQueue.offer("a")); //删除元素,删除成功返回删除元素,失败返回null System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); //返回队列首部元素 System.out.println(arrayBlockingQueue.peek()); }
public static void task03() throws InterruptedException { /* 队列阻塞(一直阻塞) */ ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue<>(3); //添加元素无返回值 arrayBlockingQueue.put("a"); arrayBlockingQueue.put("a"); arrayBlockingQueue.put("a"); //arrayBlockingQueue.put("a"); //删除元素,返回删除元素 System.out.println(arrayBlockingQueue.take()); System.out.println(arrayBlockingQueue.take()); System.out.println(arrayBlockingQueue.take()); //System.out.println(arrayBlockingQueue.take()); }
public static void task04() throws InterruptedException { /* 队列阻塞(超时等待,超时后不再等待) */ ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue<>(3); //添加元素,超过两秒后不再等待。 arrayBlockingQueue.offer("a",2, TimeUnit.SECONDS); arrayBlockingQueue.offer("a",2, TimeUnit.SECONDS); arrayBlockingQueue.offer("a",2, TimeUnit.SECONDS); arrayBlockingQueue.offer("a",2, TimeUnit.SECONDS); //删除元素,超过两秒后不再等待。 arrayBlockingQueue.poll(); arrayBlockingQueue.poll(); arrayBlockingQueue.poll(); arrayBlockingQueue.poll(2,TimeUnit.SECONDS); }
同步队列SynchronousQueue
官方解释如下图
简单来说SynchronousQueue同步队列,只能放入一个线程,想再插入线程时,只有删除上一个线程才能成功插入,否则阻塞。
package JUC.Queue;import java.util.concurrent.SynchronousQueue;public class SynchronousQueueDemo { public static void main(String[] args) { SynchronousQueue<String> strings=new SynchronousQueue<>(); new Thread(()->{ try { //put阻塞等待 strings.put("a"); } catch (InterruptedException e) { e.printStackTrace(); } },"A").start(); new Thread(()->{ try { //take阻塞等待 strings.take(); } catch (InterruptedException e) { e.printStackTrace(); } },"B").start(); }}
10.线程池(重点)
线程池:三大方法,七大参数,4种拒绝策略
池化技术
本质:占用系统的资源,优化资源的使用。
事先准备好一些资源,有人要用就拿走,用完再还回来。
线程池好处:
1.降低资源的消耗
2.提高响应速度
3.方便管理
线程复用,可以控制最大并发数,管理线程。
1.三大方法
package JUC.pool;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Demo01 { public static void main(String[] args) { //单个线程池 ExecutorService service = Executors.newSingleThreadExecutor();// //固定大小的线程池// ExecutorService service = Executors.newFixedThreadPool(5);// //弹性线程池,遇强则强// ExecutorService service = Executors.newCachedThreadPool(); try { for (int i = 0; i < 10; i++) { //用线程池创建线程 service.execute(()->{ System.out.println(Thread.currentThread().getName()+"->OK"); }); } } catch (Exception e) { e.printStackTrace(); } finally { //关闭线程池 service.shutdown(); } }}
阿里编程手册:
2.七大参数
package JUC.pool;import java.util.concurrent.*;public class Demo01 { public static void main(String[] args) { //创建线程池执行器,工作的时候常用 ExecutorService service=new ThreadPoolExecutor( 2,//线程核心数,最小运行的线程数 5,//最大线程数 2L,//超过时长没有人调用就释放 TimeUnit.SECONDS,//超时单位 new LinkedBlockingDeque<Runnable>(3),//阻塞双端队列 Executors.defaultThreadFactory(),//线程工厂,创建线程的,一般默认 new ThreadPoolExecutor.AbortPolicy()//拒绝策略 ); try { //最大承载数=Deque阻塞队列数+max最大核心数 for (int i = 1; i <=9; i++) { //用线程池创建线程 service.execute(()->{ System.out.println(Thread.currentThread().getName()+"->OK"); }); } } catch (Exception e) { e.printStackTrace(); } finally { //关闭线程池 service.shutdown(); } }}
3.四种拒绝策略
/*中止策略* new ThreadPoolExecutor.AbortPolicy()//线程池满了,还有线程来,不处理,抛异常 调用者运行策略* new ThreadPoolExecutor.CallerRunsPolicy()//线程池满了,还有线程来,哪里来的回那里去。 丢弃策略* new ThreadPoolExecutor.DiscardPolicy()//线程池满了,丢掉任务,不抛异常。 丢弃老的策略* new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试与最先进来的线程竞争,竞争成功执行,反之丢掉,不抛异常*/
CPU密集型和IO密集型
最大线程到底该如何定义
1.CPU密集型:电脑是几核就是几,可以保持CPU的效率最高
2.IO密集型: > 判断你程序中十分耗IO的线程。(一般为IO线程的两倍)
Runtime.getRuntime().availableProcessors();
获取当前电脑cpu核心。