一、认识多线程
进程 VS 线程
进程:打开任务管理器,里面有多个进程。
进程是系统进行资源分配和调度的独立单位。每一个进程都有它自己的内存空间和系统资源
- 独立性:拥有自己独立的资源,每个进程拥有自己的私有地址空间。
- 动态性:程序是一个静态的指令集合,而进程加入了时间的概念。
- 并发性:多个进程可以在单个处理器并发执行。
线程:线程是进程的一部分。每个进程可以执行多个任务,这个任务就是线程。(可以想象成某个程序的子模块)
分类:
-
用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。
-
系统级线程(核心级线程):由操作系统内核进行管理。操作系统内核给应用程序提供相应的系统调用和应用程序接口API,以使用户程序可以创建、执行以及撤消线程。
why 多线程?
- 线程之间可以共享内存
- 创建代价小(不需要重新分配系统资源)
二、使用多线程
常用的有两种方法:
- 继承 Thread 类,重写**run()**方法
- 实现 Runnable 接口
示例1:
public class MyThread extends Thread{
private int num = 10;
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName()+"-->"+num);
num--;
if(num==0) {
return;
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread a = new Thread(myThread,"thread1");
a.start();
}
}
- 重写 run() 方法,使用 start() 启动线程
- Thread.currentThread().getName() 用于获取线程名,即
Thread a = new Thread(myThread,"thread1");
的构造方法的第二个参数。
示例2:
public class MyThread1 implements Runnable{
private int num = 10;
public void run() {
while(true) {
System.out.println(Thread.currentThread().getName()+"-->"+num);
num--;
if(num==0) {
return;
}
}
}
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
Thread a = new Thread(myThread1,"thread1");
a.start();
}
}
- run() VS start()
-
run():仅仅是封装被线程执行的代码,直接调用是普通方法
-
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
- main方法也是一个线程
public class MyThread1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}
/***
main
***/
三、线程的优先级
- 线程的优先级可以在某种程度上影响执行线程的顺序,但设置优先级仅仅只是个建议,优先级高不一定先执行,这取决与操作系统。
- 设置优先级使用 setPriority() 方法,参数的取值范围为 1 <= x <= 10,不在范围内则抛出IllegalArgumentException 异常
- Thread.MIN_PRIORITY(1) Thread.MAX_PRIORITY(10) Thread.NORM_PRIORITY(5)是Thread类里面的变量,线程创建时优先级默认为5.
- 线程设置优先级要在调用start方法之前
public class MyThread1 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
for(int i = 0;i < 6;i++) {
System.out.println("lamada.."+i);
}
});
System.out.println("thread的优先级是"+thread.getPriority());
thread.setPriority(10);
System.out.println("thread的优先级是"+thread.getPriority());
thread.start();
for(int i = 0;i < 6;i++) {
System.out.println("main.."+i);
}
}
}
- Runnable接口时函数式接口,可以用Lamada表达式建立Runnable对象。
四、线程的生命周期
线程的生命周期有 5 种状态:
- 新建( New )
- 就绪 ( Runnable )
- 运行 ( Running )
- 阻塞 ( Blocked )
- 死亡 ( Dead )
五、操作线程的方法
1.join() 线程插队
public class MyThread1 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for(int i = 0;i < 20;i++) {
System.out.println("lamada.."+i);
}
});
thread.start();
for(int i = 0;i < 20;i++) {
if(i==10) {
thread.join();
}
System.out.println("main.."+i);
}
}
}
运行结果:
main..0
main..1
main..2
main..3
main..4
main..5
main..6
main..7
main..8
main..9
lamada..0
lamada..1
lamada..2
lamada..3
lamada..4
lamada..5
lamada..6
lamada..7
lamada..8
lamada..9
lamada..10
lamada..11
lamada..12
lamada..13
lamada..14
lamada..15
lamada..16
lamada..17
lamada..18
lamada..19
main..10
main..11
main..12
main..13
main..14
main..15
main..16
main..17
main..18
main..19
- 很显然,当main()方法中的 i 为 10时,main()方法被插队了,只有thread执行完成后,main()才执行。
2.slepp() 线程睡眠 抱着资源睡觉
处于sleep()中的线程不会执行,调用后线程进入阻塞状态。
Thread.sleep(long millis);//参数为毫秒数,1000ms = 1s
3.yield() 线程让步
- sleep()暂停主线程后会给其他线程机会,而yield()只会给优先级相同或更高的线程执行机会
- yield()不会阻塞进程,只是将该线程进入就绪状态。
- yield()不会抛出异常,而sleep()会