线程中止
- 设置标记位(受限与线程中业务逻辑有循环条件)
thread.setFlag(false); - 使用 stop(),不安全,已经被废弃
thread.stop(),因为 stop()方法会解除由线程获得的所有锁,当在一个线程对象上调用 stop()方法时,这个线程对象所运行的线程会立即停止 - 使用 Thread 类的 interrupt() 方法中断线程
调用 interrupt()方法只会给线程设置一个为 true 的终端标志,而设置之后,则根据线程当前状态进行不同的后续操作
线程的五大状态及其转换
线程的五大状态分别为:
创建状态(new)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Dead)。
五大状态之间的关系图:
(1)新建状态:即单纯的创建一个线程
创建线程有三种方式;
1.集成 Thread 类创建线程
使用集成 Thread 类创建线程时,首先需要创建一个类集成 Thread 类并覆写 Thread 类
run()方法,在 run()方法中,需要写线程要执行的任务
但是调用 run()方法并不是真正的启动一个线程,真正的启动线程,需要调用的时 Thread 类的 start()方法,而 start()方法会自动调用 run()方法,从而启动一个线程。
说明: main 方法其实也是一个线程,是该进程的主线程。但是在使用多线程技术时,代码的运行结果与代码调用的顺序无关,因为线程是一个子任务,CPU 以不确定的方式或者说以随机的时间来调用线程中的 run()方法,所以会出现每次运行结果不同的情况。
2.实现 Runnable 接口创建线程
Thread 类的核心功能就是进行线程的启动,但一个类为了实现多线程直接取继承 Thread 类时出现的问题就是:单集成的局限性!所以 Java 中还提供了另一种实现多线程的方法:实现 Runnable 接口来创建多线程。
注意: 启动一个线程的唯一方法就是调用 Thread 类的 start()方法,抓住这点去建立与 Thread 类之间的关系。
Runnable 接口中只有一个抽象方法就是 run()方法。那怎么使用 Runnable 接口去创建线程呢?(如何执行 start()方法呢)
第一步:定义一个类实现 Runnable 接口的抽象方法 run()方法。 此时 Thread 类有一个 Thread 类的构造方法 public Thread(Runnable target)方法,参数用于接收 Runnable 接口的实例化对象,所以在 Runnable 接口与 Thread 类之间就建立起了联系,从而可以调用 Thread 类的 start()方法启动一个线程。所以
第二步:利用 Thread 类的 public Thread(Runnable target)构造方法与 Runnable 接口建立关系实例化 Thread 类的对象;
第三步:调用 Thread 类的 start()方法启动线程。
3.实现 Callable 接口创建线程
Runnable 接口的 run()方法没有返回值,而 Callable 接口中的 call()方法有返回值,若某些线程执行完成后需要一些返回值的时候,就需要用 Callable 接口创建线程。
再次强调:启动一个线程的唯一方法就是调用 Thread 类的 start()方法,那么,要想通过实现 Callable 接口创建线程,就需要找到 Callable 接口与 Thread 类之间的关系。首先 FutureTask 类提供了构造方法 public FutureTask(Callable<V> callable
)方法,而 FutureTask 类又实现了 RunnableFuture 接口,而 RunnableFuture 接口又继承了 Runnable 接口,再通过 public Thread(Runnable runnable)构造方法,使 Callable 接口与
Thread 类之间建立了联系。
所以使用 Callable 接口创建线程的步骤如下:
1 | // 1.定义一个类MyThread实现Callable接口,从而覆写call()方法 |
(2)就绪状态
在创建了线程之后,调用了 Thread 类的 start()方法来启动一个线程,即表示线程进入了就绪状态!
(3)运行状态
当线程获得 CPU 时间,线程才从就绪状态进入到运行状态!
(4)阻塞状态
线程进入运行状态后,可能由于多种原因让线程进入阻塞状态,如:调用 sleep()方法,让线程睡眠,调用 wait()方法让线程等待,调用 join()方法以及阻塞式 IO 方法
阻塞情况分为三种:
(一)等待阻塞:运行中(running)的线程执行 o.wait()方法,JVM 会把该线程放入等待队列(waiting queue)中。
(二)同步阻塞:运行中(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。
(三)其他阻塞:运行中(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入可运行状态。
阻塞方法的异同:
- sleep、yield 方法是静态方法;作用是当前执行的线程;
- yield 方法释放了 cpu 的执行权,但是保留了争夺 cpu 的资格,这也意味着,该线程可能马上会再次执行。yield()只能使同优先级或更高优先级的线程有执行的机会。
- wait 释放 CPU 资源,同时释放锁,只有执行 notify/notifyAll()时,才会唤醒处于等待的线程,然后继续往下执行,直到执行完 synchronized 代码块的代码火种中途再次遇到 wait()。一般配合 synchronized 关键字使用,即,一般在 synchronized 同步代码块中使用 wait()、notify/notifyAll 方法。
- sleep 释放 CPU 资源,但不释放锁
- join 把调用该方法的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程 B 中调用了线程 A 的 join()方法,直到线程 A 执行完毕后,才会继续执行线程 B
(5)死亡状态
run()方法的正常退出,就是让线程进入到死亡状态,还有当一个异常未被捕获而终止了 run()方法的执行也会进入到死亡状态!
设置或获取多线程的线程名称的方法
由于在一个进程中可能有多个线程,而多线程的运行状态又是不确定的,所以在多线程操作中需
要有一个明确的标识符标识出当前线程对象的信息,这个信息往往通过线程的名称来描述。在
Thread 类中提供了一些设置或者获取线程名称的方法:
- 创建线程时设置线程的名称;
public Thread(Runnable target,String name) - 设置线程名称的普通方法;
public final synchronized void setName(String name) - 取得线程名称的普通方法;
public final String getName()