1、什么是多线程
进程的执行需要依赖线程。线程是进程的最小执行单位,每一个进程中最少有一个线程。
例如:使用某网盘下载时,当我们同时进行下载和上传操作时(同一时间同时进行),就使用到了多线程,上传是一个线程,下载也是一个线程。
使用聊天软件时,同时在收消息和发消息也用到了多线程。
我们的java程序执行main方法时就是执行了一条线程。
2、如何创建多线程(多个方法)
在java中使用的是java.lang.Thread类的对象来代表线程的。
方法一:继承Thread类创建线程
写一个main方法(main方法是由一条默认的主线程执行的)
新建一个子类MyThread并继承Thread类
在MyThread类中我们需要重写Thread中的run方法,run方法中就是我们需要在新线程中执行的代码
在run方法中我们写上一段测试代码
然后我们在main方法中创建我们的子线程对象,并调用start方法启动我们的子线程
为了对比,我们在main方法中也执行一段测试代码
运行main方法
多次执行后可以发现执行结果顺序是随机的。因为两个线程同时执行,谁先谁后不是固定的。
步骤总结:
(1)创建一个子类MyThread继承Thread类,并且重写run()方法
(2)在main方法中创建MyThread对象
(3)调用start()方法启动线程
注意:
(1)子线程的执行必须调用的是start()方法,而不是run()方法。如果调用run()方法,调用的是子线程中的run()方法,而不是启动线程,则仍然是单线程执行的。
(2)子线程启动必须在主线程任务之前。如果子线程的启动在主线程之后,主线程任务执行完之后才去启动的子线程,没有实现真正的多线程。
优缺点:
(1)编码简单
(2)线程类已经继承了Thread类,无法继承其他类,不利于扩展。
方法二:实现Runnable接口创建线程
步骤:
(1)声明一个MyRunnable任务类实现Runnable接口,重写里面的run()方法。
(2)在main方法中创建MyRunnable任务对象myRunnable
(3)新建一个线程对象,将myRunnable作为参数,调用start()方法即可
优缺点:
(1)优点:任务类只是实现接口,还可以继承其他类,实现其他接口,可扩展性强
(2)缺点:需要多一个Runnable对象
扩展:匿名内部类写法
简化写法1:
简化写法2(lambda表达式):
方法三、实现Callable接口创建线程
前面两种线程创建方式存在的问题---->线程执行完后有一些数据要返回,重写的run方法都不能直接返回结果
而利用Callable接口、FutureTask类实现的可以返回数据
步骤:
(1)创建任务对象
定义一个类实现Callable方法,重写call方法,封装要执行的代码和返回的数据
将Callable类型的对象封装程FutureTask(线程任务对象)
(2)把线程任务对象交给Thread对象
(3)调用Thread对象的start方法启动线程
(4)线程执行完之后,通过FutureTask对象的get方法获取线程任务执行的结果
优缺点:
(1)优点:线程任务只是实现接口,可以继承和实现其他接口,扩展性强。可以在线程结束后获取结果
(2)缺点:代码复杂一点
Thread提供的常用的线程方法
public void run() | 线程的任务发放 |
public void start() | 启动线程 |
public String getName() | 获取当前线程的名称,线程默认名称是Thread-索引 |
public void setName() | 为线程设置名称 |
public static Thread currentThread() | 获取当前执行的线程对象 |
public static void sleep(long time) | 让当前线程休眠多少毫秒后继续执行 |
public final void join() | 让调用当前这个方法的线程先执行完 |