知识点1-线程中断

  1. public class LearnThread {
  2. public static void main(String[] args) {
  3. Runnable task1 = () -> {
  4. for (int i = 0; i < 5; i++) {
  5. System.out.println("task1" + i);
  6. }
  7. };
  8. Runnable task2 = () -> {
  9. for (int i = 0; i < 5; i++) {
  10. System.out.println("task2" + i);
  11. }
  12. };
  13. Thread thread1 = new Thread(task1);
  14. Thread thread2 = new Thread(task2);
  15. thread1.start();
  16. thread2.start();
  17. // 中断线程
  18. // 注意:当线程设置为中断true,如果线程被一个sleep调用阻塞,则会抛出一个InterruptedException
  19. thread1.interrupt();
  20. // 静态方法,检查当前线程是否中断,并清除当前线程中断状态从true->false
  21. Thread.interrupted();
  22. // 获取当前线程对象并判断状态是否中断
  23. System.out.println(Thread.currentThread().isInterrupted());
  24. // 线程状态
  25. System.out.println(thread1.isInterrupted());
  26. System.out.println(thread2.isInterrupted());
  27. }
  28. }

知识点2-守护进程

守护线程可以通过调用t.setDaemon(true)将一个线程转换为守护线程,守护线程的唯一的用途是为其他线程提供服务。计时器线程就是定时地发送“计时器抵达”信号给其他线程,另外清空过时缓存项的线程也是守护线程。当只剩下守护线程时,虚拟机就会退出,因为只剩下守护线程,就没必要继续运行了;且该方法必须在线程启动前调用;

知识点3-线程组和异常处理器

  1. public class LearnThread {
  2. public static void main(String[] args) {
  3. Runnable task1 = () -> {
  4. for (int i = 0; i < 3; i++) {
  5. System.out.println("task1" + i);
  6. }
  7. };
  8. Runnable task2 = () -> {
  9. List list = new ArrayList();
  10. list.get(1);
  11. for (int i = 0; i < 3; i++) {
  12. System.out.println("task2" + i);
  13. }
  14. };
  15. Thread thread1 = new Thread(task1);
  16. Thread thread2 = new Thread(task2);
  17. // 异常处理器
  18. System.out.println(thread1.getUncaughtExceptionHandler());
  19. thread2.setUncaughtExceptionHandler(new LearnUncaughtExceptionHandle());
  20. System.out.println(thread2.getUncaughtExceptionHandler());
  21. // 线程组
  22. LearnThreadGroup threadGroup = new LearnThreadGroup("test");
  23. threadGroup.uncaughtException(thread2, new RuntimeException());
  24. thread1.start();
  25. thread2.start();
  26. }
  27. }
  1. public class LearnUncaughtExceptionHandle implements Thread.UncaughtExceptionHandler {
  2. @Override
  3. public void uncaughtException(Thread t, Throwable e) {
  4. System.out.println("线程异常抛出");
  5. }
  6. }
  1. public class LearnThreadGroup extends ThreadGroup {
  2. public LearnThreadGroup(String name) {
  3. super(name);
  4. }
  5. public LearnThreadGroup(ThreadGroup parent, String name) {
  6. super(parent, name);
  7. }
  8. @Override
  9. public void uncaughtException(Thread t, Throwable e) {
  10. System.out.println("LearnThreadGroup 线程异常抛出");
  11. }
  12. }

知识点4-线程优先级

setPriority()

知识点5-i++

i++ 不是原子操作,无法保证线程安全,包含读取累加再写入等操作;

知识点6-同步机制

修饰词synchronized,重入锁ReentrantLock(),公平锁ReentrantLock(true);公平锁比常规锁慢很多;

