多线程是java并发的基础,我们先来学习一下吧:

首先,让我们来起一个多线程,看看

  1. public class HelloWorld {
  2. public static void main(String[] args) throws Exception {
  3. // lambda 写法
  4. Thread t = new Thread(() -> {
  5. System.out.println("thread start");
  6. try {
  7. Thread.sleep(100); // 模拟IO操作,让线程等100毫秒
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. } finally {
  11. System.out.println("thread end");
  12. }
  13. });
  14. t.start();
  15. System.out.println("main end");
  16. }//thread start
  17. // main end
  18. // thread end mianend与thread start 打印顺序并非一定的,这个是并发,不一定谁会先执行
  19. }

线程一般会存在几个状态,New 新建的线程对象,Runnable 正在运行中,Block 被阻塞,Waitting 等待中, Timeed Waittding 被sleep计时等待, Terminated 执行完毕

  1. public class HelloWorld {
  2. public static void main(String[] args) throws Exception {
  3. // lambda 写法
  4. Thread t = new Thread(() -> {
  5. System.out.println("thread start");
  6. try {
  7. Thread.sleep(100); // 模拟IO操作,让线程等100毫秒
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. } finally {
  11. System.out.println("thread end");
  12. }
  13. });
  14. System.out.println(t.getState()); // NEW 未执行线程
  15. t.start();
  16. System.out.println(t.getState()); // RUNNABLE 正在运行的线程
  17. System.out.println("main end");
  18. Thread.sleep(10);
  19. System.out.println(t.getState()); // TIMED_WAITING 被sleep阻塞住的线程
  20. Thread.sleep(100);
  21. System.out.println(t.getState()); // TERMINATED 线程退出
  22. }
  23. }

在学习python的时候我们知道一个线程可以等待另外一个线程,那java中也是一样的,都使用join

  1. public class HelloWorld {
  2. public static void main(String[] args) throws Exception {
  3. // lambda 写法
  4. Thread t = new Thread(() -> {
  5. System.out.println("thread start");
  6. try {
  7. Thread.sleep(100); // 模拟IO操作,让线程等100毫秒
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. } finally {
  11. System.out.println("thread end");
  12. }
  13. });
  14. t.start();
  15. t.join(); // 等待 t 结束再往下走
  16. System.out.println("main end");
  17. } // thread start
  18. // thread end
  19. // main end 这个main end会最终执行
  20. }

注意join中也是可以传参数的,传入的int值,意思是等待多少毫秒

进程如何中断呢,有两种方法

方法一

  1. class MyThread extends Thread{
  2. @Override
  3. public void run(){
  4. // 判断进程是否被终端
  5. while(! isInterrupted()){
  6. System.out.println("i am alive");
  7. }
  8. System.out.println("end");
  9. }
  10. }
  11. public class HelloWorld {
  12. public static void main(String[] args) throws Exception {
  13. // lambda 写法
  14. Thread t = new MyThread();
  15. t.start();
  16. Thread.sleep(1000);
  17. t.interrupt(); // 中断进程
  18. System.out.println("main end");
  19. }
  20. }

还有一种是设置标识位

当我们使用public时,线程间是可以访问变量的,java虚拟机是将变量保存在主内存上,当某一个线程访问变量时,会复制一份走,然后保存在自己的工作空间,如果发生改写,虚拟机会在某个时间之后将修改后的变量值改写主内存,但是这个时间是不确定的,因此我们需要使用volatile来声明这个变量,这样就会做到下述两点:

  • 每次访问变量,都会去主内存中取复制一份
  • 每次修改变量,会立即写会主内存
  1. class MyThread extends Thread {
  2. public volatile boolean isExit = false;
  3. @Override
  4. public void run() {
  5. // 判断标志位
  6. while (!this.isExit) {
  7. System.out.println("i am alive");
  8. }
  9. System.out.println("end");
  10. }
  11. }
  12. public class HelloWorld {
  13. public static void main(String[] args) throws Exception {
  14. // lambda 写法
  15. MyThread t = new MyThread();
  16. t.start();
  17. Thread.sleep(1);
  18. t.isExit = true; // 中断进程
  19. System.out.println("main end");
  20. }
  21. }

