what?

  • 比进程更加轻量级的调度执行单位
  • 线程是cpu调度的基本单位
  • 各个线程可以共享进程资源又可以独立调度

操作系统线程实现

  • 使用内核线程实现,由内核管理,对外接口是LWP(Light Weight Process)轻量级进程
    • 由内核通过scheduler对线程进行调度
    • 程序不直接使用内核线程,而是使用内核线程的高级接口轻量级进程(LWP)
    • 每个Light Weight Process都由一个内核线程支持
    • 需要系统调用,调用代价相对较高
  • 使用用户线程实现(Java, Ruby曾经使用过,最终放弃),
    • 完全建立在用户空间,不需要内核帮助
    • 所有线程操作用户程序自己管理(复杂):
      • 线程切换、创建、调度
      • 阻塞如何处理
      • 多处理器系统如何将线程映射到其他处理器上
  • 使用用户线程和轻量级进程混合实现
    • 用户线程完全建立在用户空间,用户线程的创建、切换、析构等操作依然廉价
    • 用户线程的调度使用轻量级进程来完成
    • 用户线程和轻量级进程是多对多关系,N:M

Java线程实现

  • < JDK1.2 基于”绿色线程”的用户线程实现
  • JDK1.2开始, 基于操作系统原生线程模型
    • Windows版和Linux版都是使用一对一的线程模型实现, 一条java线程映射到一条轻量级进程中
    • Solaris平台同时支持一对一和多对多

MainThread vs child thread

  • no relationship of parent and child
  • inherit from ‘parent thread’
    • thread daemon property
    • thread priority

how many ways create thread

one way create thread: new Thread() , but three way to assign task:

  • java.lang.Runnable
  • java.lang.Thread class itself.
  • java.util.concurrent.Callable Interface.
    • A callable returns a , which will become a Future when submitted via an ExecutorService
  1. //extends thread
  2. class MyThread extends Thread {
  3. @Override
  4. public void run() {
  5. System.out.println("Thread assign task of new Thread");
  6. }
  7. }
  8. //by Runnable
  9. class ThreadTask implements Runnable {
  10. @Override
  11. public void run() {
  12. //Code present here will be executed in separate independent path.
  13. System.out.println("Thread assign task of Runnable interface");
  14. }
  15. }
  16. //Lets create Task first to assign it to the Thread
  17. ThreadTask threadTask = new ThreadTask();
  18. //Lets create a Thread and assign task to it.
  19. //Way to assign task to a Thread is by passing task object(Runnable) to Thread's constructor.
  20. Thread thread1 = new Thread(threadTask);
  21. //Start a thread
  22. thread1.start();

start() vs run()

  • 直接跑run() , 则在本线程跑
  • 调用start, 会在新的线程跑

Java常用线程类

start thread twice

illegalThreadStateException will be thrown

调用子线程的join()方法

主线程会等待子线程执行完才会结束

Thread.yield()

yield() basically means that the thread is not doing anything particularly important and if any other threads or processes need to be run, they should run. Otherwise, the current thread will continue to run.

  • gives hint to the thread scheduler that it is ready to pause its execution
  • thread scheduler checks if there is any thread with same or high priority than this thread, then move the current thread to Ready/Runnable state and give processor to other thread

一般比较少用该方法,一般是debug或者测试的时候使用

join()

waits for this thread to die

