Java在诞生时就选择了对内置多线程的支持,线程作为操作系统的`最小调度单位`,能够更好的利用多核cpu的优势,显著提高程序的运行性能。
## 线程
现代操作系统在运行程序的时候都会创建一个进程,在进程里面创建运行程序所需要的调度单位,就是一个线程,一个进程可以包含多个线程。每个线程都有自己的程序计数器,栈和局部变量表等属性。Java中的线程和操作系统中的线程是一一对应的,当运行一个main方法的时候会创建一个java进程,其包含的线程如下:
~~~
// 获取Java线程管理MxBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取同步的synchronized和monitor信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
}
~~~
结果:
~~~
[1] main # main线程
[2] Reference Handler # 清除引用的线程
[3] Finalizer # 调用finalize方法的线程
[4] Signal Dispatcher # 分发处理发送给JVM信号的线程
[5] Attach Listener
[12] Common-Cleaner
[13] Monitor Ctrl-Break
[14] Notification Thread
~~~
备注:上述是基于JDK14的测试结果,不通过版本可能不太一致。
### 线程的使用
java使用Thread类实例来表示一个线程,所有的线程对象都是Thread类的实例或是其子类的实例。Thread类继承Runnable接口,并实现该接口的唯一一个抽象方法run(),在该方法中执行线程要运行的代码。
线程的创建方式有如下几种:
#### 继承Thread类创建线程
1. 自定义线程类
~~~
public class MyThread extends Thread {
public MyThread() {}
/**
* 带参构造,可以指定该线程的名称
*/
public MyThread(String name) {
super(name); //调用父类Thread的构造方法指定线程名称
}
/**
* 重写run方法 -- 必须
*/
@Override
public void run() {
/* 线程执行的代码 */
}
}
~~~
2. 创建实例
~~~
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread("新线程名称"); //创建一个线程实例
myThread.start(); //开启新线程 -- 执行线程中run方法的内容
}
}
~~~
> 注意:要调用start方法才会自动执行run中的方法,线程并不是调用run方法来执行。
~~~
调用start()方法开启一个新线程的时候,并不是立刻就会执行该线程中run中的代码。因为java是采用抢占式调度的方式来分配CPU的使用,新创建的线程在不设置其优先级的情况下优先级是相同的。因此在调用start方法之后,该线程参与CPU使用权的抢夺,和main线程抢夺或者是和另外创建的线程抢夺,能不能抢夺到还是一回事,**只有抢夺到了才会执行该线程对应的run方法。**
~~~
Thread类常用方法
**构造方法**
~~~
// 无参构造,创建一个新的Thread对象
public Threa();
// 传入Runnable接口实现类对象,在启动线程的时候调用target接口的run方法。
public Thread(Runnable target);
// 自定义线程名称
public Thread(String name);
// 传入Runnable接口对象,并自定义线程名称
public Thread(Runnable target, String name);
~~~
**常用的基础方法**
~~~
// 获取当前线程名称
public String getName();
// 当前正在执行的线程停止执行指定的毫秒数,时间一到就自动CPU就自动恢复并继续执行下去。
public static void sleep(long millis);
// 设置此此线程的优先级,有些操作系统会忽略优先级的设置,因此并不能根据优先级来编排程序的运行顺序。
public final void setPriority(int newPriority);
// 获取当前正在执行的线程
public static Thread currentThread();
~~~
#### 继承Runnable类创建
在Thread的构造方法中,有一个**public Thread(Runnable target)**的构造方法,即传入Runnable接口的实现类对象,重写里面的run方法创建一个新线程。值得注意的是,Thread类本身就是实现Runnable接口的,Runnable接口只有一个run方法(函数式接口)。
1. 定义Runnable实现类接口
~~~
public class RunnableDemo implements Runnable{
@Override
public void run() { //重写run方法
/* 线程执行代码 */
}
}
~~~
2. 创建实例
~~~
public static void main(String[] args) {
Runnable run = new RunnableDemo();
Thread thread = new Thread(run);
thread.start(); //注意最后要调用start方法才会开启该多线程
}
~~~
##### 使用匿名内部类的方式
上面创建Runnable接口实现类的时候可以发现,创建实现类仅仅是为了重写其run方法,如果要在线程中执行的代码有各种各样的话,就要创建各种各样的实现类,还只是重写其run方法,反倒有点复杂了。
而使用匿名内部类的方式可以更方便的创建一个Thread接口:
~~~
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("这是一个匿名内部类");
}
});
thread.start();
//也可以
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("这是一个匿名内部类");
}
}).start();
~~~
##### 使用Lambda表达式创建
在JDK8中引入了Lambda表达式的新特性,有关于Lambda表达式会在以后中讲到,先来看看怎么用:
~~~
new Thread(()->{
/* 这里即可书写run方法中的内容 */
}).start();
~~~
**使用Thread和Runnable的区别**
1. Java中Thread实例才表示一个线程,Runnable中的run方法表示需要执行的任务。
2. 使用Runnable接口是很容易实现资源共享的,同时可以实现代码实现和线程相互独立。
3. 后面的线程池中只能放入实现Runnable接口的类,不能直接放入Thread类。
4. 可以使用Lambda表达式,代码书写更加简便。
## 线程的状态
与操作系统中进程的状态类似,Java中也定义了一套与线程有关的状态,共有6种状态,用ThreadState枚举来表示
![线程状态迁移图](https://www.freesion.com/images/626/77b14bf05c03d76daf85eb5eaf19536a.png)
* NEW:初始状态,线程被构建,但是还没有调用**start方法**。
* RUNNABLE:运行状态,调用了start方法,Java将就绪和运行统称为“运行中”。
* BLOCKED:阻塞状态,从运行态中竞争不到synchronized锁时进入阻塞状态;获取到锁之后又重新恢复到运行态。
* TERMINATED:终止状态,run方法中代码执行完毕后进入终止状态。
* WAITING:等待状态,运行态调用`wait`、`join`、`LockSupport.park`方法时进入等待状态。调用`notify`、`notifyAll`、`LockSupport.unpark(Thread)`方法时恢复到运行态。*\*由自己进入等待,由其他线程唤醒!*
* TIMED\_WAITING:超时等待状态,可以设置等待之间,时间到了自动恢复到运行态。
- 第一章 Java基础
- ThreadLocal
- Java异常体系
- Java集合框架
- List接口及其实现类
- Queue接口及其实现类
- Set接口及其实现类
- Map接口及其实现类
- JDK1.8新特性
- Lambda表达式
- 常用函数式接口
- stream流
- 面试
- 第二章 Java虚拟机
- 第一节、运行时数据区
- 第二节、垃圾回收
- 第三节、类加载机制
- 第四节、类文件与字节码指令
- 第五节、语法糖
- 第六节、运行期优化
- 面试常见问题
- 第三章 并发编程
- 第一节、Java中的线程
- 第二节、Java中的锁
- 第三节、线程池
- 第四节、并发工具类
- AQS
- 第四章 网络编程
- WebSocket协议
- Netty
- Netty入门
- Netty-自定义协议
- 面试题
- IO
- 网络IO模型
- 第五章 操作系统
- IO
- 文件系统的相关概念
- Java几种文件读写方式性能对比
- Socket
- 内存管理
- 进程、线程、协程
- IO模型的演化过程
- 第六章 计算机网络
- 第七章 消息队列
- RabbitMQ
- 第八章 开发框架
- Spring
- Spring事务
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 数据库
- Mysql
- Mysql中的索引
- Mysql中的锁
- 面试常见问题
- Mysql中的日志
- InnoDB存储引擎
- 事务
- Redis
- redis的数据类型
- redis数据结构
- Redis主从复制
- 哨兵模式
- 面试题
- Spring Boot整合Lettuce+Redisson实现布隆过滤器
- 集群
- Redis网络IO模型
- 第十章 设计模式
- 设计模式-七大原则
- 设计模式-单例模式
- 设计模式-备忘录模式
- 设计模式-原型模式
- 设计模式-责任链模式
- 设计模式-过滤模式
- 设计模式-观察者模式
- 设计模式-工厂方法模式
- 设计模式-抽象工厂模式
- 设计模式-代理模式
- 第十一章 后端开发常用工具、库
- Docker
- Docker安装Mysql
- 第十二章 中间件
- ZooKeeper