写在前面:
之前学习Java基础时,对线程这块不是很清晰,当时上课几个基本的概念知识点学习了一下就匆匆过去了。后期,发现线程这块还是挺重要的。工作上,也看到有在应用。所以,趁着寒假过年在家,把线程这块给弥补弥补。老样子。尚硅谷,找视频。跟着学。做笔记。
链接地址:
http://www.gulixueyuan.com/course/96/reviews
源码百度网盘:
链接地址:https://pan.baidu.com/s/1YeyL43QOjr8gTsJso39J3A
提取码:08c7
ps:若要收费,可去B站搜索 尚硅谷。几乎都是免费且都有的。
好了,还是老样子。列知识点。层层进入。
JavaJUC简介
Volatile关键字-内存可见性
原子变量-CAS算法
ConcurrentHashMap锁分段机制
CountDownLatch闭锁
实现Callable接口
Lock同步锁
等待唤醒机制
Condition控制线程通信
线程八锁
RedWriteLock读写锁
线程池
线程调度
ForkJoinPool分支/合并框架 工作窃取
一、JavaJUC简介
JUC就是java.util .concurrent工具包的简称。这是一个处理线程的工具包,JDK 1.5开始出现的。
二、Volatile关键字-内存可见性
含义:
当变量被volatile关键字修饰时,会保证修改的值立即写入到主内存当中。其它线程需要访问时从主内存中读取新值。
public class VolatileDemo{
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
while (true){
if(threadDemo.isFlag()){
System.out.println("-------------------");
break;
}
}
}
}
class ThreadDemo implements Runnable{
private boolean flag = false;
public void run() {
try {
System.out.println(1);
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(2);
flag = true;
System.out.println(3);
System.out.println("flag" + isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
一个ThreadDemo类实现Runnable创建一个线程。它有一个成员变量flag为false,然后重写run方法,在run方法里面将flag改为true,同时还有几条输出语句+线程睡眠。然后就是main方法主线程去读取flag。如果flag为true,就会break掉while循环,否则就是死循环。按道理,下面那个线程将flag改为true了,主线程读取到的应该也是true,循环应该会结束。看看运行结果:
从图中可以看到,ThreadDemo类里输出的flag已经被更改为true。但main方法并没有结束。Main主线程在while死循环。进一步分析if(threadDemo.isFlag())这里还是false。分线程里flag为true。而主线程里为false。
内存可见性问题:当多个线程操作共享数据时,彼此不可见。
解决办法1
加synchronized关键字。
while (true){
synchronized (threadDemo){
if(threadDemo.isFlag()){
System.out.println("-------------------");
break;
}
}
}
加了锁,就可以让while循环每次都从主存中去读取数据,这样就能读取到true了。但是一加锁,每次只能有一个线程访问,当一个线程持有锁时,其他的就会阻塞,效率就非常低了。不想加锁,又要解决内存可见性问题,那么就可以使用volatile关键字。
解决办法2
private volatile boolean flag = false;
volatile关键字:当多个线程操作共享数据时,可以保证内存中的数据可见。用这个关键字修饰共享数据,就会及时的把线程缓存中的数据刷新到主存中去。
volatile和synchronized的区别:
volatile不具备互斥性(当一个线程持有锁时,其他线程进不来,这就是互斥性)
volatile不具备原子性
三、原子变量-CAS算法
什么是原子性?
public class AtomicDemo {
public static void main(String[] args) {
ThreadAtomicDemo threadAtomicDemo = new ThreadAtomicDemo();
for(int i=0;i<10;i++){
new Thread(threadAtomicDemo).start();
}
}
}
class ThreadAtomicDemo implements Runnable{
private int num = 0;
public int getNum(){
return num++;
}
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(getNum());
}
}
开启10个线程,去执行getNum()方法。获得每个num值。
得到的结果。多个重复相同的值。出现了重复数据。明显产生了多线程安全问题。
所谓原子性就是操作不可再细分,而i++操作分为读改写三步
分析。num++问题。分为三步。读、改、写
读:int temp = num
改:num = num +1
写:num = temp
读改写有三步。不能分开,读改执行完,准备写时。CPU被其它线程抢走。主存中还是0,但CPU中应该要为1。
解决办法1
Synchronized关键字
public synchronized int getNum(){
return num++;
}
每次都刷新下,让CPU去主存中读取最新的num。但一加锁,每次只能有一个线程访问,会造成线程堵塞。
解决办法2
Jdk1.5后java.util.concurrent.atomic包提供了原子类。AtomicInteger
AtomicInteger num = new AtomicInteger();
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(num.getAndIncrement());
}
原子变量
有volatile保证内存可见性。
用CAS算法保证原子性。
CAS算法
CAS算法是计算机硬件对并发操作共享数据的支持,CAS包含3个操作数:
内存值V
预估值A
更新值B
当且仅当 V==A时,V=B。否则,不做任何操作。
首先V是主存中的值0,然后预估值A也是0,因为此时还没有任何操作,这时V=A,所以进行自增,同时把主存中的值变为1。如果第二个线程读取到主存中的还是0也没关系,因为此时预估值已经变成1,V不等于A,所以不进行任何操作。
四、ConcurrentHashMap锁分段机制
JDK 1.5之后,在java.util.concurrent包中提供了多种并发容器类来改进同步容器类的性能。其中最主要的就是ConcurrentHashMap。
ConcurrentHashMap
ConcurrentHashMap就是一个线程安全的hash表。我们知道HashMap是线程不安全的,Hash Table加了锁,是线程安全的,因此它效率低。HashTable加锁就是将整个hash表锁起来,当有多个线程访问时,同一时间只能有一个线程访问,并行变成串行,因此效率低。所以JDK1.5后提供了ConcurrentHashMap,它采用了锁分段机制。
将ConcurrentHashMap分成了16段,每一段对应一个Hash表,且都有独立的锁。所以这样就可以每个线程访问一个Segment,就可以并行访问了,从而提高了效率。这就是锁分段。但是,java 8 又更新了,不再采用锁分段机制,也采用CAS算法了。
public class CopyOnWriteDemo {
public static void main(String[] args) {
CopyOnWriteArrayDemoThread copy = new CopyOnWriteArrayDemoThread();
for(int i=0;i<=10;i++){
new Thread(copy).start();
}
}
}
class CopyOnWriteArrayDemoThread implements Runnable{
private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
static {
list.add("1");
list.add("2");
}
@Override
public void run() {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
list.add("0");
}
}
}
10个线程并发访问这个集合,读取集合数据的同时再往集合中添加数据。运行这段代码会报错,并发修改异常。
将创建集合方式改成:
private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
复制并写入。每次生成新的,所以如果添加操作比较多的话,开销非常大,适合修改操作比较多的时候使用。
五、CountDownLatch闭锁
java.util.concurrent包中提供了多种并发容器类来改进同步容器的性能。ContDownLatch是一个同步辅助类,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行,这就叫闭锁。看下面代码:
public class CountDownLatchDemo {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(5);//有多少个线程这个参数就是几
LatchThread latchThread = new LatchThread(countDownLatch);
long start = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
new Thread(latchThread).start();
}
try {
countDownLatch.await();//这5个线程执行完之前先等
} catch (InterruptedException e) {
}
long end = System.currentTimeMillis();
System.out.println("耗时时间:"+(end-start));
}
}
class LatchThread implements Runnable{
private CountDownLatch countDownLatch;
public LatchThread(CountDownLatch countDownLatch){
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
synchronized (this){
try {
for(int i=0;i<=50;i++){
if(i%2==0){
System.out.println(i);
}
}
}finally {
countDownLatch.countDown();//每执行完一个就递减一个
}
}
}
}
开启了5个线程。每个线程去打印50以内的偶数。统计这5个线程执行完共花费了多少时间。若不用到CountDownLatch因为主线程与这5个线程也是同时执行的,可能那5个线程才执行到一半,主线程就已经输出“耗费时间为x秒”这句话了。所以要想计算这5个线程执行的时间,就得让主线程先等待,等5个分线程都执行完了才能执行主线程。这就要用到闭锁。
如上代码,主要就是用latch.countDown()和latch.await()实现闭锁。
CountDownLatch countDownLatch = new CountDownLatch(5)
countDownLatch.await();
countDownLatch.countDown();
六、实现Callable接口
创建线程的4种方式?
①继承Thread类,重写run方法。子类对象调用start()方法。
②实现Runnable接口,重写run方法。Thread(Runnable r)传递Runnable接口实现类。
③实现Callable接口
④线程池。Executors线程池工厂。
线程对象调用start()和run()方法有什么区别?
线程对象调用start()代表开启线程并让JVM调用run()方法在开启的线程中执行。
线程对象调用run()方法不开启线程,仅仅是普通对象调用方法。
继承Thread类 VS 实现Runable接口?
用继承的话,线程对象和线程任务紧密耦合在一起,而实现接口的话,可以根据Thread构造方法自定义需要实现的线程任务。且将线程对象和线程任务分离出来。
线程有哪几种运行状态?
新建、运行、休眠、睡眠、等待、死亡、受阻塞
线程池作用?
并发线程数量很多,每个线程都执行一下就结束了。频繁创建影响效率,使用线程池可以复用,执行完并不被销毁,继续执行其它线程。
实现Runnable接口VS实现Callable接口的区别?
实现Runnable接口 没有返回值 不带泛型 不能往上抛异常
实现Callable接口 有返回值 带泛型 运行抛异常
解决多线程安全问题有哪几种方法?
①synchronized代码块
②synchronized方法
③Lock前置锁
先看代码
public class CallableDemo {
public static void main(String[] args) {
ThreadCallableDemo threadCallableDemo = new ThreadCallableDemo();
//执行callable方式,需要FutureTask实现类的支持,用来接收运算结果
FutureTask<Integer> futureTask = new FutureTask<>(threadCallableDemo);
new Thread(futureTask).start();
try {
//当上面的线程执行完后,才会打印结果。跟闭锁一样。所有futureTask也可以用于闭锁
Integer sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class ThreadCallableDemo implements Callable<Integer>{
private int sum = 0;
@Override
public Integer call() throws Exception {
for(int i=0;i<=100;i+=2){
sum+=i;
System.out.println(i);
}
return sum;
}
}
现在Callable接口和实现Runable接口的区别就是,Callable带泛型,其call方法有返回值。使用的时候,需要用FutureTask来接收返回值。而且它也要等到线程执行完调用get方法才会执行,也可以用于闭锁操作。
七、Lock同步锁
引入:
在JDK1.5之前,解决多线程安全问题(通过线程休眠出现的安全问题)有两种方式(sychronized隐式锁):
同步代码块
同步方法
在JDK1.5之后,出现了更加灵活的方式(Lock显式锁):
同步锁
Lock需要通过lock()方法上锁,通过unlock()方法释放锁。为了保证锁能释放,所有unlock方法一般放在finally中去执行。
之前一直采用synchronized加锁的方式来解决多线程安全问题。
典型例子:售票。
public class LockDemo {
public static void main(String[] args) {
LockThread lockThread = new LockThread();
new Thread(lockThread,"1号窗口").start();
new Thread(lockThread,"2号窗口").start();
new Thread(lockThread,"3号窗口").start();
}
}
class LockThread implements Runnable{
private int ticket = 50;
@Override
public void run() {
while (true){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"完成售票,还剩余"+ --ticket);
}else{
break;
}
}
}
}
可以看到。不加锁,多个线程来抢,票数出现了问题。
解决办法1
加synchronized关键字。
@Override
public void run() {
while (true){
synchronized (this){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"完成售票,还剩余"+ --ticket);
}else {
break;
}
}
}
}
解决办法2
Lock锁。Lock lock =new ReentrantLock();
直接创建lock对象,然后用lock()方法上锁,最后用unlock()方法释放锁即可。
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try {
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"完成售票,还剩余"+ --ticket);
}else{
break;
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
八、等待唤醒机制
生产消费模式是等待唤醒机制的一个经典案例。
采用同步锁解决
public class ConditionDemo {
public static void main(String[] args) {
Shop shop = new Shop();
Producter producter = new Producter(shop);
Consumer consumer = new Consumer(shop);
new Thread(producter,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
}
//商店
class Shop{
private int product = 0;
//卖货
public synchronized void sale(){
if(product<=0){
System.out.println("产品缺货!");
}else{
System.out.println(Thread.currentThread().getName()+"卖货成功,还剩余"+ --product);
}
}
//进货
public synchronized void get(){
if(product>=10){
System.out.println("产品已满!");
}else{
System.out.println(Thread.currentThread().getName()+"进货成功,库存有"+ ++product);
}
}
}
//生产者
class Producter implements Runnable{
private Shop shop;
public Producter(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
//进行生产
for (int i = 0; i < 20; i++) {
shop.get();
}
}
}
//消费者
class Consumer implements Runnable{
private Shop shop;
public Consumer(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
//进行消费
for (int i = 0; i < 20; i++) {
shop.sale();
}
}
}
可以看到。上面运行的代码,产品已满了,没有通知消费者可以进行消费。产品缺货了,没有通知生产者可以进行生产。不是我们想要产品满了通知消费者消费,产品缺货了通知生产者生产的结果。
用等待唤醒机制改进:
*Obeject类下常用的方法?
toEquals() toSring() wait() notify() getClass() hashCode()
//卖货
public synchronized void sale(){
if(product<=0){
System.out.println("产品缺货!");
try {
this.wait();//缺货就等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName()+"卖货成功,还剩余"+ --product);
this.notifyAll();//不缺货就可以卖
}
}
//进货
public synchronized void get(){
if(product>=10){
System.out.println("产品已满!");
try {
this.wait();//满货就等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName()+"进货成功,库存有"+ ++product);
this.notifyAll();//没满就可以进货
}
}
}
这样就不会出现上述问题了。没有的时候就生产,生产满了就通知消费,消费完了再通知生产。但是这样还是有点问题,将上述代码做如下改动:
if(product>=1){
System.out.println("产品已满!");
@Override
public void run() {
//进行生产
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
shop.get();
}
}
就做这两处修改,再次运行,发现虽然结果没问题,但是程序却一直没停下来。出现这种情况是因为有一个线程在等待,而另一个线程没有执行机会了,唤醒不了这个等待的线程了,所以程序就无法结束。
解决办法就是把get和sell方法里面的else去掉,不要用else包起来。
但如果再增加一对消费者和生产者。
new Thread(producter,"生产者AA").start();
new Thread(consumer,"消费者BB").start();
发现出现了负数。
分析:一个消费者线程抢到执行权,发现product是0,就等待,这个时候,另一个消费者又抢到了执行权,product是0,还是等待,此时两个消费者线程在同一处等待。然后当生产者生产了一个product后,就会唤醒两个消费者,发现product是1,同时消费,结果就出现了0和-1。这就是虚假唤醒
解决办法就是把if判断改成while
while(product<=0){
while(product>=1){
这样就可以了。
采用Lock锁解决
class Shop2 {
private int product = 0;//共享数据
private Lock lock = new ReentrantLock();//创建锁对象
private Condition condition = lock.newCondition();//获取condition实例
//卖货
public void sale(){
lock.lock();
try {
while(product<=0){
System.out.println("产品缺货!");
try {
/*this.wait();*/
condition.await();//缺货就等待
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName()+"卖货成功,还剩余"+ --product);
/*this.notifyAll();*/
condition.signalAll();//不缺货就可以卖
}finally {
lock.unlock();
}
}
//进货
public void get(){
lock.lock();
try {
while(product>=1){
System.out.println("产品已满!");
try {
/*this.wait();*/
condition.await();//满货就等待
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName()+"进货成功,库存有"+ ++product);
/*this.notifyAll();*/
condition.signalAll();//没满就可以进货
}finally {
lock.unlock();
}
}
}
使用lock同步锁,就不需要sychronized关键字了,需要创建lock对象和condition实例。condition的await()方法、signal()方法和signalAll()方法分别与wait()方法、notify()方法和notifyAll()方法对应。
九、Condition控制线程通信
编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,
每个线程将自己的 ID 在屏幕上打印10 遍,要求输出的结果必须按顺序显示。
如:ABCABCABC…… 依次递归
线程本来是抢占式进行的,要按序交替,所以必须实现线程通信,
那就要用到等待唤醒。可以使用同步方法,也可以用同步锁。
public class ABCLoopDemo {
public static void main(String[] args) {
Print print = new Print();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
print.printA(i);
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
print.printB(i);
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
print.printC(i);
System.out.println("========================");
}
}
},"C").start();
}
}
class Print{
private int num = 1;//当前正在执行的线程的标记
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA(int totalLoop){
lock.lock();//采用Lock锁替代synchronized解决多线程安全问题
try {
if(num != 1){//判断
condition1.await();
}
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+num+"\t"+totalLoop+"次");
}
num = 2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//执行完成 必须在finally里解锁
}
}
public void printB(int totalLoop){
lock.lock();//采用Lock锁替代synchronized解决多线程安全问题
try {
if(num != 2){//判断
condition2.await();
}
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+num+"\t"+totalLoop+"次");
}
num = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//执行完成 必须在finally里解锁
}
}
public void printC(int totalLoop){
lock.lock();//采用Lock锁替代synchronized解决多线程安全问题
try {
if(num != 3){//判断
condition3.await();
}
for (int i = 1; i <= 1; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+num+"\t"+totalLoop+"次");
}
num = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//执行完成 必须在finally里解锁
}
}
}
十、线程八锁
同步代码块执行原理?
①线程遇到同步代码块后,线程判断同步锁还有没有。
②有,获取锁,进入同步中去执行。执行完毕,出同步块,把锁对象还回去
②没有,没有锁的线程不能进入同步代码块去执行,被阻挡在同步块外面
*加同步后,线程进同步代码块判断锁,出同步后,释放锁。速度下降。容易阻塞。
*某一时刻内,无论有多少个方法,只能有一个线程持有锁。
同步方法有锁吗?
有,同步方法中的锁对象是本类对象的引用。This
如果方法是静态的呢?
有,静态方法中,同步锁是本类对象.class
十一、RedWriteLock读写锁
我们在读数据的时候,可以多个线程同时读,不会出现问题,但是写数据的时候,如果多个线程同时写数据,那么到底是写入哪个线程的数据呢?所以,如果有两个线程,写写/读写需要互斥,读读不需要互斥。这个时候可以用读写锁。看例子:
ReadWriteLock rw = new ReentrantReadWriteLock();
rw.readLock().lock();
rw.readLock().unlock();
rw.writeLock().lock();
rw.writeLock().unlock();
public class ReadWriteLockDemo {
public static void main(String[] args) {
ReadWriteLockThread readWriteLockThread = new ReadWriteLockThread();
new Thread(new Runnable() {
@Override
public void run() {
readWriteLockThread.set((int)(Math.random()*100));
}
},"write1").start();
for (int i = 1; i <= 10 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
readWriteLockThread.get();
}
}).start();
}
new Thread(new Runnable() {
@Override
public void run() {
readWriteLockThread.set2((int)(Math.random()*100));
}
},"write2").start();
}
}
class ReadWriteLockThread{
private int num = 0;
private ReadWriteLock rw = new ReentrantReadWriteLock();
//读
public void get(){
rw.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+":"+num);
}finally {
rw.readLock().unlock();
}
}
//写1
public void set(int num){
rw.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+":"+num);
this.num = num;
}finally {
rw.writeLock().unlock();
}
}
//写2
public void set2(int num){
rw.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+":"+num);
this.num = num;
}finally {
rw.writeLock().unlock();
}
}
}
这个就是读写锁的用法。上面的代码实现了2个线程写,10个线程同时读的操作。
十二、线程池
线程池作用?
并发线程数量很多,每个线程都执行一下就结束了。频繁创建影响效率,使用线程池可以复用,执行完并不被销毁,继续执行其它线程。
public class ThreadPoolExecutorDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.创建线程池
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<Integer>> list = new ArrayList<>();
for (int i = 0; i <= 10; i++) {
//2.为线程池中的线程分配任务
Future<Integer> futureTask = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum+=i;
}
return sum;
}
});
list.add(futureTask);
}
for (Future<Integer> integerFuture : list) {
System.out.println(integerFuture.get());
}
//3.关闭线程池
executorService.shutdown();
}
}
通过Executors线程池工具类创建了一个可缓存的线程池。向线程池中调用了一个Callable线程对象,开启10个线程执行累加100操作。最后shutdown关闭线程。
十三、线程调度
ScheduledExecutorService. Schedule();
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public class ScheduledExecutorServiceDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.创建线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
for (int i = 1; i <= 5; i++) {
//2.为线程池中的线程分配任务.schedule
//延迟3秒 执行
Future<Integer> future = scheduledExecutorService.schedule(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(100);
System.out.println(Thread.currentThread().getName()+":"+num);
return num;
}
},3, TimeUnit.SECONDS);
System.out.println(future.get());
}
//3.关闭线程池
scheduledExecutorService.shutdown();
}
}
十四、ForkJoinPool分支/合并框架 工作窃取
ForkJoinPool forkJoinPool = new ForkJoinPool();
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
ForkJoinTask<Long> submit = forkJoinPool.submit(new CopyForkJoinPoolDemo(0L,100000000L));
public class ForkJoinPoolDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Instant start = Instant.now();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> submit = forkJoinPool.submit(new CopyForkJoinPoolDemo(0L,100000000L));
System.out.println(submit.invoke());
//System.out.println(submit.get());
Instant end = Instant.now();
System.out.println("耗费时间为:"+ Duration.between(start,end).toMillis());
forkJoinPool.shutdown();
}
@Test
public void test1(){
Instant start = Instant.now();
long sum = 0L;
for (long i = 0L; i <= 100000000L; i++) {
sum+=i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("耗费时间为:"+ Duration.between(start,end).toMillis());
}
}
class CopyForkJoinPoolDemo extends RecursiveTask<Long>{// extends递归任务 有返回值
private static final long serialVersionUID = -9173191152935453018L;
private Long start;
private Long end;
private static final long criticalValue = 1000000L; //临界值
public CopyForkJoinPoolDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
//计算
@Override
protected Long compute() {
long length = end-start;
if(length<=criticalValue){
long num = 0;
for (long i=start; i<=end; i++) {
num+=i;
}
return num;
}else{
long middle = (start+end) / 2;
CopyForkJoinPoolDemo left = new CopyForkJoinPoolDemo(start,middle);
left.fork();//进行拆分
CopyForkJoinPoolDemo right = new CopyForkJoinPoolDemo(middle+1,end);
right.fork();//进行拆分
return left.join()+right.join();//进行合并
}
}
}