尚硅谷JUC源码学习笔记总结

文章是学习 尚硅谷JUC源码讲授实战教程完整版(java juc线程精讲) 时做的笔记与总结

1. JUC 简介

JUC 是指 java.util.concurrent , 在jdk的rt.jar 中 1.5版本就以及引入了 , rj.jar属于bootstrap classloader加载的内存,通过双亲委派机制去加载

JUC下面有多个工具类,如并发集合类:ConcurrentHashMap CopyOnWriteArrayList , 锁工具: 闭锁 CountDownLatch, 循环栅栏 CyclicBarrier 信号量 Semaphore 等,以及两个子包分别是atomiclocks

atomiclocks

2. volatile 关键字特性

2.1 内存可见性,不保证原子性

在同一进程的不同线程访问进程共享数据时,会将共享变量加载到当前cpu缓存中,因此不同线程对共享数据的修改不能及时在其他线程读取中体现,内存可见性就能够在写的时候强制刷新到主存中,这样就避免了其他线程的脏读

代码案例

代码本意:在线程run中 更改 状态变量之后打印,main线程中循环读状态变量,如果为真打印停止

bug原因:当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。因此,在run线程中状态变量已经true , 然而 main线程中仍然是 之前读到的false 所以也就陷入了死循环中


package jdk.learn.juc;

import lombok.Data;

import java.util.concurrent.TimeUnit;

public class VolatileTest {


	public static void main(String[] args) {
		Run run = new Run();
		new Thread(run).start();
		while (true) {
			if (run.isStopped()) {
				System.out.println("主-监听停止");
				break;
			}
		}
	}


	@Data
	static class Run implements Runnable {

//		private volatile boolean stopped;
		private boolean stopped;


		@Override
		public void run() {
			try {
				TimeUnit.SECONDS.sleep(2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			stopped = true;
			System.out.println("run is stopped");
		}
	}
}


那么有什么解决办法?让不同线程之间的变量赋值能够实时可见

  1. 使用volatile修饰状态变量
  2. 使用synchronized代码块包裹读状态变量代码,它也可以刷新缓存(它的正确使用应该在于同步)

这里是解决可见性,那么更优先选择是volatile
在这里插入图片描述

对于操作系统硬件层面内存知识可以看这篇JMM

2.2 防止指令重排

在高级语言中我们也追求性能最优化,那么在计算机的底层,cpu层面上也是追求性能的最优化,在计算机设计时也在速度较慢的内存与速度超快的cpu中间加高速缓存来粘合中间的速度不匹配,逻辑层面则使用了指令重排对输入的代码经行乱排序之后错位执行的处理器优化策略,让cpu的运算单元能够充分利用

重排序需要遵守一定规则:

  • 重排序操作不会对存在数据依赖关系的操作进行重排序。

比如:a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运
行时这两个操作不会被重排序

  • 重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变

比如:a=1;b=2;c=a+b这三个操作,第一步(a=1)和第二步(b=2)由于不存在数据依赖关系, 所以可能会发生重排序,但是c=a+b这个操作是不会被重排序的,因为需要保证最终的结果一定是c=a+b=3。

重排序在单线程下一定能保证结果的正确性,但是在多线程环境下,可能发生重排序,影响结果,下例中的1和2由于不存在数据依赖关系,则有可能会被重排序,先执行status=true再执行a=2。而此时线程B会顺利到达4处,而线程A中a=2这个操作还未被执行,所以b=a+1的结果也有可能依然等于2。

使用volatile关键字修饰共享变量便可以禁止这种重排序。若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序,volatile禁止指令重排序也有一些规则:

  • 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
  • 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

即执行到volatile变量时,其前面的所有语句都执行完,后面所有语句都未执行。且前面语句的结果对volatile变 量及其后面语句可见。

2.3 单例中双重校验使用原理

Java中单例模式的7种写法 附注释和源码

3. Atomic 原子类

在并发操作中有经典的 i++ 问题,一条命令对于CPU而言,其实包含了三步操作,读i的值 i+1 写入i=i+1
atomic
示例代码



import java.util.concurrent.TimeUnit;

public class AtomicTest {

