1.线程与进程关系:
- 进程(process)
- 所有运行中的任务通常对应一个进程。
- 当一个程序进入内存运行时,即变成一个进程。
- 进程是处于运行过程中的程序。
- 进程是系统进行资源分配和调度的一个独立单位。
- 独立性:每个进程都有自己的地址空间,在没有经过进程本身允许的情况下,进程不可直接访问其他进程的地址空间。
- 动态性:程序是一个静态的指令集合,而进程是一正在系统中活动的指令集和,进程具有自己的声明周期和不同的状态。
- 并发性:多个进程可在单个处理器上并发运行,多个进程之间不会互相影响。
- 线程(thread)
- 线程也被称为轻量级进程,线程是进程的执行单元。
- 线程是进程的组成部分,一个进程可以有多个线程。
- 一个线程必须有一个父进程。
- 线程可以拥有自己的堆栈、计数器和自己局部变量,但不能拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部系统资源。
- 线程可以完成一定的任务,可以与其他线程共享父进程中的共享变量即部分环境,相互之间协同完成进程要完成的任务。
- 线程是独立运行的。要确保线程不会妨碍同一进程里的其他线程。
- 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发运行。
- 多线程存在于一个应用程序中,让一个应用程序中可以有多个执行部分同时执行,但操作系统无需讲多个线程看作多个独立的应用,对多线程实现调度和管理以及资源分配。线程的调度和管理有进程本身负责完成。
- 一个程序运行后至少有一个进程,一个进程中可以包含多个线程,但至少要包含一个线程。
2.线程的优势
- 进程之间不能共享内存,但线程之间共享内存非常容易
- 系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小的多,因此使用多线程来实现多任务并发比多进程的效率高。
3.线程的创建
- 继承Thread类创建线程类
-
①定义Thread类的子类,并重写该类的run()方法,该run()方法的方法提就是线程 需要完成的任务,因此run()方法成为线程的执行体。 ②创建Thread子类的实例,及创建了线程对象。 ③调用线程对象的start()方法来启动线程。 public class FirstThread extends Thread { private int i ; // 重写run方法,run方法的方法体就是线程执行体 public void run() { for ( ; i < 100 ; i++ ) { // 当线程类继承Thread类时,直接使用this即可获取当前线程 // Thread对象的getName()返回当前该线程的名字 // 因此可以直接调用getName()方法返回当前线程的名 System.out.println(getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { // 调用Thread的currentThread方法获取当前线程 System.out.println(Thread.currentThread().getName() + " " + i); if (i == 20) { // 创建、并启动第一条线程 new FirstThread().start(); // 创建、并启动第二条线程 new FirstThread().start(); } } } }
- 实现Runnable接口创建线程类
-
①定义Runnable接口的实现类 ②创建Runnable实现类的实例 ③调用线程对象的start()方法启动多线程 public class SecondThread implements Runnable { private int i ; // run方法同样是线程执行体 public void run() { for ( ; i < 100 ; i++ ) { // 当线程类实现Runnable接口时, // 如果想获取当前线程,只能用Thread.currentThread()方法。 System.out.println(Thread.currentThread().getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 20) { SecondThread st = new SecondThread(); // ① // 通过new Thread(target , name)方法创建新线程 new Thread(st , "新线程1").start(); new Thread(st , "新线程2").start(); } } } }
- 使用Callable和Future创建线程
-
①创建Callable接口的实现类,并实现call()方法,将call()方法作为线程执行体,
且该call()方法有返回值,在创建Callable实现类的实例。java8开始可使用Lambda表达式创建Callable对象。 public class ThirdThread { public static void main(String[] args) { // 创建Callable对象 ThirdThread rt = new ThirdThread(); // 先使用Lambda表达式创建Callable<Integer>对象 // 使用FutureTask来包装Callable对象 FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)() -> { int i = 0; for ( ; i < 100 ; i++ ) { System.out.println(Thread.currentThread().getName() + " 的循环变量i的值:" + i); } // call()方法可以有返回值 return i; }); for (int i = 0 ; i < 100 ; i++) { System.out.println(Thread.currentThread().getName() + " 的循环变量i的值:" + i); if (i == 20) { // 实质还是以Callable对象来创建、并启动线程 new Thread(task , "有返回值的线程").start(); } } try { // 获取线程返回值 System.out.println("子线程的返回值:" + task.get()); } catch (Exception ex) { ex.printStackTrace(); } } }
4.创建线程三种方式的比较
**实现Runnable接口与实现Callable接口方式基本相同,试试Calllable接口里定义的方法有返回值,可以声明抛出异常。
- 创建线程一般推荐使用Runnable几口、Callable接口的方式。