Java多线程
- 继承Thread类
- 实现Runnable接口
- 使用匿名内部类的形式
- 使用lambda表达式
- 使用Callable和Future创建
- 使用线程池
- Spring的@Async注解
使用Callable和Future创建
futureTask的get方法之所以会阻塞是因为实现方法中使用了LockSupport.park()方法
1public class ThreadCallable implements Callable<Integer> {
2 @Override
3 public Integer call() throws Exception {
4 System.out.println(Thread.currentThread().getName() + " 开始执行");
5 Thread.sleep(1000);
6 System.out.println(Thread.currentThread().getName() + "执行结束");
7 return 1;
8 }
9}
10
11
12public class Test01 {
13
14 public static void main(String[] args) throws Exception {
15 ThreadCallable threadCall = new ThreadCallable();
16 FutureTask<Integer> futureTask = new FutureTask<>(threadCall);
17 new Thread(futureTask).start();
18 Integer result = futureTask.get();
19 System.out.println(Thread.currentThread().getName() + "," + result);
20 }
21}
线程安全
synchronized
用法:
- 修饰代码块 :指定加锁对象,进入同步代码块前要获得给定对象的锁
- 修饰实例方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁
- 修饰静态方法:作用于当前类对象(类.class)加锁,进入同步代码块的时候需要获得当前类对象的锁
线程同步
线程如何保证同步,即如何保证线程安全性问题
- 使用
synchronized锁,注意锁升级过程 - 使用
Lock锁,锁升级 - 使用T
hreadLocal,需要注意内存泄漏的问题 - 使用原子类,
CAS非阻塞
线程通讯
等待/通知机制
wait():线程阻塞并且释放锁notify():通知一个在对象上等待的线程,使其返回main()方法继续执行。返回的前提是改线程已获得了对象的锁。notifyAll():通知所有等待在该对象的线程
需要结合synchronoized关键字来使用:两个线程之间的通信,对于同一个对象来说,一个线程调用对象的wait()方法,一个线程调用对象的notify()方法,这个对象本身就需要同步。所以在调用wait()和notify()方法之前,需要使用synchronized关键字同步对象。
join()
join底层原理是基于wait()方法封装。
唤醒?
线程的状态

- 初始化状态
- 就绪状态
- 运行状态
- 死亡状态
- 阻塞状态
- 超时等待
- 等待状态
守护线程和用户线程
Java中的线程分为两种:守护线程和用户线程
创建出来的线程,默认为用户线程,通过Thread.setDaemon(true)来将线程设置为守护线程。
守护线程依赖于用户线程,用户线程退出了,其中的守护线程也就退出了,典型的额守护线程—垃圾回收线程。
用户线程是独立存在的,不会应为其他用户线程的退出而退出。
安全的停止一个线程
- stop():终止线程,并且清楚监控器锁的信息。
- destory():
- interrupt():打断正在运行或者正在阻塞的线程。
- 如果目标线程在调用
Object class的wait()、wait(long)或wait(long, int)方法、join()、join(long, int)或者sleep(long, int)方法时被阻塞,那么interrupt会生效,改线程中断状态将会被清除,抛出InterruptedException异常。 - 如果目标线程是被I/O或者NIO中的Channel所阻塞,同样,I/O操作会被终端或者返回特殊的异常值,达到终止线程的目的。
- 如果以上条件都不满足,则会设置此线程为中断状态。
- 如果目标线程在调用
- 标志位
Lock锁
使用ReentrantLock实现同步
lock()方法上锁unlock()方法释放锁
使用Condition实现等待/通知 类似于wait()和notify()、notifyAll()
Condition
1public class Test02 {
2
3 private Lock lock = new ReentrantLock();
4
5 private Condition condition = lock.newCondition();
6
7 private void signal(){
8 try {
9 lock.lock();
10 // 唤醒线程
11 condition.signal();
12 } catch(Exception e) {
13 e.printStackTrace();
14 } finally {
15 lock.unlock();
16 }
17
18 }
19
20 public static void main(String[] args) throws Exception {
21 Test02 test02 = new Test02();
22 test02.cal();
23
24 Thread.sleep(3000);
25 test02.signal();
26 }
27
28 public void cal(){
29 new Thread(() -> {
30 try {
31 lock.lock();
32 System.out.println("Thread is waiting...");
33 // 阻塞线程
34 condition.await();
35 System.out.println("Thread is running...");
36 } catch (InterruptedException e) {
37 e.printStackTrace();
38 } finally {
39 lock.unlock();
40 }
41 }).start();
42 }
43}
yield()
该方法会是线程主动放弃CPU的执行权
- 多线程
yield()会让线程从云心状态进入到就绪状态,让后调度执行的其他线程来竞争CPU。 - 具体的实现依赖于底层操作系统的任务调度器
优先级
- 在Java语言中,每个线程都有优先级,当线程调控器有机会选择新的线程时,线程的优先级越高,越有可能被执行。优先级可以设置1~10。数字越大代表优先级越高。(Oracle为Linux提供的Java虚拟机中,线程的优先级被忽略,即所有线程具有相同的优先级)。所以不能过度的依赖优先级
- 线程的优先级用数字来表示。默认范围是1~10.即
Thread.MIN_PRIORITY到Thread.MAX_PRIORITY。 - 如果CPU非常繁忙,优先级越高的线程获得更多的时间片,但是CPU空闲时,设置优先级几乎没有任何作用。
问题
Join()/wait()和sleep()的区别
-
sleep(long)方法在睡眠时不释放对象锁。 -
join(long)方法先执行另外一个线程,在等待的过程中释放锁,底层是基于wait()方法实现的。 -
wait(long)方法在等待的过程中会释放锁。
wait()/notify()在Object类中的原因
使用wait()方法的时候需要结合synchronized关键字,synchronized这把锁可以是任意对象,所以任意对象都可以调用wait()和notify()。
Callable和FutureTask
LockSupport
— END —