知识点7-条件对象

  1. import java.util.Arrays;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. /**
  6. * 排他锁,条件对象使用场景
  7. *
  8. * @author zhuangshd
  9. * @date 2022/3/1 22:28
  10. */
  11. public class Bank {
  12. /**
  13. * 银行账号
  14. */
  15. private final double[] accounts;
  16. /**
  17. * 唯一锁
  18. */
  19. private Lock bankLock;
  20. /**
  21. * 条件对象
  22. */
  23. private Condition sufficientFunds;
  24. public Bank(int n, double initialBalance) {
  25. // 初始化银行账号、存款
  26. accounts = new double[n];
  27. Arrays.fill(accounts, initialBalance);
  28. // 初始化重入锁
  29. bankLock = new ReentrantLock();
  30. // 初始化条件对象
  31. sufficientFunds = bankLock.newCondition();
  32. }
  33. /**
  34. * 转账方法
  35. */
  36. public void transfer(int from, int to, double amount) throws InterruptedException {
  37. // 加锁
  38. bankLock.lock();
  39. try {
  40. while (accounts[from] < amount)
  41. // 条件对象,当条件不满足线程进入等待集,等待激活校验
  42. sufficientFunds.await();
  43. System.out.print(Thread.currentThread());
  44. accounts[from] -= amount;
  45. System.out.printf("%10.2f from %d to %d", amount, from, to);
  46. accounts[to] += amount;
  47. System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
  48. // 激活,线程进行条件校验
  49. sufficientFunds.signalAll();
  50. } finally {
  51. // finally 解锁,防止异常不释放
  52. bankLock.unlock();
  53. }
  54. }
  55. /**
  56. * 获取账户总合
  57. */
  58. public double getTotalBalance() {
  59. // 加锁
  60. bankLock.lock();
  61. try {
  62. double sum = 0;
  63. for (double a : accounts) {
  64. sum += a;
  65. }
  66. return sum;
  67. } finally {
  68. // 解锁
  69. bankLock.unlock();
  70. }
  71. }
  72. public int size() {
  73. return accounts.length;
  74. }
  75. }
  1. /**
  2. * @author zhuangshd
  3. * @date 2022/3/1 22:47
  4. */
  5. public class LockBankTest {
  6. public static final int ACCOUNTS = 10;
  7. public static final int MAX_AMOUNT = 1000;
  8. public static void main(String[] args) {
  9. Bank bank = new Bank(ACCOUNTS, MAX_AMOUNT);
  10. for (int i = 0; i < ACCOUNTS; i++) {
  11. int fromAccount = i;
  12. Runnable r = () -> {
  13. try {
  14. while (true) {
  15. int toAccount = (int) (bank.size() * Math.random());
  16. double amount = MAX_AMOUNT * Math.random();
  17. bank.transfer(fromAccount, toAccount, amount);
  18. Thread.sleep((int) (10 * Math.random()));
  19. }
  20. } catch (InterruptedException ex) {
  21. ex.printStackTrace();
  22. }
  23. };
  24. Thread thread = new Thread(r);
  25. thread.start();
  26. }
  27. }
  28. }/**
  29. * @author zhuangshd
  30. * @date 2022/3/1 22:47
  31. */
  32. public class LockBankTest {
  33. public static final int ACCOUNTS = 10;
  34. public static final int MAX_AMOUNT = 1000;
  35. public static void main(String[] args) {
  36. Bank bank = new Bank(ACCOUNTS, MAX_AMOUNT);
  37. for (int i = 0; i < ACCOUNTS; i++) {
  38. int fromAccount = i;
  39. Runnable r = () -> {
  40. try {
  41. while (true) {
  42. int toAccount = (int) (bank.size() * Math.random());
  43. double amount = MAX_AMOUNT * Math.random();
  44. bank.transfer(fromAccount, toAccount, amount);
  45. Thread.sleep((int) (10 * Math.random()));
  46. }
  47. } catch (InterruptedException ex) {
  48. ex.printStackTrace();
  49. }
  50. };
  51. Thread thread = new Thread(r);
  52. thread.start();
  53. }
  54. }
  55. }

知识点8-synchronized关键字

①每个对象都有一个内部锁,而wait/notifyAll/notify方法是Object类的final方法,Condition方法必须命名为await/signalAll/signal从而不会与那些方法冲突;
②将静态方法修饰为同步也是合法的,它会获得相关类对象的内部锁,会锁定该方法或任何其他同步静态方法;
③内部锁和条件存在一些限制,包括:不能中断一个正在尝试获得锁的线程,不能指定尝试获取锁时的超时时间,每个锁仅有一个条件可能是不够的;
④同步方法推荐使用梯度,首先考虑java.util.concurent包中的某种机制,其次考虑synchronized关键字修饰程序,最后再使用Lock/condition;

  1. /**
  2. * 每个对象都有一个内部锁
  3. *
  4. * @author zhuangshd
  5. * @date 2022/3/1 23:15
  6. */
  7. public class SyncBank {
  8. /**
  9. * 银行账号
  10. */
  11. private final double[] accounts;
  12. public SyncBank(int n, double initialBalance) {
  13. // 初始化银行账号、存款
  14. accounts = new double[n];
  15. Arrays.fill(accounts, initialBalance);
  16. }
  17. /**
  18. * 转账方法
  19. */
  20. public synchronized void transfer(int from, int to, double amount) throws InterruptedException {
  21. while (accounts[from] < amount)
  22. // synchronized 仅有一个条件对象,当条件不满足线程进入等待,等待激活校验
  23. wait();
  24. System.out.print(Thread.currentThread());
  25. accounts[from] -= amount;
  26. System.out.printf("%10.2f from %d to %d", amount, from, to);
  27. accounts[to] += amount;
  28. System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
  29. // 激活,线程进行条件校验
  30. notifyAll();
  31. }
  32. /**
  33. * 获取账户总合
  34. */
  35. public synchronized double getTotalBalance() {
  36. double sum = 0;
  37. for (double a : accounts) {
  38. sum += a;
  39. }
  40. return sum;
  41. }
  42. public int size() {
  43. return accounts.length;
  44. }
  45. }