	public static void main(String[] args) {
		AtomicCount count = new AtomicCount();
		for (int i = 0; i < 10; i++) {
			new Thread(count).start();
		}
	}

}

class AtomicCount implements Runnable {
	private int count = 0;

	@Override
	public void run() {
		try {
			TimeUnit.MILLISECONDS.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(count++);
	}
}

**输出 **

3
8
6
0
7
2
0
5
4
1

可以在 AtomicCount.count 加上volatile修饰 验证 无法保证原子性

这里是为了引出Atomic原子类,将count 改为AtomicInteger ++ 操作换成getAndIncrement 其底层使用的就是 sun.misc.Unsafe 的 CAS操作

3.1 CAS 算法

CAS 算法是 CompareAndSwap , 如 : 从某一内存上取值V,和预期值A进行比较,如果内存值V和预期值A的结果相等,那么我们就把新值B更新到内存 .

**并不包含不等一直循环操作 , 一直循环这是自定义的业务逻辑 , 如 sun.misc.Unsafe#getAndAddInt **

CAS 就是通过这样的过程避免了加锁的开销,并且实现了多线程安全问题,同时在学了上一节可以知道,还有一点,仅有CAS 还不能保证我们每次取值V 都是最新的值,因此需要使用 volatile保证内存可见性

在这里插入图片描述

同时如AtomicInteger 类中 getAndIncrement incrementAndGet 等操作都是使用 sun.misc.Unsafe#getAndAddInt 因此 包含自旋操作:未获得预期值替换则会一直循环CAS 形成自旋

自旋锁在竞争激烈的情况下性能反而不高 , 因为大多数都在无效循环判断预期值

在这里插入图片描述
总结

  • CAS 其实可以算乐观锁 , 与预期值一致才替换
  • 长时间自旋非常消耗资源,不适合竞争激烈操作
  • 不能保证代码块的原子性,只能保证单个变量的原子性

4. CurrentHashMap 和 CopyOnWriteArrayList

这里视频中讲解的是jdk1.8以前的实现,采用的是分段锁 , 而1.8之后使用 synchronized node 对象锁+cas 保证容器的并发安全问题

分段锁大致理念就是 把 hashMap中的桶元素 将一些桶元素归类于一段 并发操作时 给这一段加锁 ,从而保证并发安全

详细比较可以看这篇文章

5. 闭锁 CountDownLatch

在Java 5.0 引入的同步辅助类,可以在其他线程未执行完之前一个或多个线程等待 ,闭锁可以延迟线程的执行进度,确保某些活动在其他活动完成才执行:

