线程的实现方式

1.使用Thread类或继承Thread类

  1. Thread thread = new Thread() {
  2. @Override
  3. public void run() {
  4. //do something
  5. }
  6. };
  7. thread.start();

2.实现Runnable接口

  1. Runnable runnable = new Runnable() {
  2. @Override
  3. public void run() {
  4. //do something
  5. System.out.println("do something");
  6. }
  7. };
  8. Thread thread = new Thread(runnable);
  9. thread.start();

3.使用有返回值的Callable

  1. public class CallableTest implements Callable<Integer> {
  2. @Override
  3. public Integer call() throws Exception {
  4. return new Random().nextInt();
  5. }
  6. }
  7. ExecutorService service = Executors.newFixedThreadPool(10);
  8. Future<Integer> future = service.submit(new CallableTest());
  9. System.out.println(future.get());

4.使用lambda

  1. new Thread(() -> {
  2. System.out.println(Thread.currentThread().getName());
  3. }).start();

Java线程执行为什么不能直接调用run()方法,而要调用start()方法?

Java 中实现真正的多线程是 start 中的 start0() 方法,start0是native方法�,直接执行run() 方法只是当普通的方法调用。
截屏2022-03-13 11.12.28.png

CAS涉及到用户模式到内核模式的切换吗?

不涉及,因为CAS只是一个指令。

JAVA的线程都是内核线程。


Java线程调度是抢占式调度

抢占式线程调度:每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定,线程执行时间系统可控,也不会有一个线程导致整个进程阻塞。
Java语言一共10个级别的线程优先级(1-10,默认是5),在两线程同时处于ready状态时,优先级越高的线程越容易被系统选择执行。但优先级并不是很靠谱,因为Java线程是通过映射到系统的原生线程上来实现的,所以线程调度最终还是取决于操作系统。

Java线程的生命周期

Java语言中线程共有六种状态,分别是:

  • NEW(初始化状态)
  • RUNNABLE(可运行状态+运行状态)
  • BLOCKED(阻塞状态)
  • WAITING(无时限等待)
  • TIMED_WAITING(有时限等待)
  • TERMINATED(终止状态)

Thread常用方法

  • sleep方法,调用sleep会让当前线程从Running进入TIMED_WAITING状态,不会释放对象锁.
  • yield方法,yield会释放CPU资源,让当前线程从Running进入Runnable状态,让优先级更高(至少是相同)的线程获得执行机会,不会释放对象锁
  • join方法,调用join方法后,会等待join的方法结束再执行其他线程,一般用于等待异步线程执行完结果之后才能继续运行的场景。 ```java Thread t = new Thread(new Runnable() {

    1. @Override
    2. public void run() {
    3. System.out.println("thread begin...");
    4. try {
    5. Thread.sleep(2000);
    6. } catch (InterruptedException e) {
    7. e.printStackTrace();
    8. }
    9. System.out.println("thread end...");
    10. }
    11. });
    12. t.start();
    13. //主线程会等待线程t执行完
    14. t.join();
    15. System.out.println("main end...");

===================== thread begin… thread end…

main end…

  1. <br />
  2. <a name="bOcTK"></a>
  3. ## Java线程的中断机制
  4. Java没有直接的方法来停止某个线程,而是提供了中断机制。中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理。被中断的线程拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择不停止。
  5. <a name="AUG8I"></a>
  6. ### API的使用:
  7. - interrupt():将线程的中断标志位设置为true,不会停止线程。
  8. - isInterrupted():判断当前线程的中断标志位是否为true,不会清除中断标志位
  9. - Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle
  10. ```java
  11. System.out.println("begin...");
  12. Thread t = new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. while (true) {
  16. i++;
  17. System.out.println(i);
  18. //Thread.interrupted(); 清除中断标志位,不会打印下面的===========
  19. //Thread.currentThread().isInterrupted(); 不会清除中断标志位,继续打印下面的===========
  20. if (Thread.currentThread().isInterrupted()) {
  21. System.out.println("===========");
  22. }
  23. if (i == 10) {
  24. break;
  25. }
  26. }
  27. }
  28. });
  29. t.start();
  30. //不会停止线程,只是设置了一个中断标志位为true
  31. t.interrupt();

利用中断机制停止线程

  1. public class StopThread implements Runnable {
  2. @Override
  3. public void run() {
  4. int count = 0;
  5. while (!Thread.currentThread().isInterrupted() && count < 100) {
  6. System.out.println("count:" + count);
  7. count++;
  8. }
  9. System.out.println("线程stop...");
  10. }
  11. public static void main(String[] args) throws InterruptedException {
  12. Thread t = new Thread(new StopThread());
  13. t.start();
  14. Thread.sleep(5);
  15. Thread.interrupted();
  16. }
  17. }

截屏2022-03-13 12.32.36.png
处于休眠中的线程被中断,线程是可以感受到中断信号的,并且会抛出一个InterruptedException异常,同时清除中断信号,将中断标记位设置成false。

sleep可以被中断抛出中断异常:sleep interrupted,清除中断标志位
wait可以被中断抛出中断异常:InterruptedException,清除中断标志位

Java线程间通信

volatile

volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。

等待唤醒机制

等待唤醒机制可以基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被唤醒。

LockSupport

LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”。使用它可以在任何场合使线程阻塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒操作的顺序,但要注意连续多次唤醒的效果和一次唤醒是一样的。