一、多线程是什么?为什么要用多线程?
介绍多线程之前要介绍线程,介绍线程则离不开进程。
首先 进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元;
线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。一个进程中至少有一个进程。
多线程:一个进程中不只有一个线程。
为什么要用多线程:
①、为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,
如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;
②、进程之间不能共享数据,线程可以;
③、系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;
④、Java语言内置了多线程功能支持,简化了java多线程编程。
二、线程的生命周期:
整理:start(新建)----->就绪(等待jvm的线程调度器)----->run(运行)------>终止
运行期间三种状态: 就绪、阻塞及死亡
新建 :从新建一个线程对象到程序start() 这个线程之间的状态,都是新建状态;
就绪 :线程对象调用start()方法后,就处于就绪状态,等到JVM里的线程调度器的调度;
运行 :就绪状态下的线程在获取CPU资源后就可以执行run(),此时的线程便处于运行状态,
运行状态的线程可变为就绪、阻塞及死亡三种状态。
等待/阻塞/睡眠 :在一个线程执行了sleep(睡眠)、suspend(挂起)等方法后会失去所占有的资源,从而进入阻塞状态,
在睡眠结束后可重新进入就绪状态。
终止 :run()方法完成后或发生其他终止条件时就会切换到终止状态。
三.三种创建线程方法
三种方法对比:
继承Thread:线程代码存放在Thread子类run方法中。
优势:编写简单,可直接用this.getname()获取当前线程,不必使用Thread.currentThread()方法。
劣势:已经继承了Thread类,无法再继承其他类。
实现Runnable:线程代码存放在接口的子类的run方法中。
优势:避免了单继承的局限性、多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。
劣势:比较复杂、访问线程必须使用Thread.currentThread()方法、无返回值。
实现Callable:
优势:有返回值、避免了单继承的局限性、多个线程可以共享一个target对象,非常适合多线程处理同一份资 源的情形。
劣势:比较复杂、访问线程必须使用Thread.currentThread()方法
建议使用实现接口的方式创建多线程。
代码如下:
package thread.basic;
/**
* @Package:thread.basic
* @Class:Thread
* @Description: TODO
* @Author:何仲奇
* @Date:Created in 2019-03-13 23:14
* @Company:
* @Version:
* @Modified By:
*/
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 创建线程三种方式:
* 1.继承Thread类
* 2.实现Runnable接口
* 3.通过Callable和Future创建线程
*/
public class ThreadMethod {
public static void main(String[] args) {
//第一种:继承Thread类
ThreadDemo td = new ThreadDemo("td");
ThreadDemo ts = new ThreadDemo("ts");
td.start();
ts.start();
//主线程
for (int i = 0; i < 5; i++) {
System.out.println("main" + ":run" + i);
}
//第二种:实现Runnable接口
RunnableDemo runnableDemo = new RunnableDemo();
//不要显式创建线程,请使用线程池。
//线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
//说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。
// 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题
Thread t1 = new Thread(runnableDemo);
Thread t2 = new Thread(runnableDemo);
//开启线程并调用run方法。
t1.start();
t2.start();
//第二种优化:后续补充
//第三种方法:通过Callable和Future创建线程
//1.创建对象
CallableFutrueDemo call = new CallableFutrueDemo();
//2.创建FutureTask,并启动线程(使用FutureTask包装CallableTest对象)
FutureTask<String> ft = new FutureTask<String>(call);
for (int i = 0; i <= 2; i++) {
//输出主线程
System.out.println(Thread.currentThread().getName() + "主线程的i为:" + i);
if(2 == i) {
//不要显式创建线程,请使用线程池。
Thread t = new Thread(ft,"子线程");
t.start();
}
}
//获取并输出子线程call()方法的返回值
try {
//get()方法获取返回结果
System.out.println("子线程的返回值为" + ft.get());
}catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
/**
*1.继承Thread类
* 步骤:
* ①、定义类继承Thread;
* ②、复写Thread类中的run方法;
* 目的:将自定义代码存储在run方法,让线程运行
* ③、调用线程的start方法:
* 该方法有两步:启动线程,调用run方法。
*/
class ThreadDemo extends Thread {
/**
* 构造方法,设置线程名
* @param name
* */
public ThreadDemo(String name) {
super(name);
}
@Override
public void run(){
for (int i = 0; i < 5; i++) {
//currentThread() 获取当前线程对象(静态)。
// getName() 获取线程名称。
System.out.println("第一种:"+"线程名:"+this.getName() + ";线程对象:" +this.currentThread());
}
}
}
/**
* 2、实现Runnable接口: 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run 的无参方法。
*
* 实现步骤: ①、定义类实现Runnable接口
*
* ②、覆盖Runnable接口中的run方法
*
* 将线程要运行的代码放在该run方法中。
*
* ③、通过Thread类建立线程对象。
*
* ④、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
*
* 自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程执行指定对象的run方法就要先明确run方法所属对象
*
* ⑤、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
*/
class RunnableDemo implements Runnable{
private int tick = 10;
/**
* 覆盖Runnable接口中的run方法,并将线程要运行的代码放在该run方法中。
*/
@Override
public void run() {
while (true) {
if(tick > 0){
//Thread.currentThread().getName()
System.out.println("第二种" +Thread.currentThread().getName() + "..." + tick--);
}
}
}
}
/**
* 3、通过Callable和Future创建线程:
*
* 实现步骤:①、创建Callable接口的实现类,并实现call()方法,该方法将作为线程执行体,且具有返回值。
*
* ②、创建Callable实现类的实例,使用FutrueTask类进行包装Callable对象,
* FutureTask对象封装了Callable对象的call()方法的返回值
*
* ③、使用FutureTask对象作为Thread对象启动新线程。
*
* ④、调用FutureTask对象的get()方法获取子线程执行结束后的返回值。
*/
class CallableFutrueDemo implements Callable<String> {
/**复写call() 方法,call()方法具有返回值
* @return
* @throws Exception
*/
@Override
public String call() throws Exception {
System.out.println("第三种:" + Thread.currentThread().getName() + "的变量值为:");
return Thread.currentThread().getName();
}
}