大家好,我是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 then
it 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<String> list=new Vector<>();
2.List<String> list= Collections.synchronizedList(new ArrayList<>());
3.List<String> 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核心。