多线程基础:
参考:廖雪峰官网https://www.liaoxuefeng.com/wiki/1252599548343744/1255943750561472
《java技术卷1》
进程:一个任务就是一个进程
线程:子任务
进程 V 线程:.
- 创建进程比创建线程开销
- 进程间的通信比线程通信慢
进程优点:稳定性高,一个进程崩溃不影响其它进程
创建进程并执行代码:
从 Thread 派生一个自定义类
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Hello {
public static void main(String[] args) throws Exception {
Thread t = new MyThread();
t.start();
}
}
class MyThread extends Thread {
public void run() {
System.out.println("start new Thread!");
}
}- 创建 Thread 实例,传入一个 Runnable 实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Hello {
public static void main(String[] args) throws Exception {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
class MyRunnable implements Runnable {
public void run(){
System.out.println("start new thread!");
}
}lamba 语法
1
2
3
4Thread t = new Thread(()->{
System.out.println("start new thread!");
});
t.start();
线程状态
New:新创建的线程,尚未执行;
Runnable:运行中的线程,正在执行 run()方法的 Java 代码;
Blocked:运行中的线程,因为某些操作被阻塞而挂起;
Waiting:运行中的线程,因为某些操作在等待中;
Timed Waiting:运行中的线程,因为执行 sleep()方法正在计时等待;
Terminated:线程已终止,因为 run()方法执行完毕。
中断线程
1 | public class Main { |
标识位:
1 | t.running = false; // 标志位置为false |
守护线程
1 | Thread t = new MyThread(); |
线程同步
多线程同时读写共享变量时,会造成逻辑错误,因此需要通过 synchronized 同步;
同步的本质就是给指定对象加锁,加锁后才能继续执行后续代码;
注意加锁对象必须是同一个实例;
对 JVM 定义的单个原子操作不需要同步。
基本类型(long 和 double 除外)赋值,例如:int n = m;
引用类型赋值,例如:List
1 |
|
同步方法
用 synchronized 修饰方法可以把整个方法变为同步代码块,synchronized 方法加锁对象是 this;
通过合理的设计和数据封装可以让一个类变为“线程安全”;
一个类没有特殊说明,默认不是 thread-safe; arrayList
ThreadLocal
横跨若干方法调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态,可以是用户身份、任务信息等。
ThreadLocal相当于给每个线程都开辟了一个独立的存储空间,各个线程的ThreadLocal关联的实例互不干扰。
hreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量
保证能释放ThreadLocal关联的实例,我们可以通过AutoCloseable接口配合try (resource) {…}结构,让编译器自动为我们关闭
1 | private static ThreadLocal<String> threadLabel = new ThreadLocal<>(); |
死锁
可重入锁: JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫做可重入锁。
死锁:两个线程各自持有不同的锁,然后各自试图获取对方手里的锁,造成了双方无限等待下去,这就是死锁。
解决死锁: 线程获取锁的顺序要一致
1 |
|
wait notify
wait和notify用于多线程协调运行:
在synchronized内部可以调用wait()使线程进入等待状态;
必须在已获得的锁对象上调用wait()方法;
在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程;
必须在已获得的锁对象上调用notify()或notifyAll()方法;
已唤醒的线程还需要重新获得锁后才能继续执行。
1 |
|
ReentrantLock
java 引入了一个 java.util.concurrent包,它提供了大量更高级的并发功,简化synchronized操作
1 |
|
Condition
ReentrantLock 无法实现 wait和notify功能
1 |
|
ReadWriteLock
ReadWriteLock只允许一个线程写入;
ReadWriteLock允许多个线程在没有写入时同时读取;
ReadWriteLock适合读多写少的场景。
1 |
|
StampedLock
StampedLock 是java8引入 是一种读的过程中的,相对于ReadWriteLock读锁是一种乐观锁
1 | long stamp = stampedLock.writeLock(); // 获取写锁 |
Concurrent
List、Map、Set、Deque等,java.util.concurrent包也提供了对应的并发集合类。
- interface non-thread-safe thread-safe
- List ArrayList CopyOnWriteArrayList
- Map HashMap ConcurrentHashMap
- Set HashSet/TreeSet CopyOnWriteArraySet
- Queue ArrayDeque / LinkedList ArrayBlockingQueue / LinkedBlockingQueue
- Deque ArrayDeque / LinkedList LinkedBlockingDeque
1 |
|
Atomic原子操作
1 |
|
线程池
创建固定大小线程池:
ExecutorService es = Executors.newFixedThreadPool(4);
es.submit
es.shutdown
Executors.newCachedThreadPool()
1 |
|
定期的任务:
ScheduledThreadPool
// 1秒后执行一次性任务:
1 |
|
Future
当我们提交一个Callable任务后,我们会同时获得一个Future对象,然后,我们在主线程某个时刻调用Future对象的get()方法,就可以获得异步执行的结果。
1 | ExecutorService executor = Executors.newFixedThreadPool(4); |
一个Future
get():获取结果(可能会等待)
get(long timeout, TimeUnit unit):获取结果,但只等待指定的时间;
cancel(boolean mayInterruptIfRunning):取消当前任务;
isDone():判断任务是否已完成。
CompletableFuture
从Java 8开始引入了CompletableFuture,它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。
ForkJoin
把大任务拆分小任务
- 本文作者: 东方觉主
- 本文链接: http://www.charon193.com/2021/05/23/javaThread/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!