进程是程序的一次动态执行,需要经历从代码加载,代码执行以及执行完毕的一个完整的过程。由于 CPU
的具备分时机制,也即把 CPU
划分为无数个小的时间片,每个时间片去执行一个进程(程序),让我们感觉程序在同时运行一样。
线程是进程中的一个执行单元,负责执行程序中的代码。一个进程可以包含多个线程,它们共享进程的资源。线程之间共享同一份内存,因此线程间通信更加容易。
例如,我们在一个 World 里在打字的同时,World 还可以为我们做拼写检查。
通过继承 Thread
类,可以创建一个线程类,然后重写 run()
方法,该方法包含线程要执行的代码
实例代码:
public class Demo {
public static void main(String[] args) {
// 创建线程
ThreadDemo thread1 = new ThreadDemo();
ThreadDemo thread2 = new ThreadDemo();
// 启动线程
thread1.start();
thread2.start();
}
}
class ThreadDemo extends Thread {
public void run() {
// 线程执行的任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value " + i);
}
}
}
输出结果:
21 Value 0
20 Value 0
20 Value 1
21 Value 1
21 Value 2
21 Value 3
21 Value 4
20 Value 2
20 Value 3
20 Value 4
通过实现 Runnable
接口,可以将线程的任务封装在一个类中,然后创建 Thread
对象并将该类的实例传递给 Thread
的构造函数
实例代码:
public class Demo {
public static void main(String[] args) {
// 创建线程
Thread thread1 = new Thread(new RunnableDemo());
Thread thread2 = new Thread(new RunnableDemo());
// 启动线程
thread1.start();
thread2.start();
}
}
class RunnableDemo implements Runnable {
public void run() {
// 线程执行的任务
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value " + i);
}
}
}
输出结果:
20 Value 0
20 Value 1
21 Value 0
21 Value 1
21 Value 2
21 Value 3
21 Value 4
20 Value 2
20 Value 3
20 Value 4
在 Java 8 及以后的版本,可以使用 Lambda
表达式简化创建线程的代码
实例代码:
public class Demo {
public static void main(String[] args) {
// 使用Lambda表达式创建线程1
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value " + i);
}
});
// 使用Lambda表达式创建线程2
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value " + i);
}
});
// 启动线程
thread1.start();
thread2.start();
}
}
输出结果:
20 Value 0
21 Value 0
20 Value 1
20 Value 2
20 Value 3
21 Value 1
20 Value 4
21 Value 2
21 Value 3
21 Value 4
无论采用哪种方式,都需要调用 start()
方法来启动线程。 start()
方法会在一个新的线程中调用 run()
方法。避免直接调用 run()
方法,因为这样并不会在新线程中执行,而只是在当前线程中作为普通的方法调用。
推荐使用 Runnable
接口的方式,因为 Java 不支持多重继承,而通过实现接口更为灵活可以避免这个限制。 此外,Runnable
接口可以被多个线程共享,提高代码的可复用性。
多线程的状态主要包括以下几种:
这些状态构成了线程的生命周期,线程在这些状态之间来回转换。
示例代码:
public class ThreadStateDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
// 在新建状态
printThreadState("New");
// 启动线程,进入就绪状态
Thread.yield();
printThreadState("Runnable");
// 线程获取锁,进入运行状态
synchronized (ThreadStateDemo.class) {
printThreadState("Running");
// 线程调用wait(),进入等待状态
try {
ThreadStateDemo.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadState("Waiting");
// 等待超时后重新进入运行状态
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadState("Running");
}
});
// 新建状态
printThreadState("New");
// 启动线程,进入就绪状态
thread.start();
Thread.yield();
printThreadState("Runnable");
// 主线程等待一会儿
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 线程被唤醒,进入就绪状态
synchronized (ThreadStateDemo.class) {
ThreadStateDemo.class.notify();
}
Thread.yield();
printThreadState("Runnable");
// 主线程等待线程执行完毕
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终止状态
printThreadState("Terminated");
}
private static void printThreadState(String state) {
long tid = Thread.currentThread().getId();
System.out.println("Thread State: " + state + ", Thread ID: " + tid);
}
}
输出结果:
Thread State: New, Thread ID: 1
Thread State: Runnable, Thread ID: 1
Thread State: New, Thread ID: 20
Thread State: Runnable, Thread ID: 20
Thread State: Running, Thread ID: 20
Thread State: Waiting, Thread ID: 20
Thread State: Runnable, Thread ID: 1
Thread State: Running, Thread ID: 20
Thread State: Terminated, Thread ID: 1
在这个例子中,通过一个新建的线程演示了新建、就绪、运行、等待等状态的转换。注意到在等待状态时,通过notify()
方法唤醒线程,然后等待超时后重新进入运行状态。最后,主线程等待新建的线程执行完毕,线程进入终止状态。这个例子模拟了多线程状态的典型转换过程。
下面是线程状态之间的转换:
start()
方法。yield()
方法,主动让出CPU时间。sleep()
等方法。run()
方法或者因为异常退出了 run()
方法。notify()
、notifyAll()
方法,或者等待的时间到了。notify()
、notifyAll()
方法。多线程编程是一门复杂而有趣的艺术,合理的多线程设计能够提高程序的性能和响应性。在进行多线程编程时,了解线程的基本概念、合理使用同步和通信机制,以及注意最佳实践,将有助于编写出高质量、可维护的多线程程序。