Java 线程是程序中的执行流。在 Java 中,线程是由 java.lang.Thread
类的实例来表示和控制的。Java 线程可以通过以下两种主要方式来创建:
- 继承
Thread
类:创建一个新的类继承Thread
类,并重写它的run()
方法。 - 实现
Runnable
接口:创建一个类实现Runnable
接口,并实现run()
方法。然后,创建Thread
类的实例并将Runnable
对象作为参数传递给它。
继承 Thread 类
】
class MyThread extends Thread {
public void run() {
// 任务代码
System.out.println("线程运行中...");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // 启动线程
}
}
实现 Runnable 接口
class MyRunnable implements Runnable {
public void run() {
// 任务代码
System.out.println("线程运行中...");
}
}
public class RunnableExample {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
t1.start(); // 启动线程
}
}
线程的生命周期
Java 线程在其生命周期中可以处于以下几种状态:
- 新建(New):创建后尚未启动的线程处于这个状态。
- 就绪(Runnable):调用
start()
方法后,线程处于就绪状态,等待调度器分配时间片运行。 - 运行(Running):线程获取 CPU 执行权并执行
run()
方法。 - 阻塞(Blocked):线程因为某些原因放弃了 CPU 执行权,并暂时停止运行。
- 等待阻塞:运行的线程执行
wait()
方法,JVM 会把该线程放入等待池中。 - 同步阻塞:运行的线程在获取 synchronized 同步锁失败(因为其他线程正在使用锁)时,会进入同步阻塞状态。
- 其他阻塞:运行的线程执行
Thread.sleep()
或join()
方法,或者发出 I/O 请求时,线程会进入阻塞状态。
- 等待阻塞:运行的线程执行
- 等待(Waiting):处于这个状态的线程需要等待其他线程执行特定的(通知)动作。
- 计时等待(Timed Waiting):类似于等待状态,但有最大等待时间,例如
sleep()
方法。 - 终止(Terminated):线程的
run()
方法执行完毕或者因异常退出了run()
方法,线程结束生命周期。
线程的重要方法
start()
:启动线程,使其变为就绪状态。run()
:线程在获取 CPU 执行权时执行的方法。join()
:使其他线程等待当前线程完成。interrupt()
:中断线程。sleep(long millis)
:使当前正在执行的线程暂停执行一段时间(给其他线程执行的机会)。wait()
:使当前线程进入等待状态。notify()
/notifyAll()
:唤醒在此对象监视器上等待的单个线程或所有线程。
线程同步
在多线程环境中,同步是非常重要的,它涉及到如何安全地让多个线程访问共享资源。Java 提供了几种同步机制:
- Synchronized 方法:通过在方法声明上添加
synchronized
关键字,可以确保同一时刻只有一个线程执行该方法。 - Synchronized 块:可以减小同步的范围,只锁定必要的代码区域。
- Lock 接口:
java.util.concurrent.lockJava 线程是程序中的执行流。在 Java 中,线程是由
java.lang.Thread` 类的实例来表示和控制的。Java 线程可以通过以下两种主要方式来创建:
- 继承
Thread
类:创建一个新的类继承Thread
类,并重写它的run()
方法。 - 实现
Runnable
接口:创建一个类实现Runnable
接口,并实现run()
方法。然后,创建Thread
类的实例并将Runnable
对象作为参数传递给它。
以下是两个简单的 Java 程序示例,分别演示了如何使用继承
Thread
类和实现Runnable
接口来创建线程。示例 1: 继承 Thread 类
class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getId() + " Value " + i); } } public static void main(String args[]) { MyThread t1 = new MyThread(); t1.start(); MyThread t2 = new MyThread(); t2.start(); } }
这段代码创建了一个
MyThread
类,它继承自Thread
类,并重写了run()
方法。在run()
方法中,我们打印了当前线程的 ID 和一个循环变量。在main
方法中,我们创建了两个MyThread
的实例,并通过调用start()
方法来启动两个线程。示例 2: 实现 Runnable 接口
class MyRunnable implements Runnable { public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getId() + " Value " + i); } } public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread t1 = new Thread(myRunnable); t1.start(); Thread t2 = new Thread(myRunnable); t2.start(); } }
在这个示例中,我们创建了一个
MyRunnable
类,它实现了Runnable
接口,并实现了run()
方法。在run()
方法中,我们同样打印了当前线程的 ID 和一个循环变量。在main
方法中,我们创建了MyRunnable
的一个实例,并将其作为参数传递给两个Thread
对象。然后我们启动这两个线程。两个示例中,当调用
start()
方法时,每个线程都会在其自己的调用栈中执行run()
方法。结果是并发执行的线程,每个线程都打印出一系列数字。
另外两个示例,这次我们将看到如何使用 synchronized
关键字同步线程,以及如何使用 wait()
和 notify()
方法来协调线程间的通信。
示例 3: 使用 synchronized 同步方法
class Counter {
private int count = 0;
// 同步方法,保证每次只有一个线程可以访问
public synchronized void increment() {
count++; // count = count + 1;
}
public int getCount() {
return count;
}
}
class CounterThread extends Thread {
private Counter counter;
public CounterThread(Counter counter){
this.counter = counter;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
counter.increment();
System.out.println(Thread.currentThread().getName() + " count: " + counter.getCount());
}
}
public static void main(String args[]) {
Counter counter = new Counter();
CounterThread t1 = new CounterThread(counter);
CounterThread t2 = new CounterThread(counter);
t1.start();
t2.start();
}
}
这个程序中,Counter
类有一个同步方法 increment()
,它会递增 count
变量。我们创建了两个线程,它们共享同一个 Counter
实例。因为 increment
方法是同步的,所以我们不会遇到线程安全问题。
示例 4: 使用 wait() 和 notify() 方法
class Message {
private String msg;
public Message(String str){
this.msg=str;
}
public String getMsg() {
return msg;
}
public synchronized void setMsg(String str) {
this.msg = str;
notify();
}
public synchronized void waitForMessage() {
while(this.msg == null){
try {
wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Waiter implements Runnable {
private Message msg;
public Waiter(Message m){
this.msg=m;
}
public void run() {
synchronized (msg) {
msg.waitForMessage();
System.out.println("Waiter Thread got the message: " + msg.getMsg());
}
}
}
class Notifier implements Runnable {
private Message msg;
public Notifier(Message m){
this.msg=m;
}
public void run() {
synchronized (msg) {
msg.setMsg("Notifier work done");
}
}
}
public class WaitNotifyDemo {
public static void main(String[] args) {
Message msg = new Message("process it");
Waiter waiter = new Waiter(msg);
new Thread(waiter, "waiter").start();
Notifier notifier = new Notifier(msg);
new Thread(notifier, "notifier").start();
System.out.println("All the threads are started");
}
}
在这个程序中,我们有一个 Message
类,它包含 waitForMessage()
和 setMsg()
方法,这两个方法都是同步的。Waiter
线程在 Message
对象上调用 wait()
方法等待消息,而 Notifier
线程在相同的对象上调用 notify()
方法。当 Notifier
设置了消息后,它会唤醒正在等待的线程。