一、概述
1、线程
线程是CPU的调度与分配最小单位,它是比进程更小的能独立运行的基本单位,一个进程中可以包含多个线程,但至少包含一个主线程。每个进程执行前,操作系统都会为其分配所需的资源,所有线程共享进程的资源,各个线程也可以拥有属于自己的私有资源
进程仅负责为各个线程提供所需的资源,真正执行任务的是线程,而不是进程。
2、多线程
所谓多线程,即一个进程中拥有多(≥2)个线程,线程之间相互协作、共同执行一个应用程序。在操作系统中,有很多种调度方式,在Java中采用的是抢占式调度。每个线程都拥有一个优先级,优先级高的使用CPU几率更大,如果优先级相同,则随机选择执行。
(1) CPU 使用抢占式调度模式在多个线程间进行着高速的切换
(2) 对于 CPU 的一个核而言,某个时刻只能执行一个线程,而 CPU 在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻执行
(3) 多线程程序并不能提高程序的运行速度,但能提高程序运行效率,让 CPU 的使用率更高
二、线程的实现
在jdk.1.5之前: 创建线程的方式: 两种:
-
继承Thread类
-
实现Runnable接口
在jdk1.5之后: 多加了两种:
-
实现 Callable接口
-
线程池
1、继承Thread类
-
定义一个类继承 Thread 类
-
重写 run 方法
-
创建子类对象,即创建线程对象
-
调用 star 方法,开启线程并让线程执行,同时还会告诉 JVM 去调用 run 方法
自定义线程类:
public class MyThread extends Thread{
@Override
public void run() {
for(int i = 0;i < 20;i++)
{
System.out.println("新线程正在执行" + i);
}
}
}
主线程:
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for(int i = 0;i < 20;i++)
{
System.out.println("main线程正在执行" + i);
}
}
-
创建新的线程后,会产生两个执行路径,都会被CPU执行,CPU有自己的选择执行权力,所以会出现随机的执行结果
-
可以理解为两个线程在抢夺CPU的资源(时间)
-
注:线程对象调用 run 方法和调用 star 方法的区别: (1) 线程对象调用 run 方法不开启线程,仅仅是对象调用方法 (2) 线程对象调用 start 方法开启线程,并让 JVM 调用 run 方法在开启的线程中执行
2、实现Runnable接口
-
定义类实现 Runnable 接口
-
重写接口中的 run 方法
-
创建 Thread 类的方法
-
将 Runnable 接口的子类对象作为参数传递给 Thread 类的构造函数
-
调用 Thread 类的 start 方法开启线程
Runnable 实现类:
public class MyThread implements Runnable{
@Override
public void run() {
for(int i = 0;i < 20;i++)
{
System.out.println("新线程正在执行" + i);
}
}
}
主线程:
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
t1.start();
t2.start();
for(int i = 0;i < 20;i++)
{
System.out.println("main线程正在执行" + i);
}
}
实现 Runnable 接口的好处:
-
避免了单继承的局限性,所以此方法较为常用
-
将线程分为两部分,一部分线程对象,一部分线程任务,更加符合面向对象思想
-
将线程任务单独分离出来封装成对象,类型就是 Runnable 接口类型
3、实现Callable接口
-
定义类实现 Callable 接口
-
重写接口中的 call 方法
-
创建 Callable 接口实现类对象
-
创建一个 FutureTask 对象, 传递 Callable 接口实现类对象
-
创建一个 Thread 对象,传递FutureTask 的对象,并调用 start 方法开启线程
-
调用 FutureTask 提供的 get 方法获取返回值
Callable 实现类:
public class MyThread implements Callable {
@Override
public Object call() throws Exception {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
return list;
}
}
主线程:
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
FutureTask futureTask = new FutureTask<>(mt);
new Thread(futureTask).start();
List<Integer> list = (List<Integer>) futureTask.get();
for (Integer integer : list) {
System.out.print(integer + " ");
}
}
三、线程的常用方法
-
Thread.currentThread():获取当前线程对象
-
Thread.currentThread().getName():获取当前线程对象的名称
-
setName():设置线程名称
-
Thread.sleep(毫秒数),让线程休眠,时间到,自动唤醒并继续执行
优先级:可以给线程设置优先级,范围是1-10,10优先级最高
-
setPriority(数字):给线程设置优先级,这里优先级高,并不代表一定先执行,只是增加了抢占到资源的机会
-
getPriority():获取线程的优先级
-
yield():当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行
-
join():当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程