detail:
https://stackoverflow.com/questions/15956231/what-does-this-thread-join-code-mean

  1. Thread t1 = new Thread(new EventThread("e1"));
  2. t1.start();
  3. Thread t2 = new Thread(new EventThread("e2"));
  4. t2.start();
  5. while (true) { //这个while是为了保证出异常的时候t2继续运行
  6. try {
  7. t1.join();
  8. t2.join();
  9. break;
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  • When join method is invoked, current thread would release its resources and goes in waiting state
  • thread1 thread2 run in parallel—of course, 他们先调用了start()
  • After run() method of thread is completed, it doesn’t mean thread task is completed, It has to do many other tasks like
    • Destroying the associated stack,
    • Setting the necessary threadStatus etc.

One of the task is notifying the waiting threads, So that Thread waiting on join() method will be notified that thread has completed its task and joined threads can resume.

  • Above task are executed inside native thread call, so it wont be visible in java thread API.
    • 原理
  • 检查join线程是否存活,如果存活则让当前线程wait
  • join线程中止后,该线程的this.notifyAll()会被调用

what join mean

  1. while (isAlive()) { //这个while是为了保证lock.notifyAll的时候判断线程是否还存活
  2. lock.wait(0); //target thread's lock
  3. }

Can Thread be created without any ThreadGroup

  • No

    java-thread-group-hierarchy.png

子线程抛异常,主线程是否会继续运行

yes, In general, Exception thrown by one thread will not affect another thread, as all threads are independent and have different stack.

线程状态

image.png

  • BLOCKED 是指线程正在等待获取锁
  • WAITING 是指线程正在等待其他线程的“唤醒动作”
  • Thread.sleep
    • 告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”
    • Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”
    • 不会释放锁
  • object.wait会释放锁
    • Waiting only releases the lock for the object you call wait() on. It doesn’t release any other locks ```java //You don’t actually need to call sleep from within a lock - //locks and wait/notify go hand in hand but locks and sleep are unrelated synchronized(LOCK) { Thread.sleep(1000); // LOCK is held }

synchronized(LOCK) { LOCK.wait(); // LOCK is not held }

  1. <a name="nL22K"></a>
  2. ### 线程状态转换
  3. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/291168/1627226649374-759b70e5-f22f-4aa0-b44f-abadf795ac71.png#align=left&display=inline&height=256&margin=%5Bobject%20Object%5D&name=image.png&originHeight=511&originWidth=723&size=33626&status=done&style=none&width=361.5)
  4. - _WAITING [never](https://stackoverflow.com/a/13664082/791406) goes directly to RUNNABLE_
  5. - _The thread is WAITING until it is notified...Then it [becomes](https://stackoverflow.com/a/2534158/791406) BLOCKED..._
  6. - _Once this thread is notified, it [will not be runnable](https://stackoverflow.com/a/15680579/791406)...This is..Blocked State._
  7. - 操作系统隐藏 Java虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到 RUNNABLE 状态
  8. <a name="q3fZj"></a>
  9. ### wait-notify
  10. when we call wait() – this forces the current thread to wait until some other thread invokes notify() or notifyAll() on the same object.
  11. current thread must own the object’s monitor, 3 ways--synchronized方法
  12. - executed synchronized instance method for the given object
  13. - executed the body of a synchronized block on the given object
  14. - by executing _synchronized static _methods for objects of type _Class_
  15. > **only one active thread can own an object’s monitor at a time.**
  16. <a name="synchronized"></a>
  17. ### synchronized
  18. - 方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.
  19. - 对某一代码块使用,synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块
  20. - synchronized后面括号里是一对象,此时,线程获得的是对象锁.如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行.在对象级使用锁通常是一种比较粗糙的方法
  21. - synchronized后面括号里是类, 如,synchronized(ArrayWithLockOrder.class)。 如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法。
  22. - 锁是和对象相关联的,每个对象有一把锁,为了执行synchronized语句,线程必须能够获得synchronized语句中表达式指定的对象的锁,一个对象只有一把锁,被一个线程获得之后它就不再拥有这把锁,线程在执行完synchronized语句后,将获得锁交还给对象。
  23. - 在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法。
  24. - 同步化方法在执行之前获得一个锁。如果这是一个类方法,那么获得的锁是和声明方法的类相关的Class类对象的锁。如果这是一个实例方法,那么此锁是this对象的锁。
  25. <a name="09b7f3a4"></a>
  26. ### notify, notifyAll
  27. - wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
  28. - 调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
  29. - 调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
  30. - 调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
  31. - 一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。
  32. 为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?
  33. - 由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。 而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
  34. - 调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
  35. - 调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);
  36. - notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
  37. - 同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
  38. - nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。 这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。
  39. <a name="5895e27e"></a>
  40. ### Multithreading Exceptions
  41. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/291168/1588644368901-ec57ab99-fe84-46ee-8f97-265a5c6e5222.png#align=left&display=inline&height=435&id=jhWDD&margin=%5Bobject%20Object%5D&name=image.png&originHeight=386&originWidth=640&size=45597&status=done&style=none&width=722)
  42. > Current Thread -> ThreadGroup -> Jvm Level(Global Thread level)
  43. > 如果都没有,则调用Threadgroup class ("main" threadgroup to which main thread is part of) provide default implementation

System.err.print(“Exception in thread \”” + t.getName() + “\” “);

  1. - At Thread Level

Thread t1 = new Thread(new WorkerThread()); t1.setName(“T4”);

t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){ @Override public void uncaughtException(Thread t, Throwable e){ System.out.println(“Thread Exception Handler :Thread Name :”+t.getName() + “ Message :”+e.getMessage()); } }); t1.start();

  1. - At ThreadGroup Level

ThreadGroup tr = new ThreadGroup(“MyGroup”){ @Override public void uncaughtException(Thread t, Throwable e) { System.out.println(“ThreadGroup Exception Handler :Thread Name :”+t.getName() + “ Message :”+e.getMessage()); } };

Thread t1 = new Thread(tr, new WorkerThread()); t1.setName(“T1”);

  1. - At Global Thread Level

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
@Override public void uncaughtException(Thread t, Throwable e){ System.out.println(“Default Exception Handler :Thread Name :”+t.getName() + “ Message :”+e.getMessage()); }

  1. - 线程发生异常是会释放锁的--monitorexit
  2. <a name="DW1ap"></a>
  3. ### To kill a thread
  4. - Thread.stop() is Deprecated
  5. - 不安全,unlock所有锁
  6. - 使用标志位
  7. ```java
  8. class MyThread extends Thread
  9. {
  10. volatile boolean finished = false;
  11. public void stopMe()
  12. {
  13. finished = true;
  14. }
  15. public void run()
  16. {
  17. while (!finished)
  18. {
  19. //do dirty work
  20. }
  21. }
  22. }

Thread.interrupt()-参考

一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。而 Thread.interrupt 的作用其实也不是中断线程,而是「通知线程应该中断了」。调用interrupt()时

  • 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException
  • 线程处于正常活动状态,将会将终端标志设置为true

中断是协作机制,需要程序猿自己实现。

  1. Thread t1 = new Thread( new Runnable(){
  2. public void run(){
  3. // 若未发生中断,就正常执行任务
  4. while(!Thread.currentThread.isInterrupted()){
  5. // 正常任务代码……
  6. }
  7. // 中断的处理代码……
  8. doSomething();
  9. }
  10. } ).start();

触发中断

  1. t1.interrupt();

线程调度

主流的调度各不相同,

  • 优先级调度—priority schedule
    • 高优先级先执行
    • 线程优先级改变的三种方式
      • 用户指定优先级
      • 系统根据进入等待状态的频繁程度提升或者降低优先级
      • 长时间得不到执行而被提升优先级
  • 轮转法 — Round Robin

    • 让各个线程轮流执行一小段时间

      IO Bound Thread vs CPU bound Thread

  • IO Bound

    • 频繁等待的线程—频繁进入等待状态
    • 容易得到系统的优先级的提升
  • CPU Bound

    • 很少等待的线程
    • 频繁进行大量计算

Time Slice

approximately 20 milliseconds

  • The period of time for which a process is allowed to run in a preemptive multitasking system is generally called the time slice or quantum.
  • The length of each time slice can be critical to balancing system performance vs process responsiveness
    • too long
      • processes will take longer to respond to input
    • too short
      • the scheduler will consume too much processing time
  • An interrupt is scheduled to allow the operating system kernel) to switch between processes when their time slices expire

Semaphore

  • Semaphore 是一个计数信号量,必须由获取它的线程释放。
  • 常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。

CountDownLatch vs CyclicBarrier

  • Latches are for waiting for events;
    • 不能重复利用,只能用一次
  • barriers are for waiting for other threads.
    • cyclicBarrier.await() goes into a blocking state
    • CyclicBarrier的计数器更像一个阀门,需要所有线程都到达,然后继续执行,计数器递增,提供reset功能,可以多次使用
    • 执行完之后会自动reset,可以重复利用,所以叫做CyclicBarrier