一、多线程概念
- 并行:同一时间段,无论从微观还是宏观,程序都是一起执行的。
- 并发:同一时间段,两个或多个程序执行,有时间上的重叠。
进程
一段程序的执行过程。
进程有以下几个特征:
- 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
- 并发性:任何进程都可以同其他进程一起并发执行
- 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
- 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
结构特征:进程由程序、数据和进程控制块三部分组成。
进程的状态: Ready(就绪状态)、Running(运行状态)、 Blocked(阻塞状态)
线程:进程是操作系统分配资源的单位,线程是调度的基本单位,线程之间共享进程资源。 多线程:一个进程如果有多条执行路径,则称为多线程程序
二、多线程实现方式
继承Thread
- 定义继承
Thread类的MyThread类 - 在
MyThread类中重写run方法 - 定义测试类
ThreadDemo - 创建线程,执行线程
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"_"+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.start();
mt2.start();
}
}
注意:
start():启动线程;然后由JVM调用此线程的run()方法run():封装线程执行的代码,直接调用,相当于普通方法的调用
实现Runnable接口
- 定义RunnableDemo实现Runnable接口
- 重写Run方法
- 创建RunnableTest类
- 创建RunnableDemo对象
- 创建Thread类的对象并将RunnableDemo对象作为参数传入Thread
- 启动线程
public class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "_" + i);
}
}
}
public class RunnableTest {
public static void main(String[] args) {
// 创建参数的对象
RunnableDemo rb1 = new RunnableDemo();
// 创建线程对象并传递参数
Thread t1 = new Thread(rb1);
// 开启线程
t1.start();
RunnableDemo rb2 = new RunnableDemo();
Thread t2 = new Thread(rb2);
t2.start();
}
}
实现Callable接口
- 定义CallableDemo类实现Callable接口
- 在CallableDemo类中重写call()方法
- 创建CallableTest类对象
- 创建FutureTask对象,并将CallableDemo对象作为参数传递进去
- 创建Thread类对象,并将FutureTask对象作为参数传递进去
- 启动线程,并通过get获取执行结果
public class CallableDemo implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "----" + i);
}
return "CallableDemo";
}
}
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建CallableDemo对象
CallableDemo cd = new CallableDemo();
// 作为参数传递给Thread对象
FutureTask<String> sft = new FutureTask<>(cd);
//创建线程对象
Thread thread = new Thread(sft);
//开启线程
thread.start();
System.out.println(sft.get());
}
}
总结
- 继承Thread类: 编写简单,但扩展性差,不能在继承其他的类
- Runnable与Callable: 扩展性强,还可以继承其他的类,但编写复杂,不能直接使用Thread
线程的修改
- setName(String name): 修改线程名称为name
- getName(): 获取该线程名称
- currentThread():返回对当前正在执行的线程对象的引用
- sleep(long millis):使当前正在执行的线程停留(暂停执行)指定的毫秒数
三、线程的优先级
线程有两种调度方式:
- 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
- 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
- Java使用的是抢占式调度模型
修改线程优先级
- setPriority(int newPriority):更改此线程的优先级线程默认优先级是5;线程优先级的范围是1-10
- getPriority():返回此线程的优先级
public class ThreadTest {
public static void main(String[] args) {
ThreadDemo mt1 = new ThreadDemo();
ThreadDemo mt2 = new ThreadDemo();
mt1.setName("线程1");
mt1.setPriority(1);
mt2.setName("线程2");
mt2.setPriority(10);
mt1.start();
mt2.start();
}
}
守护线程
- setDaemon(boolean on):如果普通线程执行完毕,那么守护线程也就停止执行,java虚拟机退出
mt2.setDaemon(true);