知识点1-线程中断
public class LearnThread {public static void main(String[] args) {Runnable task1 = () -> {for (int i = 0; i < 5; i++) {System.out.println("task1" + i);}};Runnable task2 = () -> {for (int i = 0; i < 5; i++) {System.out.println("task2" + i);}};Thread thread1 = new Thread(task1);Thread thread2 = new Thread(task2);thread1.start();thread2.start();// 中断线程// 注意:当线程设置为中断true,如果线程被一个sleep调用阻塞,则会抛出一个InterruptedExceptionthread1.interrupt();// 静态方法,检查当前线程是否中断,并清除当前线程中断状态从true->falseThread.interrupted();// 获取当前线程对象并判断状态是否中断System.out.println(Thread.currentThread().isInterrupted());// 线程状态System.out.println(thread1.isInterrupted());System.out.println(thread2.isInterrupted());}}
知识点2-守护进程
守护线程可以通过调用t.setDaemon(true)将一个线程转换为守护线程,守护线程的唯一的用途是为其他线程提供服务。计时器线程就是定时地发送“计时器抵达”信号给其他线程,另外清空过时缓存项的线程也是守护线程。当只剩下守护线程时,虚拟机就会退出,因为只剩下守护线程,就没必要继续运行了;且该方法必须在线程启动前调用;
知识点3-线程组和异常处理器
public class LearnThread {public static void main(String[] args) {Runnable task1 = () -> {for (int i = 0; i < 3; i++) {System.out.println("task1" + i);}};Runnable task2 = () -> {List list = new ArrayList();list.get(1);for (int i = 0; i < 3; i++) {System.out.println("task2" + i);}};Thread thread1 = new Thread(task1);Thread thread2 = new Thread(task2);// 异常处理器System.out.println(thread1.getUncaughtExceptionHandler());thread2.setUncaughtExceptionHandler(new LearnUncaughtExceptionHandle());System.out.println(thread2.getUncaughtExceptionHandler());// 线程组LearnThreadGroup threadGroup = new LearnThreadGroup("test");threadGroup.uncaughtException(thread2, new RuntimeException());thread1.start();thread2.start();}}
public class LearnUncaughtExceptionHandle implements Thread.UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("线程异常抛出");}}
public class LearnThreadGroup extends ThreadGroup {public LearnThreadGroup(String name) {super(name);}public LearnThreadGroup(ThreadGroup parent, String name) {super(parent, name);}@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("LearnThreadGroup 线程异常抛出");}}
知识点4-线程优先级
知识点5-i++
i++ 不是原子操作,无法保证线程安全,包含读取累加再写入等操作;
知识点6-同步机制
修饰词synchronized,重入锁ReentrantLock(),公平锁ReentrantLock(true);公平锁比常规锁慢很多;
知识点7-条件对象
import java.util.Arrays;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*** 排他锁,条件对象使用场景** @author zhuangshd* @date 2022/3/1 22:28*/public class Bank {/*** 银行账号*/private final double[] accounts;/*** 唯一锁*/private Lock bankLock;/*** 条件对象*/private Condition sufficientFunds;public Bank(int n, double initialBalance) {// 初始化银行账号、存款accounts = new double[n];Arrays.fill(accounts, initialBalance);// 初始化重入锁bankLock = new ReentrantLock();// 初始化条件对象sufficientFunds = bankLock.newCondition();}/*** 转账方法*/public void transfer(int from, int to, double amount) throws InterruptedException {// 加锁bankLock.lock();try {while (accounts[from] < amount)// 条件对象,当条件不满足线程进入等待集,等待激活校验sufficientFunds.await();System.out.print(Thread.currentThread());accounts[from] -= amount;System.out.printf("%10.2f from %d to %d", amount, from, to);accounts[to] += amount;System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());// 激活,线程进行条件校验sufficientFunds.signalAll();} finally {// finally 解锁,防止异常不释放bankLock.unlock();}}/*** 获取账户总合*/public double getTotalBalance() {// 加锁bankLock.lock();try {double sum = 0;for (double a : accounts) {sum += a;}return sum;} finally {// 解锁bankLock.unlock();}}public int size() {return accounts.length;}}
/*** @author zhuangshd* @date 2022/3/1 22:47*/public class LockBankTest {public static final int ACCOUNTS = 10;public static final int MAX_AMOUNT = 1000;public static void main(String[] args) {Bank bank = new Bank(ACCOUNTS, MAX_AMOUNT);for (int i = 0; i < ACCOUNTS; i++) {int fromAccount = i;Runnable r = () -> {try {while (true) {int toAccount = (int) (bank.size() * Math.random());double amount = MAX_AMOUNT * Math.random();bank.transfer(fromAccount, toAccount, amount);Thread.sleep((int) (10 * Math.random()));}} catch (InterruptedException ex) {ex.printStackTrace();}};Thread thread = new Thread(r);thread.start();}}}/*** @author zhuangshd* @date 2022/3/1 22:47*/public class LockBankTest {public static final int ACCOUNTS = 10;public static final int MAX_AMOUNT = 1000;public static void main(String[] args) {Bank bank = new Bank(ACCOUNTS, MAX_AMOUNT);for (int i = 0; i < ACCOUNTS; i++) {int fromAccount = i;Runnable r = () -> {try {while (true) {int toAccount = (int) (bank.size() * Math.random());double amount = MAX_AMOUNT * Math.random();bank.transfer(fromAccount, toAccount, amount);Thread.sleep((int) (10 * Math.random()));}} catch (InterruptedException ex) {ex.printStackTrace();}};Thread thread = new Thread(r);thread.start();}}}
知识点8-synchronized关键字
①每个对象都有一个内部锁,而wait/notifyAll/notify方法是Object类的final方法,Condition方法必须命名为await/signalAll/signal从而不会与那些方法冲突;
②将静态方法修饰为同步也是合法的,它会获得相关类对象的内部锁,会锁定该方法或任何其他同步静态方法;
③内部锁和条件存在一些限制,包括:不能中断一个正在尝试获得锁的线程,不能指定尝试获取锁时的超时时间,每个锁仅有一个条件可能是不够的;
④同步方法推荐使用梯度,首先考虑java.util.concurent包中的某种机制,其次考虑synchronized关键字修饰程序,最后再使用Lock/condition;
/*** 每个对象都有一个内部锁** @author zhuangshd* @date 2022/3/1 23:15*/public class SyncBank {/*** 银行账号*/private final double[] accounts;public SyncBank(int n, double initialBalance) {// 初始化银行账号、存款accounts = new double[n];Arrays.fill(accounts, initialBalance);}/*** 转账方法*/public synchronized void transfer(int from, int to, double amount) throws InterruptedException {while (accounts[from] < amount)// synchronized 仅有一个条件对象,当条件不满足线程进入等待,等待激活校验wait();System.out.print(Thread.currentThread());accounts[from] -= amount;System.out.printf("%10.2f from %d to %d", amount, from, to);accounts[to] += amount;System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());// 激活,线程进行条件校验notifyAll();}/*** 获取账户总合*/public synchronized double getTotalBalance() {double sum = 0;for (double a : accounts) {sum += a;}return sum;}public int size() {return accounts.length;}}
知识点9-同步块
使用一个对象的锁来实现额外的原子操作,称为客户端锁定;
synchronized(accounts){accounts[from] -= amount;System.out.printf("%10.2f from %d to %d", amount, from, to);accounts[to] += amount;System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());}
知识点10-监视器概念
知识点11-volatile字段
volatile关键字为实例字段的同步访问提供了一个免锁机制,如果声明一个字段为volatile,那么编译器和虚拟机就知道该字段可能被另外一个线程并发更新。编译器会插入适当代码,以确保如果一个线程对变量的修改,对读取这个变量的其他线程都可见。volatile变量不能提供原子性,i++,不能保证读取、翻转、写入不被中断;
知识点12-final变量
用final修饰的变量可作为共享变量,可以保证其他线程看到变量更新后的值,但是映射操作不是线程安全的,如果有多个线程修改或读取这个映射,仍然需要同步;
知识点13-原子性
java.util.concurrent.atomic包中有很多类使用了很高效的机器级指令来保证其他操作的原子性;AtomicInteger/AtomicIntergerArray/LongAddr等等;
知识点14-死锁
知识点15-线程局部变量
SimleDateFormat不是线程安全的,多线程下,内部数据结构(?)可能会被并发破坏,做同步synchronized开销太大,局部变量SimleDateFormat太浪费(内存?);
public class LearnThreadLocal {// ThreadLoacl辅助类public static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("YYYY-MM-dd"));public static void main(String[] args) {// get方法会返回当前线程的那个实例System.out.println(dateFormat.get().format(new Date()));}}
知识点16-线程安全的集合
java.util.concurrent包
①阻塞队列,LinkedBlockingQueue,PriorityBlockingQueue;使用offer/poll/peek方法,队列容量没有上线,在并发操作上可以使用队列来实现线程问题;
②映射,ConcurrentHashMap,ConcurrentSkipListMap;线程安全,16,0.75;批操作search/reduce/forEach
③有序集,ConcurrentSkipListSet
/*** concurrent包下线程安全集合** @author zhuangshd* @date 2022/3/3 22:55*/public class LearnConcurrent {public static void main(String[] args) {// 阻塞队列LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue<String>();System.out.println(linkedBlockingQueue.offer("test"));//插入元素System.out.println(linkedBlockingQueue.poll());//移除并返回列头元素System.out.println(linkedBlockingQueue.peek());//返回队列头元素// 优先队列PriorityQueue priorityQueue = new PriorityQueue<>();// 映射ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>();ConcurrentSkipListMap concurrentSkipListMap = new ConcurrentSkipListMap<>();// 集合ConcurrentSkipListSet concurrentSkipListSet = new ConcurrentSkipListSet<>();//底层ConcurrentHashMap}}
知识点17-串行数组算法-parallel
知识点18-Callable和Futrue
异步运行的任务但是有返回值;接口Callable是参数化类型的接口,只有方法call,参数类型是返回值的类型,当Callable
public static void main(String[] args) throws Exception {Callable<Integer> task = () -> {Thread.sleep(1000L);return 1;};FutureTask<Integer> futureTask = new FutureTask<>(task);Thread thread = new Thread(futureTask);thread.start();System.out.println(futureTask.get());}
知识点19-执行器
执行器拥有许多静态工程方法用来构造线程池;例如:newCacheThreadPoll/newFixedThreadPoll等,在最优情况下,并发线程数等于处理器内核数;
调用submit提交Runable或Callable对象,会得到一个Future对象,得到结果或者取消线程任务;
对一个任务进行分解,通过执行器创建线程池来完成,使用ExecutorService的等待其中一个任务完成invokeAny()或者全部任务完成invokeAll(),阻塞式完成任务;结果集List
知识点20-异步计算-可完成Futrue
CompletableFuture
知识点21-回调中的长时间运行任务
当应用程序有界面时,需要使用线程来提高程序的响应性能,当遇到耗时工作时,不能在当前页面完成工作,需要启动另外一个工作线程;(下载文件等)