知识点9-同步块

使用一个对象的锁来实现额外的原子操作,称为客户端锁定;

  1. synchronized(accounts){
  2. accounts[from] -= amount;
  3. System.out.printf("%10.2f from %d to %d", amount, from, to);
  4. accounts[to] += amount;
  5. System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
  6. }

知识点10-监视器概念

monitor

知识点11-volatile字段

volatile关键字为实例字段的同步访问提供了一个免锁机制,如果声明一个字段为volatile,那么编译器和虚拟机就知道该字段可能被另外一个线程并发更新。编译器会插入适当代码,以确保如果一个线程对变量的修改,对读取这个变量的其他线程都可见。volatile变量不能提供原子性,i++,不能保证读取、翻转、写入不被中断;

知识点12-final变量

用final修饰的变量可作为共享变量,可以保证其他线程看到变量更新后的值,但是映射操作不是线程安全的,如果有多个线程修改或读取这个映射,仍然需要同步;

知识点13-原子性

java.util.concurrent.atomic包中有很多类使用了很高效的机器级指令来保证其他操作的原子性;AtomicInteger/AtomicIntergerArray/LongAddr等等;

知识点14-死锁

知识点15-线程局部变量

SimleDateFormat不是线程安全的,多线程下,内部数据结构(?)可能会被并发破坏,做同步synchronized开销太大,局部变量SimleDateFormat太浪费(内存?);

  1. public class LearnThreadLocal {
  2. // ThreadLoacl辅助类
  3. public static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("YYYY-MM-dd"));
  4. public static void main(String[] args) {
  5. // get方法会返回当前线程的那个实例
  6. System.out.println(dateFormat.get().format(new Date()));
  7. }
  8. }

知识点16-线程安全的集合

java.util.concurrent包
①阻塞队列,LinkedBlockingQueue,PriorityBlockingQueue;使用offer/poll/peek方法,队列容量没有上线,在并发操作上可以使用队列来实现线程问题;
②映射,ConcurrentHashMap,ConcurrentSkipListMap;线程安全,16,0.75;批操作search/reduce/forEach
③有序集,ConcurrentSkipListSet

  1. /**
  2. * concurrent包下线程安全集合
  3. *
  4. * @author zhuangshd
  5. * @date 2022/3/3 22:55
  6. */
  7. public class LearnConcurrent {
  8. public static void main(String[] args) {
  9. // 阻塞队列
  10. LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue<String>();
  11. System.out.println(linkedBlockingQueue.offer("test"));//插入元素
  12. System.out.println(linkedBlockingQueue.poll());//移除并返回列头元素
  13. System.out.println(linkedBlockingQueue.peek());//返回队列头元素
  14. // 优先队列
  15. PriorityQueue priorityQueue = new PriorityQueue<>();
  16. // 映射
  17. ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>();
  18. ConcurrentSkipListMap concurrentSkipListMap = new ConcurrentSkipListMap<>();
  19. // 集合
  20. ConcurrentSkipListSet concurrentSkipListSet = new ConcurrentSkipListSet<>();//底层ConcurrentHashMap
  21. }
  22. }

知识点17-串行数组算法-parallel

Arrays.parallelSort(strings);

知识点18-Callable和Futrue

异步运行的任务但是有返回值;接口Callable是参数化类型的接口,只有方法call,参数类型是返回值的类型,当Callable表示最终返回Integer对象的异步计算。Future保存异步计算的结果,注意接口的get方法会阻塞,知道计算完成;注意FutureTask的使用;

  1. public static void main(String[] args) throws Exception {
  2. Callable<Integer> task = () -> {
  3. Thread.sleep(1000L);
  4. return 1;
  5. };
  6. FutureTask<Integer> futureTask = new FutureTask<>(task);
  7. Thread thread = new Thread(futureTask);
  8. thread.start();
  9. System.out.println(futureTask.get());
  10. }

知识点19-执行器

执行器拥有许多静态工程方法用来构造线程池;例如:newCacheThreadPoll/newFixedThreadPoll等,在最优情况下,并发线程数等于处理器内核数;
调用submit提交Runable或Callable对象,会得到一个Future对象,得到结果或者取消线程任务;
对一个任务进行分解,通过执行器创建线程池来完成,使用ExecutorService的等待其中一个任务完成invokeAny()或者全部任务完成invokeAll(),阻塞式完成任务;结果集List>;

知识点20-异步计算-可完成Futrue

CompletableFuture 除了get等待回调,另外就是等待结果可用时进行处理(无需等待);同时也可以组合可完成Future,例如ComletableFuturn content = readPage(url);ComletableFuturn> imageURLs = content.thenApply(this::getLinks);

知识点21-回调中的长时间运行任务

当应用程序有界面时,需要使用线程来提高程序的响应性能,当遇到耗时工作时,不能在当前页面完成工作,需要启动另外一个工作线程;(下载文件等)

知识点22-进程