  • 保证某个计算任务在所有资源初始化后才能执行
  • 确保某个服务在所有其他服务都启动之后才启动
  • 等某个操作的所有参与者都就绪再继续执行

示例

程序 application 一定会在 数据源 和 属性文件加载完毕后运行



import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {

	static CountDownLatch countDownLatch = new CountDownLatch(2);

	static Boolean Datasource = false;
	static Boolean Properties = false;

	public static void main(String[] args) {

		new Thread(() -> {
			try {
				countDownLatch.await();
				System.out.println("application is running ...");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();

		new Thread(() -> {
			Datasource = true;
			System.out.println("Datasource 就绪");
			countDownLatch.countDown();
		}).start();
		new Thread(() -> {
			Properties = true;
			System.out.println("Properties 就绪");
			countDownLatch.countDown();
		}).start();

	}


}

5.1 闭锁关键代码详解

闭锁实现 其实是依赖内部类 Sync , 闭锁构造器实际声明一个Sync 对象

在这里插入图片描述

private static final class Sync extends AbstractQueuedSynchronizer

5.2 countDown 方法

直接调用sync 的方法

在这里插入图片描述
开始尝试释放闭锁判断
在这里插入图片描述
实际是个自旋CAS 修改state 数量控制, 成功后 执行doReleaseShared
在这里插入图片描述
这一步也是个自旋 ,循环判断状态 , 才是真实释放闭锁
内部维护了一个 Head 双向链表 , 遍历链表判断状态量 CANCELLED SIGNAL
在这里插入图片描述

5.3 await 方法

sync.acquireSharedInterruptibly(1);
在这里插入图片描述

6. 四种执行线程方式

  • Runable
  • Thread
  • Callable
  • ExecutorService

Callable 方法 在FutureTask 接收时会阻塞,因此,FutureTask 也可以实现等同闭锁操作

注意 : 在多个任务时,不要第一时间调用get方法,因为会形成阻塞 ,正确方式应该是 放到集合中当完成时调用get方法获得返回值



import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class CallableTest {


	public static void main(String[] args) throws ExecutionException, InterruptedException {
		Call call = new Call();
		FutureTask<Double> task = new FutureTask<>(call);
		new Thread(task).start();
		System.out.println("线程执行====");
		Double res = task.get();
		System.out.println(res);
	}


}

class Call implements Callable<Double> {

	@Override
	public Double call() throws Exception {
		TimeUnit.SECONDS.sleep(5);
		return Math.random();
	}
}


7. 同步锁 Lock

解决代码并发安全问题之前都是采用 同步方法同步代码块 , 在1.5之后可以采用 Lock 的方式更加灵活的完成加锁解锁,并且也有更高的性能

示例-错误代码



import java.util.concurrent.TimeUnit;

public class UnLockTest {


	public static void main(String[] args) {

		Unlock unlock = new Unlock();

		new Thread(unlock, "一号窗口").start();
		new Thread(unlock, "二号窗口").start();
		new Thread(unlock, "三号窗口").start();
	}
}

class Unlock implements Runnable {
	private volatile int ticket = 100;

	@Override
	public void run() {
		while (ticket > 0) {
			try {
				TimeUnit.MILLISECONDS.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + ": 完成购票,余票" + --ticket);
		}
	}
}

由于没有加锁,所以会线程安全问题 , 如果加上Lock 方法

正确代码示例




import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class UnLockTest {


	public static void main(String[] args) {

		Unlock unlock = new Unlock();

		new Thread(unlock, "一号窗口").start();
		new Thread(unlock, "二号窗口").start();
		new Thread(unlock, "三号窗口").start();
	}
}

class Unlock implements Runnable {
	private volatile int ticket = 100;

	private static Lock lock = new ReentrantLock();

	@Override
	public void run() {
		while (true) {
			lock.lock();
			if (ticket <= 0) {
				break;
			}
			try {
				TimeUnit.MILLISECONDS.sleep(100);
				System.out.println(Thread.currentThread().getName() + ": 完成购票,余票" + --ticket);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
		}
	}
}


7.1 生产消费,等待唤醒

使用lock 完成对应锁机制的等待唤醒,而其中经典的方式是生产消费者模式

7.1.1 生产者消费者-无等待唤醒

这种会出现 , 无库存不等待重复消费 , 库存满 不等待重复生产,应该在对应地方等待


import java.util.concurrent.TimeUnit;

public class CPTest {


	public static void main(String[] args) {
		Store store = new Store();
		new Thread(new Producter(store), "生产者a").start();
		new Thread(new Customer(store), "消费者b").start();
	}

}

class Producter implements Runnable {
	Store store;

	public Producter(Store store) {
		this.store = store;
	}

	@Override
	public void run() {
		for (int i = 0; i < 2000; i++) {
			store.setProduct();
//			try {
//				TimeUnit.MILLISECONDS.sleep(200);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
		}

	}
}

class Store {

	private int product = 0;

	public synchronized void getProduct() {
		if (product <= 0) {
			System.out.println("无库存");
		} else {
			System.out.println(Thread.currentThread().getName() + ":消费" + --product);
		}
	}

	public synchronized void setProduct() {
		if (product >= 20) {
			System.out.println("库存已满");
		} else {
			System.out.println(Thread.currentThread().getName() + ":生产" + ++product);
		}
	}
}

class Customer implements Runnable {

	private Store store;

	public Customer(Store store) {
		this.store = store;
	}

	@Override
	public void run() {
		for (int i = 0; i < 2000; i++) {
			store.getProduct();
//			try {
//				TimeUnit.MILLISECONDS.sleep(200);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
		}
	}
}

7.1.2 生产者消费者-等待唤醒

需要注意的点 :

  • 不能放入else 分支
  • wait 需要在循环中,防止虚假唤醒



import java.util.concurrent.TimeUnit;

public class CPTest {


	public static void main(String[] args) {
		Store store = new Store();
		new Thread(new Producter(store), "生产者a").start();
		new Thread(new Customer(store), "消费者b").start();
	}

}

class Producter implements Runnable {
	Store store;

	public Producter(Store store) {
		this.store = store;
	}

	@Override
	public void run() {
		for (int i = 0; i < 2000; i++) {
			store.setProduct();
//			try {
//				TimeUnit.MILLISECONDS.sleep(200);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
		}

	}
}

class Store {

	private int product = 0;

	public synchronized void getProduct() {
		while (product <= 0) {
			System.out.println("无库存");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName() + ":消费" + --product);
		this.notifyAll();
	}

	public synchronized void setProduct() {
		// 需要放入循环中 不然会出现
		while (product >= 20) {
			System.out.println("库存已满");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName() + ":生产" + ++product);
		this.notifyAll();
	}
}

class Customer implements Runnable {

	private Store store;

	public Customer(Store store) {
		this.store = store;
	}

	@Override
	public void run() {
		for (int i = 0; i < 2000; i++) {
			store.getProduct();
//			try {
//				TimeUnit.MILLISECONDS.sleep(200);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
		}
	}
}


7.2 Lock 版生产消费者

Lock中可以使用Lock 代替 synchronized , 而使用 Condition 实现 唤醒等待





import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CPLockTest {


	public static void main(String[] args) {
		Store store = new Store();
		new Thread(new Producter(store), "生产者a").start();
		new Thread(new Customer(store), "消费者b").start();
		new Thread(new Producter(store), "生产者c").start();
		new Thread(new Customer(store), "消费者d").start();
	}

	static class Producter implements Runnable {
		Store store;

		public Producter(Store store) {
			this.store = store;
		}

		@Override
		public void run() {
			for (int i = 0; i < 2000; i++) {
				store.setProduct();
//			try {
//				TimeUnit.MILLISECONDS.sleep(200);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
			}

		}
	}

	static class Store {
		private static Lock lock = new ReentrantLock();
		private static Condition condition = lock.newCondition();
		private int product = 0;

		public void getProduct() {
			lock.lock();
			while (product <= 0) {
				System.out.println("无库存");
				try {
					condition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName() + ":消费" + --product);
			condition.signalAll();
			lock.unlock();
		}

		public void setProduct() {
			lock.lock();
			// 需要放入循环中 不然会出现
			while (product >= 20) {
				System.out.println("库存已满");
				try {
					condition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName() + ":生产" + ++product);
			condition.signalAll();
			lock.unlock();
		}
	}

	static class Customer implements Runnable {

		private Store store;

		public Customer(Store store) {
			this.store = store;
		}

		@Override
		public void run() {
			for (int i = 0; i < 2000; i++) {
				store.getProduct();
//			try {
//				TimeUnit.MILLISECONDS.sleep(200);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
			}
		}
	}
}

7.3 线程按序交替

编写一个程序 开启3个线程 线程名 分别为A B C,在屏幕上面打印10遍 要求按序显示,如:ABCABCABC 依次显示


import java.io.Serializable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SerializableThreadPrintTest {

	static String A = "A";
	static String B = "B";
	static String C = "C";

	public static void main(String[] args) {
		SerializableThreadPrint exec = new SerializableThreadPrint();
		Runnable run = () -> {
			for (int i = 0; i < 10; i++) {
				exec.loop();
			}
		};
		new Thread(run, A).start();

		new Thread(run, B).start();

		new Thread(run, C).start();
	}

	static class SerializableThreadPrint implements Serializable {

		private static String next = A;
		private static Lock lock = new ReentrantLock();
		private static Condition a = lock.newCondition();
		private static Condition b = lock.newCondition();
		private static Condition c = lock.newCondition();

		private void loop() {
			lock.lock();
			try {
				inprint();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
		}

		private Condition getCurrent() {
			if (A.equals(Thread.currentThread().getName())) {
				return a;
			}
			if (B.equals(Thread.currentThread().getName())) {
				return b;
			}
			if (C.equals(Thread.currentThread().getName())) {
				return c;
			}
			return null;
		}

		private void inprint() throws InterruptedException {
			if (!next.equals(Thread.currentThread().getName())) {
				getCurrent().await();
			}
			System.out.print(Thread.currentThread().getName());
			notifyNext();
		}

		private void notifyNext() {
			if (A.equals(Thread.currentThread().getName())) {
				next = B;
				b.signal();
			}
			if (B.equals(Thread.currentThread().getName())) {
				next = C;
				c.signal();
			}
			if (C.equals(Thread.currentThread().getName())) {
				next = A;
				a.signal();
			}
		}
	}
}

9. 读写锁

读写锁是为了区分 读写 操作 , 因为在实际场景中 读是不需要互斥,而写是需要互斥的,程序设计过程中,锁的粒度更小性能越好,这样的场景下,如果读写区分锁,那么锁的粒度是更小的



import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWirteLockTest {

	public static void main(String[] args) {

		RW rw = new RW();

		Runnable r = () -> {
			for (int i = 0; i < 100; i++) {
				rw.setNum((int) (Math.random() * 100));
				rw.getNum();
			}
		};
		new Thread(r, "A").start();
		new Thread(r, "B").start();
		new Thread(r, "C").start();


	}

	static class RW {
		private int num = 0;
		static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

		public int getNum() {
			readWriteLock.readLock().lock();
			System.out.println(Thread.currentThread().getName() + ":读" + num);
			readWriteLock.readLock().unlock();
			return num;
		}

		public void setNum(int num) {
			readWriteLock.writeLock().lock();
			System.out.println(Thread.currentThread().getName() + ":写" + num);
			this.num = num;
			readWriteLock.writeLock().unlock();
		}
	}
}


10. 线程八锁

线程八锁代表线程锁的八个案例,针对不同方法,不同实例 产生的锁的情况

  • 1.非静态方法的锁 this, 静态方法的锁 对应的Class实例
  • 2.某一个时刻内,只能由一个线程持有锁,无论几个方法
  • 1.两个同步方法,两个线程,打印 one two
  • 2.新增Thread.sleep()给getOne 打印 one two
  • 3.新增普通方法getThree,打印 three one two
  • 4.注释getThree,number2.getTwo,打印 two one
  • 5.修改getOne为静态同步方法,改为number.getTwo,打印 two one
  • 6.两个方法都为静态同步方法,一个number对象,打印 one two
  • 7.getOne为静态同步方法,getTwo为同步方法,改为number2.getTwo,打印two one
  • 8.两个静态同步方法,两个number对象,打印 one two

其中案例就是讲诉两个知识点:

  • 静态方法 synchronized 与 实例方法 synchronized 锁的对象区别
  • 时间片获取与sleep 休眠 是否释放锁

静态方法 使用的是.class 如 synchronized (LockMethod.class)
实例方法 使用的是 实例对象的this synchronized (this) 这里的对象就是当前调用这个方法的实例

因此,如果是实例方法,多个实例调用同一个同步实例方法,因为锁对象不同 不会产生竞争,也就不用等待
如果是静态方法,锁对象为.class 所以就会产生锁等待

同时sleep 不会释放锁 因此会有先后



import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class LockMethodTest {

	public static void main(String[] args) {

		LockMethod lockMethod = new LockMethod();
		LockMethod lockMethod2 = new LockMethod();

		// 1.先获得锁的对象 先执行 其他等待
//		new Thread(() -> {
//			lockMethod.static_sync();
//		}).start();
//		new Thread(() -> {
//			lockMethod.static_sync2();
//		}).start();

		// 2. 静态锁住了 , 实例方法能不能拿到锁
//		CountDownLatch countDownLatch = new CountDownLatch(2);
//		new Thread(() -> {
//			lockMethod.simple_sync_method();
//		}).start();
		new Thread(() -> {
			lockMethod.simple_sync_sleep3_method();
		}).start();
		new Thread(() -> {
//			countDownLatch.countDown();
//			try {
//				countDownLatch.await();
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
			lockMethod2.simple_sync_method();
		}).start();
	}

	static class LockMethod {

		private static synchronized void static_sync() {
			System.out.println("static_sync");
		}

		private static synchronized void static_sync2() {
			System.out.println("static_sync2");
		}

		private static synchronized void static_sleep3_sync() {
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("static_sleep3_sync");
		}

		private static void static_method() {
			System.out.println("static_method");
		}

		private void simple_method() {
			System.out.println("simple_method");
		}

		private synchronized void simple_sync_method() {

			System.out.println("simple_sync_method");
		}

		private synchronized void simple_sync_method2() {
			System.out.println("simple_sync_method2");
		}

		private synchronized void simple_sync_sleep3_method() {
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("simple_sync_sleep3_method");
		}
	}
}

11. 线程池

线程池,是为了减少线程创建销毁的额外开销,与其他池化技术相同都是提前创建好对应线程,等待需要执行的任务提交直接执行,执行完毕后放入池中等待下次执行.

11.1 线程池 相关对象

在这里插入图片描述
同时还有对应的工具类 java.util.concurrent.Executors 可以创建常规场景中的线程池,但是不推荐在生产环境中使用,如Executors.newCachedThreadPool() 会创建无限大小的线程池,生产环境中很容易造成OOM

11.2 使用示例



import java.util.concurrent.*;

public class ExecutorServiceTest {

	public static void main(String[] args) throws InterruptedException {
		ExecutorService executorService = new ThreadPoolExecutor(4, 4, 60, TimeUnit.MINUTES
				, new ArrayBlockingQueue<>(100), Executors.defaultThreadFactory()
				, new ThreadPoolExecutor.AbortPolicy());
		Future<?> submit = executorService.submit(() -> {
			System.out.println("1111111111");
		});
		executorService.execute(() -> {
			System.out.println("1111111111");
		});
		executorService.awaitTermination(10, TimeUnit.DAYS);
		executorService.shutdown();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

		scheduledExecutorService.scheduleAtFixedRate(()->{
			System.out.println("执行一次");
		}, 2, 2, TimeUnit.SECONDS);
	}



}

详细参数可以看这篇总结 https://blog.csdn.net/qq_35530042/article/details/109001536

11.3 Fork/Join 框架

在这里插入图片描述

  • ForkJoinPool 不是为了替代 ExecutorService,而是它的补充,在某些应用场景下性能比ExecutorService 更好。
  • ForkJoinPool 主要用于实现“分而治之”的算法,特别是分治之后递归调用的函数,例如 quick sort 等。
  • ForkJoinPool 最适合的是计算密集型的任务,并且任务量大的情况,任务量小反而比普通线程池慢 , 如果存在 I/O,线程间同步,sleep() 等会造成线程长时间阻塞的情况时,最好配合使用 ManagedBlocker。
  • 同时它也是在jdk8中并行流使用的池

最后

之前很多知识都是零散的学习过,或者学过又忘记了,过一遍视频总结一些知识点,并且在基础上找网上资源和之前的总结做了一些补充,感觉对这些知识点更清晰了,共勉!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木秀林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值