我们在使用的如果不设置volatile中断时间感觉也很快,这个是因为JVM的回写主内存非常快,所以不要爆侥幸信息,一定要记得声明volatile

接下来看一下守护线程,守护线程就是当所有非守护线程结束时,他也会结束,我记得在pyton中又叫做傀儡线程,设置守护需要在启动线程之前。

  1. class MyThread extends Thread {
  2. @Override
  3. public void run() {
  4. // 判断进程是否被终端
  5. try {
  6. Thread.sleep(1000000);
  7. // 当线程被推出时 会抛出 InterruptedException 这个错误
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. System.out.println("end");
  12. }
  13. }
  14. public class HelloWorld {
  15. public static void main(String[] args) throws Exception {
  16. // lambda 写法
  17. MyThread t = new MyThread();
  18. t.setDaemon(true);
  19. t.start();
  20. Thread.sleep(10);
  21. System.out.println("main end");
  22. }
  23. }

t这个线程是主线程的守护线程,会在主线程退出时退出。

说了这个,那么多线程对于修改数据,是不是安全的呢?我们来看一个例子

  1. class Num{
  2. public static int num = 0;
  3. }
  4. class AddThread extends Thread {
  5. @Override
  6. public void run() {
  7. int i = 0;
  8. for(;i<100000;i++){
  9. Num.num += 1;
  10. }
  11. System.out.println("AddThread end");
  12. }
  13. }
  14. class DecThread extends Thread{
  15. @Override
  16. public void run(){
  17. int i = 0;
  18. for(;i<100000;i++){
  19. Num.num -=1;
  20. }
  21. System.out.println("DecThread end");
  22. }
  23. }
  24. public class HelloWorld {
  25. public static void main(String[] args) throws Exception {
  26. // lambda 写法
  27. Thread t = new AddThread();
  28. Thread t1 = new DecThread();
  29. System.out.println(Num.num); // 0
  30. t.start();
  31. t1.start();
  32. t.join();
  33. t1.join();
  34. System.out.println(Num.num); // -803
  35. }
  36. }

这个结果并非我们预期的0,因此需要咋办,这个是因为所有的线程调度都是由操作系统做的,如果add正在主内存中取值就被挂起,然后dec去主内存中取值并作减法,然后退回给主系统,之后add才调用,这个时候就会造成数据混乱,那么怎么办呢?加锁,也就是说,某个线程在操作数据时,另外一个线程是不能去碰这个数据

  1. class Num {
  2. public static int num = 0;
  3. public static final Object lock = new Object();
  4. }
  5. class AddThread extends Thread {
  6. @Override
  7. public void run() {
  8. int i = 0;
  9. for (; i < 100000; i++) {
  10. // synchronized 这个就锁住了Num.lock
  11. synchronized (Num.lock) {
  12. Num.num += 1;
  13. }// 释放锁
  14. }
  15. System.out.println("AddThread end");
  16. }
  17. }
  18. class DecThread extends Thread {
  19. @Override
  20. public void run() {
  21. int i = 0;
  22. for (; i < 100000; i++) {
  23. synchronized (Num.lock) {
  24. Num.num -= 1;
  25. }
  26. }
  27. System.out.println("DecThread end");
  28. }
  29. }
  30. public class HelloWorld {
  31. public static void main(String[] args) throws Exception {
  32. // lambda 写法
  33. Thread t = new AddThread();
  34. Thread t1 = new DecThread();
  35. System.out.println(Num.num); // 0
  36. t.start();
  37. t1.start();
  38. t.join();
  39. t1.join();
  40. System.out.println(Num.num); // 0
  41. }
  42. }

另外对于一些复制操作(基础类型除了long,double,还有引用类型)是原子操作,不需要加锁

我们来看一下如何进行加锁

  1. class Num {
  2. public static int num = 0;
  3. public static final Object lock = new Object();
  4. private int nn = 0;
  5. // 同步方法,这条语句相当于 synchronized(this){this.nn += 1;} 就是把整个实例都锁住了
  6. public synchronized void add() {
  7. this.nn += 1;
  8. }
  9. public synchronized void dec() {
  10. this.nn -= 1;
  11. }
  12. public void printNn() {
  13. System.out.println(this.nn);
  14. }
  15. }
  16. class AddThread extends Thread {
  17. private Num n = null;
  18. public AddThread(Num n) {
  19. super();
  20. this.n = n;
  21. }
  22. @Override
  23. public void run() {
  24. int i = 0;
  25. for (; i < 100000; i++) {
  26. // synchronized 这个就锁住了Num.lock
  27. synchronized (Num.lock) {
  28. Num.num += 1;
  29. }// 释放锁
  30. this.n.add();
  31. }
  32. System.out.println("AddThread end");
  33. }
  34. }
  35. class DecThread extends Thread {
  36. private Num n = null;
  37. public DecThread(Num n) {
  38. super();
  39. this.n = n;
  40. }
  41. @Override
  42. public void run() {
  43. int i = 0;
  44. for (; i < 100000; i++) {
  45. synchronized (Num.lock) {
  46. Num.num -= 1;
  47. }
  48. this.n.dec();
  49. }
  50. System.out.println("DecThread end");
  51. }
  52. }
  53. public class HelloWorld {
  54. public static void main(String[] args) throws Exception {
  55. Num n = new Num();
  56. // lambda 写法
  57. Thread t = new AddThread(n);
  58. Thread t1 = new DecThread(n);
  59. System.out.println(Num.num); // 0
  60. t.start();
  61. t1.start();
  62. t.join();
  63. t1.join();
  64. System.out.println(Num.num); // 0
  65. n.printNn(); // 0
  66. }
  67. }

java中的锁是可重入锁,什么意思,就是可以重复获取,获取一次锁计数+1,释放一次锁计数-1,最终释放完毕所有锁就归零

因此如果我们将锁住的信息定义为final标识符,那么他就不能重复获取锁,这样一旦锁的顺序不对,就会产生死锁,如下面的例子

  1. class Num {
  2. public static int num = 0;
  3. public static final Object lock = new Object();
  4. public static final Object lock2 = new Object();
  5. }
  6. class AddThread extends Thread {
  7. @Override
  8. public void run() {
  9. int i = 0;
  10. for (; i < 100000; i++) {
  11. // synchronized 这个就锁住了Num.lock
  12. synchronized (Num.lock2) {
  13. Num.num += 1;
  14. synchronized (Num.lock) {
  15. Num.num -= 1;
  16. }
  17. }// 释放锁
  18. }
  19. System.out.println("AddThread end");
  20. }
  21. }
  22. class DecThread extends Thread {
  23. @Override
  24. public void run() {
  25. int i = 0;
  26. for (; i < 100000; i++) {
  27. synchronized (Num.lock) {
  28. Num.num -= 1;
  29. synchronized (Num.lock2) {
  30. Num.num += 1;
  31. }
  32. }
  33. }
  34. System.out.println("DecThread end");
  35. }
  36. }
  37. public class HelloWorld {
  38. public static void main(String[] args) throws Exception {
  39. Thread t = new AddThread();
  40. Thread t1 = new DecThread();
  41. System.out.println(Num.num); // 0
  42. t.start();
  43. t1.start();
  44. t.join();
  45. t1.join();
  46. System.out.println(Num.num); // 0
  47. }
  48. }

t线程先锁住lock然后在去锁住lock2,t1线程先锁住lock2然后在去锁住lock,因为lock都是不可变的,有final标识符,这样t跟t1就产生了冲突,谁也无法获取对方的未释放的锁,产生了死锁,程序进入等待,因此一定要注意加锁的顺序

那么接下来我们需要思考对于队列来讲,如何进行多线程协同呢,也就是一些放任务,一些取任务,我们可能想到取任务时使用poll,但是我们要求必须取到任务,

  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Queue;
  5. class Task {
  6. Queue<String> q = new LinkedList<>();
  7. public synchronized void add(String s) {
  8. q.add(s);
  9. }
  10. public synchronized String get() throws InterruptedException {
  11. while (q.isEmpty()) {
  12. System.out.println("wait me");
  13. }
  14. return q.remove();
  15. }
  16. }
  17. public class HelloWorld {
  18. public static void main(String[] args) throws Exception {
  19. Task t = new Task();
  20. List<Thread> tt = new ArrayList<>();
  21. for (int i = 0; i < 10; i++) {
  22. Thread ts = new Thread(() -> {
  23. while (true) {
  24. String s = null;
  25. try {
  26. s = t.get();
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. System.out.println(s);
  31. }
  32. });
  33. ts.start();
  34. tt.add(ts);
  35. }
  36. Thread add = new Thread() {
  37. @Override
  38. public void run() {
  39. while (true) {
  40. for (int i = 0; i < 100; i++) {
  41. t.add("task" + i);
  42. try {
  43. Thread.sleep(10);
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. }
  49. }
  50. };
  51. add.start();
  52. }
  53. }

我看上述代码,这个就是我们之前看的加锁,但是看输出结果确实无限的wait me,这是因为在获取任务时,因为为空就会陷入while的无限循环中,这个这个方法锁住的是this,这个实例,add也就无法进行添加任务,形成了死循环,那么这个就需要wait跟notifyall这两个方法,他们必须在synchorized中使用,wait就是释放当前锁,并休眠,notifyall就是通知所有休眠的进行起来抢锁并执行,上述代码修改为下面这样

  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Queue;
  5. class Task {
  6. Queue<String> q = new LinkedList<>();
  7. public synchronized void add(String s) {
  8. q.add(s);
  9. this.notifyAll();
  10. }
  11. public synchronized String get() throws InterruptedException {
  12. while (q.isEmpty()) {
  13. this.wait();
  14. System.out.println("wait me");
  15. }
  16. return q.remove();
  17. }
  18. }
  19. public class HelloWorld {
  20. public static void main(String[] args) throws Exception {
  21. Task t = new Task();
  22. List<Thread> tt = new ArrayList<>();
  23. for (int i = 0; i < 10; i++) {
  24. Thread ts = new Thread(() -> {
  25. while (true) {
  26. String s = null;
  27. try {
  28. s = t.get();
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println(s);
  33. }
  34. });
  35. ts.start();
  36. tt.add(ts);
  37. }
  38. Thread add = new Thread() {
  39. @Override
  40. public void run() {
  41. while (true) {
  42. for (int i = 0; i < 100; i++) {
  43. t.add("task" + i);
  44. try {
  45. Thread.sleep(10);
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. }
  51. }
  52. };
  53. add.start();
  54. }
  55. }

增加两行代码,就实现了我们的功能。

synchronized锁在锁比较多的情况下容易造成死锁,而且在想要获取锁的时候必须等待,没有任何尝试机制,接下来我们看一下另外一种锁的ReentrantLock

  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Queue;
  5. import java.util.concurrent.TimeUnit;
  6. import java.util.concurrent.locks.Condition;
  7. import java.util.concurrent.locks.Lock;
  8. import java.util.concurrent.locks.ReentrantLock;
  9. class Task {
  10. Queue<String> q = new LinkedList<>();
  11. private final Lock lock = new ReentrantLock(); // 实例化锁
  12. private final Condition condition = lock.newCondition(); // 通过该锁,实例化一个与之匹配的condition
  13.  
  14. public void add(String s) throws InterruptedException {
  15. // 这个就是尝试获取锁,如果1s获取不到就不再等待,跳过这里面的代码块
  16. if (lock.tryLock(1, TimeUnit.SECONDS)) {
  17. try {
  18. q.add(s);
  19. condition.signalAll(); // 等同于notifyall
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  24. }
  25. public String get() throws InterruptedException {
  26. // 普通的锁,会等待
  27. lock.lock();
  28. try {
  29. while (q.isEmpty()) {
  30. condition.await(); // 等同于wait
  31. }
  32. return q.remove();
  33. } finally {
  34. lock.unlock();
  35. }
  36. }
  37. }
  38. public class HelloWorld {
  39. public static void main(String[] args) throws Exception {
  40. Task t = new Task();
  41. List<Thread> tt = new ArrayList<>();
  42. for (int i = 0; i < 10; i++) {
  43. Thread ts = new Thread(() -> {
  44. while (true) {
  45. String s = null;
  46. try {
  47. s = t.get();
  48. } catch (InterruptedException e) {
  49. e.printStackTrace();
  50. }
  51. System.out.println(s);
  52. }
  53. });
  54. ts.start();
  55. tt.add(ts);
  56. }
  57. Thread add = new Thread() {
  58. @Override
  59. public void run() {
  60. while (true) {
  61. for (int i = 0; i < 100; i++) {
  62. try {
  63. t.add("task" + i);
  64. } catch (InterruptedException e) {
  65. e.printStackTrace();
  66. }
  67. try {
  68. Thread.sleep(10);
  69. } catch (InterruptedException e) {
  70. e.printStackTrace();
  71. }
  72. }
  73. }
  74. }
  75. };
  76. add.start();
  77. }
  78. }

加下来看一下读写锁,上面的例子其实所有的读操作也会被锁,但对于读操作来讲,不影响我们的数据,我们希望在进行写的时候锁住,不写的时候,读没有限制,因此可以使用读写锁

  1. import java.util.*;
  2. import java.util.concurrent.locks.*;
  3. class Task {
  4. Queue<String> q = new LinkedList<>();
  5. private final ReadWriteLock lock = new ReentrantReadWriteLock(); // 实例化读写锁
  6. private final Lock rlock = lock.readLock(); // 实例化读锁
  7. private final Lock wlock = lock.writeLock(); // 实例化写锁
  8. private int[] n = new int[100];
  9. public void add(int i) throws InterruptedException {
  10. // 写入锁
  11. wlock.lock();
  12. try {
  13. n[i] += 1;
  14. } finally {
  15. wlock.unlock();
  16. }
  17. }
  18. public int[] get() throws InterruptedException {
  19. // 读锁
  20. rlock.lock();
  21. try {
  22. return Arrays.copyOf(n, n.length);
  23. } finally {
  24. rlock.unlock();
  25. }
  26. }
  27. }
  28. public class HelloWorld {
  29. public static void main(String[] args) throws Exception {
  30. Task t = new Task();
  31. List<Thread> tt = new ArrayList<>();
  32. for (int i = 0; i < 10; i++) {
  33. Thread ts = new Thread(() -> {
  34. while (true) {
  35. int[] s = null;
  36. try {
  37. s = t.get();
  38. } catch (InterruptedException e) {
  39. e.printStackTrace();
  40. }
  41. System.out.println(s);
  42. }
  43. });
  44. ts.start();
  45. tt.add(ts);
  46. }
  47. Thread add = new Thread() {
  48. @Override
  49. public void run() {
  50. while (true) {
  51. for (int i = 0; i < 100; i++) {
  52. try {
  53. t.add(i);
  54. } catch (InterruptedException e) {
  55. e.printStackTrace();
  56. }
  57. try {
  58. Thread.sleep(10);
  59. } catch (InterruptedException e) {
  60. e.printStackTrace();
  61. }
  62. }
  63. }
  64. }
  65. };
  66. add.start();
  67. }
  68. }

上面的读写锁是一种悲观锁,比如说,如果换成队列,也就是说,写的时候,读锁必须全部释放完毕,否则无法进行下一步操作,可以使用之前队列的例子试试,下面看一下乐观锁

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.concurrent.locks.*;
  4. class Task {
  5. private final StampedLock lock = new StampedLock(); // 实例化锁
  6. private Double s = 0.00;
  7. public void add(Double d) {
  8. // 写入锁
  9. long stamp = lock.writeLock();
  10. try {
  11. s += d;
  12. } finally {
  13. lock.unlockWrite(stamp);
  14. }
  15. }
  16. public Double get() {
  17. // 获取乐观锁
  18. long stamp = lock.tryOptimisticRead();
  19. Double d = s; // double 赋值非原子级操作
  20. System.out.println("du");
  21. // 检查当前所是否是最新的
  22. if (!lock.validate(stamp)) {
  23. System.out.println("not new");
  24. // 如果不是则获取悲观锁
  25. stamp = lock.readLock();
  26. try {
  27. d = s;
  28. } finally {
  29. lock.unlockRead(stamp);
  30. }
  31. }
  32. return d;
  33. }
  34. }
  35. public class HelloWorld {
  36. public static void main(String[] args) throws Exception {
  37. Task t = new Task();
  38. List<Thread> tt = new ArrayList<>();
  39. for (int i = 0; i < 10; i++) {
  40. Thread ts = new Thread(() -> {
  41. while (true) {
  42. Double s;
  43. s = t.get();
  44. System.out.println(s);
  45. }
  46. });
  47. ts.start();
  48. tt.add(ts);
  49. }
  50. Thread add = new Thread() {
  51. @Override
  52. public void run() {
  53. while (true) {
  54. for (int i = 0; i < 100; i++) {
  55. t.add(1.0000001);
  56. try {
  57. Thread.sleep(10000);
  58. } catch (InterruptedException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }
  63. }
  64. };
  65. add.start();
  66. }
  67. }

说完线程,那接下来我们看一下线程池

  1. import java.util.ArrayList;
  2. import java.util.concurrent.*;
  3. class Task implements Callable<String> {
  4. public String call() throws Exception {
  5. Thread.sleep(10000);
  6. return "mmm";
  7. }
  8. }
  9. class Task1 implements Runnable {
  10. private int i;
  11. public Task1(int d) {
  12. this.i = d;
  13. }
  14. public void run() {
  15. try {
  16. Thread.sleep(1000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("end" + this.i);
  21. }
  22. }
  23. class MyThread extends Thread {
  24. private Future<String> f;
  25. public MyThread(Future<String> f) {
  26. super();
  27. this.f = f;
  28. }
  29. @Override
  30. public void run() {
  31. try {
  32. System.out.println(f.get());
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. } catch (ExecutionException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }
  40. public class HelloWorld {
  41. public static void main(String[] args) throws Exception {
  42. ExecutorService es = Executors.newFixedThreadPool(4); // 固定大小的线程池
  43. // <T> Future<T> submit(Callable<T> task); 这个方法是往线程池中塞任务,看到接受的是Callable类型,返回的是Future类型,那就按这个借口来定义
  44. Future<String> f = es.submit(new Task()); // 获取一个未来产生的Future对象
  45. try {
  46. String m = f.get(1, TimeUnit.SECONDS); // 获取结果只等待指定时间,如果没有等到,会报出下面的错误
  47. System.out.println(m);
  48. } catch (Exception e) {
  49. System.out.println(e); // java.util.concurrent.TimeoutException
  50. } finally {
  51. System.out.println("not recv");
  52. }
  53. System.out.println(f.isDone()); // 判断是否完成
  54. System.out.println(f.get()); // 等待获取结果,会一直阻塞
  55. es.shutdown(); // 关闭线程池
  56. ExecutorService es1 = Executors.newCachedThreadPool(); // 动态变化的线程池
  57. var f1 = new ArrayList<Future<String>>();
  58. for (int i = 0; i < 10; i++) {
  59. Future<String> ff = es1.submit(new Task()); // 在这里尽量不要用定值的线程池,如果超出线程池大小,会报错
  60. f1.add(ff);
  61. } // 十个 mmm 同时打印
  62. for (Future<String> ffff : f1) {
  63. var mt = new MyThread(ffff);
  64. mt.start();
  65. mt.join();
  66. }
  67. Thread.sleep(10100); // 记得晚点关闭线程池
  68. es1.shutdown();
  69. ExecutorService es2 = Executors.newFixedThreadPool(2); // 固定大小的线程池
  70. for (int i = 0; i < 6; i++) {
  71. es2.submit(new Task1(i));
  72. } // 两个两个的打印
  73. Thread.sleep(10000);
  74. es2.shutdown();
  75. // 定时反复执行任务的线程池
  76. ScheduledExecutorService es3 = Executors.newScheduledThreadPool(4);
  77. es3.schedule(new Task1(1), 4, TimeUnit.SECONDS); // 4 s后执行这个任务
  78. es3.scheduleAtFixedRate(new Task1(2), 2, 1, TimeUnit.SECONDS); // 两秒后,每隔1秒执行一次任务
  79. es3.scheduleWithFixedDelay(new Task1(3), 2, 1, TimeUnit.SECONDS); // 两秒后,上一次任务结束,隔1秒执行一次任务
  80. Thread.sleep(10000);
  81. es3.shutdown();
  82. }
  83. }

使用feature总是得用get等待,或者轮询看看是否isDone,来看一下自动调用回掉对象

  1. import java.util.concurrent.*;
  2. public class HelloWorld {
  3. public static void main(String[] args) throws Exception {
  4. // 这个是串行
  5. CompletableFuture<String> f = CompletableFuture.supplyAsync(HelloWorld::call); // 第一个任务
  6. // 上面任务完成后执行这个任务,s是上一个的返回值
  7. CompletableFuture<String> f1 = f.thenApplyAsync((s) -> {
  8. System.out.println(s); // mmm
  9. return HelloWorld.call();
  10. });
  11. // f1成功之后打印
  12. f1.thenAccept((res) -> {
  13. System.out.println(res); // mmm
  14. });
  15. // f1失败打印
  16. f1.exceptionally(e -> {
  17. e.printStackTrace();
  18. return null;
  19. });
  20. Thread.sleep(4000);
  21. System.out.println("开始并行");
  22. // 接下来看一下并行的
  23. CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
  24. return call1(2);
  25. });
  26. CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> {
  27. return call1(3);
  28. });
  29. CompletableFuture<String> f4 = CompletableFuture.supplyAsync(() -> {
  30. return call1(4);
  31. });
  32. CompletableFuture<String> f5 = CompletableFuture.supplyAsync(() -> {
  33. return call1(5);
  34. });
  35. CompletableFuture<Object> ff = CompletableFuture.anyOf(f2, f3, f4, f5);
  36. f2.thenAccept(System.out::println);
  37. f3.thenAccept(System.out::println);
  38. f4.thenAccept(System.out::println);
  39. f5.thenAccept(System.out::println);
  40. ff.thenAccept(System.out::println); // 感觉默认的线程应该是3个,因为第四个总感觉执行的慢一点,ff就是谁的快接受那个线程
  41. Thread.sleep(4000);
  42. }
  43. static String call() {
  44. try {
  45. Thread.sleep(2000);
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. return "mmm";
  50. }
  51. static String call1(int n) {
  52. try {
  53. Thread.sleep(2000);
  54. } catch (InterruptedException e) {
  55. e.printStackTrace();
  56. }
  57. return "mmm" + n;
  58. }
  59. }

版权声明:本文为yangshixiong原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yangshixiong/p/12177601.html