一、常用函数
1.静态方法
1.1 sleep方法:让线程由执行状态进入休眠状态,休眠时间可以指定,单位毫秒,在休眠时间内,线程不会获得cpu执行的机会,也就是说该线程内存空间内的代码不会被执行,等到过了休眠时间,进入就绪状态,获取到cpu的执行时间
源码1,C++本地方法:
public static native void sleep(long millis) throws InterruptedException;
源码2,参数1,休眠时间,参数2,额外睡眠时间,单位纳秒,本质还是调用 native 方法
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && millis < Long.MAX_VALUE) {
millis++;
}
sleep(millis);
}
使用:
Thread.sleep(2*1000); //休眠两秒
Thread.sleep(2*1000,5); //休眠两秒,额外休眠5纳秒
1.2 yield 方法:yield意为让步,由运行状态回到就绪状态,可能进入就绪状态后又被执行。yied让出cpu占有权,让出的时间是不可设定的,cpu可能会忽略这一个提示,这跟设置线程优先级一样,只是起到提示的作用。
源码,是一个C++ 本地方法:
public static native void yield();
class Task1 implements Runnable{
@Override
public void run(){
for(int i =0;i<5;i++){
System.out.println("Task1: " + i);
}
}
}
class Task2 implements Runnable{
@Override
public void run(){
Thread.yield();
for(int i =0;i<5;i++){
System.out.println("--->Task2: " + i);
}
}
}
class Task3 implements Runnable{
@Override
public void run(){
for(int i =0;i<5;i++){
System.out.println("Task3: " + i);
}
}
}
public class Demo3 {
public static void main(String[] args) {
Task1 task1 = new Task1();
Thread thread1 = new Thread(task1);
thread1.setPriority(Thread.MAX_PRIORITY); // 线程1 设置最大优先级
Task2 task2 = new Task2();
Thread thread2 = new Thread(task2);
thread2.setPriority(Thread.MIN_PRIORITY); // 线程2 设置最小优先级
Task3 task3 = new Task3();
Thread thread3 = new Thread(task3);
thread3.setPriority(Thread.MAX_PRIORITY); // 线程3 设置最大优先级
thread1.start();
thread2.start();
thread3.start();
}
}
运行结果:
Task1: 0
--->Task2: 0
--->Task2: 1
Task3: 0
--->Task2: 2
Task1: 1
--->Task2: 3
--->Task2: 4
Task3: 1
Task1: 2
Task3: 2
Task3: 3
Task1: 3
Task3: 4
Task1: 4
分析:虽然线程2 调用yield方法,线程1和线程3有最高优先级,线程2有最低优先级,但是线程2仍然有cpu执行时间,说明yield只是起到对调度器的提示作用。
1.3.currentThread方法: 获取当前线程
源码:
public static native Thread currentThread();
使用:
Thread thead = Thread.currentThread();
2.实例方法
2.1 start:创建并启动线程,本质是C++ 创建线程,调用的是本地方法
源码:
private native void start0();
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
2.2 run 方法:调用start 方法才会真正创建线程,直接调用run方法并不会创建一个新的线程,直接调用相当于一个调用一个普通方法
源码:
@Override
public void run() {
if (target != null) {
target.run();
}
}
2.3 获取和设置线程名称
源码:
public final String getName() {
return name;
}
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}
2.4 设置和获取线程优先级,最大优先级10,最小优先级1,默认优先级5
源码:
public static final int MIN_PRIORITY = 1;
public static final int NORM_PRIORITY = 5;
public static final int MAX_PRIORITY = 10;
public final int getPriority() {
return priority;
}
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
2.5 获取线程是否中断, isInterrupted() 不会重置状态为false,是一个实例方法
源码:
public boolean isInterrupted() {
return interrupted;
}
对比静态方法 interrupted会将中断状态设置为false
源码:
public static boolean interrupted() {
Thread t = currentThread();
boolean interrupted = t.interrupted;
// We may have been interrupted the moment after we read the field,
// so only clear the field if we saw that it was set and will return
// true; otherwise we could lose an interrupt.
if (interrupted) {
t.interrupted = false;
clearInterruptEvent();
}
return interrupted;
}
2.6 中断线程,哪个线程调用该方法,哪个线程就被中断
源码:
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess();
// thread may be blocked in an I/O operation
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupted = true;
interrupt0(); // inform VM of interrupt
b.interrupt(this);
return;
}
}
}
interrupted = true;
// inform VM of interrupt
interrupt0();
}
2.7 设置守护线程和判断是否是守护线程
源码:
public final boolean isDaemon() {
return daemon;
}
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
- 守护线程使用:
守护线程的特点:
后台运行:守护线程在后台运行,不会阻止 JVM 退出。
自动退出:当所有的非守护线程结束时,JVM 会自动退出,即使守护线程还在运行。
不能依赖守护线程进行重要任务:守护线程在 JVM 退出时可能会突然终止,因此不应依赖它们来执行需要确保完成的重要任务。
必须在调用 start() 方法之前调用 setDaemon(true) 方法,否则会抛出 IllegalThreadStateException。
class DaemonThread extends Thread{
@Override
public void run(){
for (int i = 0;i < 8;i++){
System.out.println("守护线程:" + i);
}
System.out.println("守护线程执行结束");
}
}
class FrontThread extends Thread{
@Override
public void run(){
for (int i = 0;i < 3;i++){
System.out.println("--》前台线程:" + i);
}
System.out.println("======前台线程执行完成====!");
}
}
public class Demo06 {
public static void main(String[] args) {
System.out.println("--------主线程开始!---------");
DaemonThread daemonThread = new DaemonThread();
daemonThread.setDaemon(true); // 设置为后台线程
daemonThread.start();
FrontThread frontThread = new FrontThread(); // 前台线程
frontThread.start();
System.out.println("----主线程结束!----");
}
}
输出结果:
--------主线程开始!---------
守护线程:0
守护线程:1
守护线程:2
守护线程:3
守护线程:4
----主线程结束!----
守护线程:5
守护线程:6
守护线程:7
守护线程执行结束
--》前台线程:0
--》前台线程:1
--》前台线程:2
======前台线程执行完成====!
分析:主线程和前台线程已经结束,jvm退出,导致守护线程未完成任务。
2.8 判断线程是否还存活
源码:
public final native boolean isAlive();
2.9 join 方法: 主要功能是使调用线程等待,直到目标线程完成。这对于确保某些任务在目标线程完成之前不会继续执行非常有用。它是实现线程之间协调和同步的一个关键工具。
例如,在一个多线程程序中,主线程可能希望等待所有工作线程完成后再继续执行,这时候 join 方法就显得非常有用。
- 举例:有线程t1和t2以及主线程main,在main中创建并启动了t1和t2,并调用t1和t2的join方法(t1.join()、t2.join()),那么主线程在等待t1和t2执行一段时间后才会继续执行(哪个线程执行完了,就继续从调用join的地方继续执行),若过了指定时间t1和t2还未执行完,主线程还是会继续执行。
- 注意 join 在start方法调用之后执行才生效
- 源码:底层调用的使本地方法 wait()
public final native void wait(long timeoutMillis) throws InterruptedException;
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(final long millis)
throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
}
} else if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && millis < Long.MAX_VALUE) {
millis++;
}
join(millis);
}
- 案例一:不使用join方法
class T1 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("T1 :" + i);
}
}
}
class T2 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("-->T2 :" + i);
}
}
}
public class Demo4 {
public static void main(String[] args) {
System.out.println("-------主线程开始执行---------");
T1 t1 = new T1();
Thread thread1 = new Thread(t1);
T2 t2 = new T2();
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
System.out.println("T1 和 T2 线程执行结束!继续主线程!");
}
}
-------主线程开始执行---------
T1 和 T2 线程执行结束!继续主线程!
T1 :0
-->T2 :0
-->T2 :1
-->T2 :2
-->T2 :3
-->T2 :4
T1 :1
T1 :2
T1 :3
T1 :4
T1 :5
T1 :6
-->T2 :5
-->T2 :6
T1 :7
T1 :8
-->T2 :7
-->T2 :8
T1 :9
-->T2 :9
分析 每个线程都有cpu执行的机会,正常上下文切换
- 案例二:使用join方法,不指定等待时间,这时主线程会等待 t1和t2 两个线程全部执行完,才会继续主线程执行
class T1 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("T1 :" + i);
}
}
}
class T2 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("-->T2 :" + i);
}
}
}
public class Demo4 {
public static void main(String[] args) {
System.out.println("-------主线程开始执行---------");
T1 t1 = new T1();
Thread thread1 = new Thread(t1);
T2 t2 = new T2();
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
try {
// 等待t1和t2全部完成
thread1.join();
thread2.join();
}catch (Exception e){
}
System.out.println("T1 和 T2 线程执行结束!继续主线程!");
}
}
运行结果:
-------主线程开始执行---------
T1 :0
-->T2 :0
T1 :1
-->T2 :1
T1 :2
-->T2 :2
T1 :3
-->T2 :3
T1 :4
-->T2 :4
T1 :5
-->T2 :5
-->T2 :6
-->T2 :7
T1 :6
-->T2 :8
T1 :7
-->T2 :9
T1 :8
T1 :9
T1 和 T2 线程执行结束!继续主线程!
分析:在t1 和 t2全部执行完才继续执行主线程,等效于join(0)
- 案例三 使用join 方法,并指定等待时间
class T1 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("T1 :" + i);
}
try {
Thread.sleep(1*1000); // 模拟处理时间过长 1 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("T1 执行结束");
}
}
class T2 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("-->T2 :" + i);
}
try {
Thread.sleep(50); // 模拟处理时间过长 50 毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("T2 执行结束");
}
}
public class Demo4 {
public static void main(String[] args) {
System.out.println("-------主线程开始执行---------");
T1 t1 = new T1();
Thread thread1 = new Thread(t1);
T2 t2 = new T2();
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
try {
thread2.join(10,1); // 等待 800 毫秒 1 纳秒
System.out.println("-------等了t2 10 毫秒后 主线程继续执行---------");
thread1.join(800); //等待 800 毫秒
}catch (Exception e){
}
System.out.println("等了t1 800 毫秒 继续主线程!");
}
}
-------主线程开始执行---------
T1 :0
-->T2 :0
T1 :1
-->T2 :1
T1 :2
-->T2 :2
T1 :3
-->T2 :3
T1 :4
T1 :5
-->T2 :4
T1 :6
-->T2 :5
T1 :7
T1 :8
-->T2 :6
-->T2 :7
-->T2 :8
-->T2 :9
T1 :9
-------等了t2 10 毫秒后 主线程继续执行---------
T2 执行结束
等了800 毫秒 继续主线程!
T1 执行结束
分析: 线程t1处理逻辑需要 1秒,t2处理任务需要 50 毫秒,等待t1 800毫秒,等待t2 10 毫秒,我们发现虽然 t2 没有在等待时间内完成而是超时了,主线程部分逻辑还是在t2执行完之前执行,而t1线程超时了,所以主线程在等了800毫秒后就执行了,此时t1还未执行完。
- 案例四:join(0)和join()一样,表示等某线程处理完,回到调用join的地方继续执行
class T1 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 2;i++){
System.out.println("T1 :" + i);
}
}
}
class T2 implements Runnable{
@Override
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("-->T2 :" + i);
}
}
}
public class Demo4 {
public static void main(String[] args) {
System.out.println("-------主线程开始执行---------");
T1 t1 = new T1();
Thread thread1 = new Thread(t1);
T2 t2 = new T2();
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
try {
thread1.join(0);
System.out.println("-------等了t1 执行完 主线程继续执行---------");
thread2.join(0);
}catch (Exception e){
}
System.out.println("等了t2 执行完 继续主线程!");
}
}
-------主线程开始执行---------
T1 :0
-->T2 :0
-->T2 :1
-->T2 :2
T1 :1
-->T2 :3
-------等了t1 执行完 主线程继续执行---------
-->T2 :4
-->T2 :5
-->T2 :6
-->T2 :7
-->T2 :8
-->T2 :9
等了t2 执行完 继续主线程!
二、生命周期
- 从操作系统层面上,任何线程一般都具有五种状态,即创建、就绪、运行、阻塞、终止。
- 从java 的getState()的返回值可以知道,有以下六种状态
- NEW
调用 new 线程后,进入NEW 状态 - RUNNABLE
调用start 后进入 RUNNABLE 状态
获取到锁后 会由BLOCKED 进入 RUNNABLE 状态 - TIMED_WAITING
调用sleep(毫秒)、join(非零毫秒)、wait(非零毫秒)进入TIMED_WAITING 状态 - WAITING
join()、join(0)、join(0,0)、wait()、wait(0)、wait(0,0) 会进入 WAITING 状态 - BLOCKED
线程试图进入一个同步块或同步方法,但该对象的监视器锁已被其他线程持有 - TERMINATED
线程异常结束或正常执行完成后,进入 TERMINATED 状态
- NEW
- wait 会释放锁,sleep 不会释放锁