一、什么是线程
线程是并发编程的基本单位,它是程序执行的最小单位,Java程序默认单线程,所有程序都在主线程中运行。一个进程通常包含多个线程,这些线程共享进程的资源。线程间可以通过共享内存进行通信,这使得线程的创建和上下文切换比进程更为轻量和高效。
进程有独立的内存空间和资源,每个进程是相互隔离的。Java应用程序运行在JVM(Java虚拟机)上,JVM本身就是一个进程。
二、如何创建线程
(1)继承Thread类
通过继承Thread类并重写run方法来创建线程。每个Thread对象代表一个线程。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
(2)实现Runable接口
实现Runnable接口并将其传递给Thread对象,这是创建线程的另一种方式。
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable thread is running");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start(); // 启动线程
}
}
(3)使用Callable和Future
Callable接口类似于Runnable,但可以返回一个结果并抛出异常。使用Future来获取结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Callable thread result";
}
}
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
try {
String result = future.get(); // 获取线程执行结果
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown(); // 关闭线程池
}
}
}
(4)使用匿名内部类
直接使用匿名内部类或Lambda表达式创建线程。
public class Main {
public static void main(String[] args) {
// 使用匿名内部类
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Anonymous inner class thread running");
}
});
thread1.start();
// 使用Lambda表达式
Thread thread2 = new Thread(() -> System.out.println("Lambda thread running"));
thread2.start();
}
}
三、线程生命周期
线程包含五个状态:
新建状态:线程对象创建后,进入新建状态。
就绪状态:也称为可执行状态,线程创建后其它线程调用了该对象的start()方法,从而启动该线程。
运行状态:线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
阻塞状态:阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(1) 等待阻塞 — 通过调用线程的wait()方法,让线程等待某工作的完成。
(2) 同步阻塞 — 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(3) 其他阻塞 — 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
四、线程常见操作
(1)线程休眠(sleep())
使当前线程暂停执行一段时间,进入“休眠”状态,让出CPU,但不会释放锁。
(2)线程让步(yield())
让出当前线程的CPU时间片,让系统调度其他相同或更高优先级的线程执行,当前线程进入可运行状态,但不保证让出时间片。
(3)线程加入(join())
等待指定线程执行完毕,当前线程会被阻塞直到目标线程结束或超时。
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println("Thread finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
try {
thread.join(); // 等待thread线程执行完毕
System.out.println("Main thread resumes after join");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(4)线程中断(interrupt())
中断线程,通常用于通知线程停止或改变状态。
(5)设置线程优先级(setPriority())
设置线程的优先级,优先级高的线程更有可能先被执行。
(6)线程等待与通知(wait()、notify()、notifyAll())
线程可以通过wait()方法等待某个条件发生,notify()或notifyAll()方法用于唤醒等待的线程。这些方法必须在同步块(synchronized)内使用。