多线程基础篇

一、进程与线程
1. 概念:

  进程是操作系统结构的基础,是一个计算机中正在运行的程序实例,是分配给处理器并由处理器执行的一个实体,是由单一顺序执行显示、描述当前状态和一组相关系统资源组成的活动单元。如打开一个浏览器就是启动浏览器进程,打开word文档,就是启动word进程。

  线程是进程执行运算的最小单位,也就是执行调度的基本单元,一个进程中至少包含一个线程。

2. 为什么会有多线程?

  多线程就像是流水线上的各部件加工,彼此独立工作,最后完成设备的生产。也就是说多线程的引用,可以将一个复杂的功能模块拆分成不同的任务来完成,这样也就大大提高了系统的性能,缩短了处理时间,提高了用户体验。


二、线程运行的状态
  • 创建状态(new):新建一个线程对象,实现的方法有四种【继承Thread类、实现Runable接口、实现Callable接口、线程池Executors框架】

 

  • 就绪状态(Runable):线程对象被创建后,调用start()方法,进程进入可运行状态参与cpu时间片的竞争,等待被调度。

 

  • 运行状态(Running):线程对象获取cpu时间片,线程被执行。

 

  • 阻塞状态(Blocked):线程放弃cpu时间片,暂停执行。

   阻塞状态分为三种:

   1)等待阻塞:运行的线程调用o.wait()方法,该线程释放所有资源,进入“等待池”(也称为等待队列)中。进入这个状态后,线程不会自动唤醒,必须依赖其他线程调用o.notify()或o.notifyAll()方法,该线程才被唤醒,进入“锁池”中。

   2)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用时,JVM会将该线程放入“锁池”中。

    3)其他阻塞:运行的线程调用Thread.sleep()或者t.join()方法,或者发出I/O请求时,JVM会将该线程置为阻塞状态,当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead):线程正常执行完毕或抛出一个未处理的异常导致线程的提前结束,线程生命周期结束。

 

 


 

 三、sleep、yield、wait三者之间的区别

1)sleep和yield是Thread下的静态方法。对运行的线程调用sleep()方法,线程让出cpu时间片,睡眠指定的时间,进入阻塞状态,该过程中,线程不会释放对象锁;到期后自动恢复,进入就绪状态,参与cpu时间片的竞争。对运行的线程调用yield()方法,线程让出cpu时间片,进入就绪状态,参与cpu时间片的竞争。

2)wait和notify、notifyAll都是Object类下的方法。对运行的线程调用wait()方法,该线程释放所有资源,同时也会释放对象锁,进入“等待池”。线程不会自动唤醒,必须通过其他线程调用notify()或notifyAll()方法,线程才可被唤醒。(注意:由于notify()只是唤醒一个线程,但是由于不能确定具体唤醒的是哪一个线程,也许需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取对象的锁标记。

3)【延伸】同wait()需要搭配notify()或notifyAll()成对出现一样,suspend()需要搭配resume()使用,区别在于wait()会释放对象的锁,而suspend()不会释放对象的锁。另外,suspend()方法和不指定超时期限的wait()方法的调用都可能产生死锁。


 四、线程中断 
1 try {
2                 Thread.sleep(100);
3 //                wait();
4 //                new Thread().join();
5             } catch (InterruptedException e) {
6                 e.printStackTrace();
7             }

先来看上面一段代码,当调用Thread类下的静态方法sleep,或者调用Thread类下的非静态方法,或者调用Object类下的wait方法,都需要捕获或者抛出中断异常 InterruptedException,那么什么情况下会出现这个异常呢,看看sleep的源码解释(wait和join等同一致):

 * @throws  InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public static native void sleep(long millis) throws InterruptedException;

上面释义:当任何其他线程打断了当前线程,当前线程的中断状态被清除时,该异常就会抛出。很明显,释义已经解释很清楚了,什么是线程中断:
  当前线程被其他线程打断,且当前线程的中断状态被清除,那么这个线程就中断了
那问题来了,什么方法可以去人工干预打断当前这个线程呢?

线程Thread类下有个方法interrupt()来请求终止线程:
 1 /**
 2      * Interrupts this thread.
 3      *
 4      * <p> Unless the current thread is interrupting itself, which is
 5      * always permitted, the {@link #checkAccess() checkAccess} method
 6      * of this thread is invoked, which may cause a {@link
 7      * SecurityException} to be thrown.
 8      *
 9      * <p> If this thread is blocked in an invocation of the {@link
10      * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
11      * Object#wait(long, int) wait(long, int)} methods of the {@link Object}
12      * class, or of the {@link #join()}, {@link #join(long)}, {@link
13      * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
14      * methods of this class, then its interrupt status will be cleared and it
15      * will receive an {@link InterruptedException}.
16      *
17      * <p> If this thread is blocked in an I/O operation upon an {@link
18      * java.nio.channels.InterruptibleChannel InterruptibleChannel}
19      * then the channel will be closed, the thread's interrupt
20      * status will be set, and the thread will receive a {@link
21      * java.nio.channels.ClosedByInterruptException}.
22      *
23      * <p> If this thread is blocked in a {@link java.nio.channels.Selector}
24      * then the thread's interrupt status will be set and it will return
25      * immediately from the selection operation, possibly with a non-zero
26      * value, just as if the selector's {@link
27      * java.nio.channels.Selector#wakeup wakeup} method were invoked.
28      *
29      * <p> If none of the previous conditions hold then this thread's interrupt
30      * status will be set. </p>
31      *
32      * <p> Interrupting a thread that is not alive need not have any effect.
33      *
34      * @throws  SecurityException
35      *          if the current thread cannot modify this thread
36      *
37      * @revised 6.0
38      * @spec JSR-51
39      */
40     public void interrupt() {
41         if (this != Thread.currentThread())
42             checkAccess();
43 
44         synchronized (blockerLock) {
45             Interruptible b = blocker;
46             if (b != null) {
47                 interrupt0();           // Just to set the interrupt flag
48                 b.interrupt(this);
49                 return;
50             }
51         }
52         interrupt0();
53     }
View Code

另外Thread类下也提供了两个方法,静态方法interrupted()和非静态方法isInterrupted,但是这两种方法有些内在的差异:

public boolean isInterrupted()                      

isInterrupted是一个实例方法,主要用于判断当前线程对象的中断标志位是否被标记了,如果被标记了则返回true表示当前已经被中断,否则返回false,调用isInterrupted并不会清除线程对象的中断标识位

public static boolean interrupted()

interrupted是一个静态的方法,用于返回当前线程是否被中断,并且该方法调用结束的时候会清空中断标识位。


 五、守护线程

  守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程,这是它的作用——而其他的线程只有一种,那就是用户线程。所以java中的线程可以分两种:

1、守护线程(典型的GC线程);2、用户线程

守护线程,专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行),此时,连jvm都停止运行了,守护线程当然也就停止执行了。

在Java语言中,守护线程一般具有较低的优先级,它并非只由JVM内部提供,用户在编写程序时也可以自己设置守护线程,例如将一个用户线程设置为守护线程的方法就是在调用start()方法启动线程之前调用对象的setDaemon(true)方法。值得注意的是,当一个线程被设置成守护线程后,即使是finally的语句,也是不会执行的。

posted @ 2020-05-07 23:44  寻尘向阳  阅读(140)  评论(0编辑  收藏  举报