JAVA 多线程编程 零基础到精通(一)
一、概念
1. 进程
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。【1】
2. 线程
线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。【1】
3. 多线程的意义
多线程的存在,其实是为了提高其所属进程对CPU的使用效率。因为,每一个线程都会分得CPU的时间片(关于CPU时间片的相关概念,请参考《CPU时间片》),而一个进程拥有的线程多,那么这个进程分到的CPU时间片就多。
4. 小结
- 进程是资源分配的最小单位,线程是程序执行的最小单位。【1】
- 进程是单独存在,其有自己的独立地址空间。当启动一个进程时,操作系统就会给该进程分配一块独立的内存和系统资源。线程是不能单独存在的,其必须依附于一个进程而存在。同一个进程中的不同线程之间可以共享该进程中的内存。
- 对于操作系统来说,创建一个进程的开销要远远大于创建一个线程的开销。但,即便如此,也不要在一个进程中创建很多线程,因为切换线程对于操作系统来说也是有很大开销的。
- 进程间通信是非常困难地,其有较高的技术门槛,详情请参见IPC(进程间通信)。同一进程下的不同线程之间的通信相对于进程间的通信来说要方便很多,因为它们共享全局变量、静态变量等数据。
- 一个线程若崩溃,很可能会影响同一进程下的其它线程,进而造成整个进程崩溃。而一个进程崩溃是不会对其它进程造成影响。
二、JAVA的线程状态
如下图所示,JAVA的线程拥有六个状态:NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、TIMED_WAITING(超时等待)、WAITING(等待)、TERMINATED(终止)。【2】
图 JAVA线程中的状态转换图
六个状态的解释如下【2】:
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 可运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“可运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
- 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
- 终止(TERMINATED):表示该线程已经执行完毕。
三、Java中的三种创建线程的方法
1. 通过实现 Runnable 接口
这是Java中最简单的创建线程的方法,即创建一个实现 Runnable 接口的类。该类必须重写run( )方法。 示例如下:
public class ThreadDemo implements Runnable{
public void run() {
System.out.println("This Thread is : " + Thread.currentThread().getName());
try {
//让线程睡眠1秒
Thread.sleep(1000L);
}catch(Exception e) {
System.out.println("The Thread " + Thread.currentThread().getName() + " throws exception.");
}
System.out.println("The Thread " + Thread.currentThread().getName() + " will be exit.");
}
}
主线程代码如下:
public class App
{
public static void main( String[] args )
{
Thread demo = new Thread(new ThreadDemo(), "Demo Thread");
demo.start();
}
}
控制台输出结果如下:
This Thread is : Demo Thread
The Thread Demo Thread will be exit.
2. 通过继承 Thread 类本身
第二种方法是创建一个新的类,该类继承 Thread 类,并实现run()方法。 示例如下:
public class ThreadDemo extends Thread {
public ThreadDemo(String name) {
this.setName(name);
}
public void run() {
System.out.println("This Thread is : " + this.getName());
try {
//让线程睡眠1秒
Thread.sleep(1000L);
}catch(Exception e) {
System.out.println("The Thread " + this.getName() + " throws exception.");
}
System.out.println("The Thread " + this.getName() + " will be exit.");
}
}
主线程代码如下:
public class App
{
public static void main( String[] args )
{
ThreadDemo demo = new ThreadDemo("Thead Demo");
demo.start();
}
}
控制台输出结果如下:
This Thread is : Demo Thread
The Thread Demo Thread will be exit.
3. 通过 Callable 和 Future 创建线程
第三种方法可以实现具有返回值的线程。 示例如下:
public class App implements Callable<Integer>
{
public static void main( String[] args )
{
App demo = new App();
FutureTask<Integer> ft = new FutureTask<Integer>(demo);
Thread thread = new Thread(ft, "Thread Demo");
thread.start();
try {
Integer result = ft.get();
System.out.println("The Thread's result is :" + result);
} catch(Exception e) {
e.printStackTrace();
}
}
public Integer call() throws Exception {
return 100;
}
}
控制台输出结果如下:
The Thread's result is :100
引用
【1】. 一道面试题:说说进程和线程的区别
【2】. Java线程的6种状态及切换(透彻讲解)