多线程: 指在软件或者硬件上实现多个线程并发执行的技术,具有多线程能力的计算机因为有硬件支持而使其能狗在同一时间内执行多个线程,进而提升整体的处理性能。
进程:在一个操作系统中,每个独立执行的程序可称为一个进程(正在运行的程序)。在多任务操作系统中,表面上支持进程并发执行。例如可以一边听音乐,一边看小说,但实际上这些程序并不是同时执行的,因为CPU具备分时机制,每个时间点只能执行一个程序,只不过由于CPU运行速度非常块,能够在极端的时间内在不同的进程之间进行切换。
线程:每个运行的程序中都是一个进程,在这个进程里包含了多个执行单元,而每个执行单元就是一个线程,而且每个进程中至少包含一个线程,当一个java程序启动时,就会产生一个进程,该进程会默认创建一个线程,称为主线程。
线程的状态和转换:
在java中,要想实现多线程,就必须在主线程中创建新的线程对象。当线程对象创建完成时,线程的生命周期就开始了,当run()方法正常执行完毕,或者出现未捕获的异常或者错误时,线程的生命周期就结束了。线程生命周期的五种状态:
1.新建状态
当线程对象创建成功后,线程就处于新建状态,处于新建状态的线程仅仅是在java虚拟机中分配内存空间,此时还不能运行。
2.就绪状态
当线程对象调用了start()方法后,就进入了就绪状态,处于就绪状态的线程位于可运行池中,具备运行的条件,能否获得CPU的执行权需要等待系统调度。
3.运行状态
当就绪状态的线程获得了CPU的执行权,并开始执行run()方法时,线程处于运行状态。一个线程启动后,他可能不会一直处于运行状态,当运行状态的线程使用玩系统分配的时间后,系统就会剥夺该线程占用的CPU资源,让其他线程获得执行机会。只有处于就绪状态的线程才可能转化为运行状态。
4.阻塞状态
一个正在运行的线程在某种特殊的情况下,如果被人挂起或者需要执行耗时的输入/输出操作时,会让出CPU的执行权进入阻塞状态。进入阻塞状态的线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
5.死亡状态
线程调用stop()方法或者run()方法执行结束后,即处于死亡状态。处于死亡状态的线程不具有继续运行继续运行的能力,也不能转换到其他状态。
线程状态转换图
在java中多线程的实现有以下三种方法:
1.继承Thread类,重写run()方法。
Thread本质上也是实现了Runnable接口的一个实例,他代表一个线程的实例,并且,启动线程的唯一办法就是通过Thread的start()方法。start()方法是一个native(本地)方法,它将启动一个新线程,并执行run()方法。
需要注意的是:当start()方法调用后并不是立即执行多线程代码,而是使得该线程变为可运行状态,什么时候运行多线程的代码是由操作系统决定的。
1 package example1; 2 public class ThreadDemo extends Thread{ 3 public void run() { 4 for(int i=0; i<3; i++){ 5 System.out.println("TreadDemo 类的run()方法执行了"); 6 } 7 8 } 9 } 10 11 public class Example1 { 12 public static void main(String[] args) { 13 ThreadDemo threadDemo = new ThreadDemo(); //穿件线程ThreadDemo的线程对象 14 threadDemo.start(); // 开启线程 15 for(int i=0; i<3; i++){ 16 System.out.println("主方法main()执行了"); 17 } 18 } 19 }
2.实现Runnable接口,并实现该接口的run()方法。
(1)自定义类并实现Runable接口,并实现run()方法。
(2)创建Thread对象,用实现Runable接口的对象作为参数实例化该Thread对象。
(3) 调用Thread的start()方法。
1 package example2; 2 public class ThreadDemo implements Runnable{ 3 public void run() { 4 for(int i=0; i<5; i++){ 5 System.out.println("TreadDemo 类的run()方法执行了"); 6 } 7 } 8 } 9 10 public class Example2 { 11 public static void main(String[] args) { 12 ThreadDemo threadDemo = new ThreadDemo(); //穿件线程ThreadDemo的线程对象 13 Thread thread = new Thread(threadDemo); 14 thread.start(); // 开启线程 15 for(int i=0; i<5; i++){ 16 System.out.println("主方法main()执行了"); 17 } 18 } 19 }
3.实现Callable接口,重写call()方法。
Callable 对象实际上是属于Executor框架中的功能类,Callable接口与Runnable接口类似,但是提供了比Runnable更强大的功能,主要表现为:
(1)Callable可以在任务结果提供一个返回值,Runnable无法提供这个功能。
(2)Callable 中的call()方法可以抛出异常。
(3) 运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果。它提供了检查计算是否完成的方法。由于线程属于异步计算模型,所以无法从其他线程中得到方法的返回值,在这种情况下,就可以使用Future来简述目标call()方法的情况,当调用Future的get()方法获取结果时,当前线程就会阻塞,知道call()方法结束返回结果。
1 package example2_; 2 import java.util.concurrent.Callable; 3 public class CallableTeat implements Callable<String>{ 4 @Override 5 public String call() throws Exception { 6 for(int i=0; i<3; i++){ 7 System.out.println("CallableTeat执行了"); 8 } 9 return "OK"; 10 } 11 } 12 13 import java.util.concurrent.ExecutorService; 14 import java.util.concurrent.Executors; 15 import java.util.concurrent.Future; 16 17 public class Example2 { 18 public static void main(String[] args) { 19 ExecutorService threadPool = Executors.newSingleThreadExecutor(); 20 // 启动线程 21 Future<String> future = threadPool.submit(new CallableTeat()); 22 try { 23 for (int i = 0; i < 3; i++) { 24 System.out.println("主方法main()执行了"); 25 } 26 System.out.println(future.get());// 等待线程结束,并获得返回结果 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } 30 } 31 }
主方法main()执行了
主方法main()执行了
主方法main()执行了
CallableTeat执行了
CallableTeat执行了
CallableTeat执行了
OK