多线程
1. 基本概念
1.1 程序,进程,线程
- 程序:完成一定功能的静态代码,对比火车站售票厅
- 进程:正在执行的静态代码,称为进程,对比北京西站售票
- 线程:为了并发执行一些异步任务,比对售票的中多个窗口
- 单核cpu和多核cpu
- java程启动后,main线程,gc线程,异常处理线程
1.2 并行和并发
- 并行:多核cpu下,多线程同时执行
- 并发:如果是单核cpu,采用抢占时cpu调度模型,让cpu在多个线程之间切换执行
完成一个任务需要多少个线程
- io密集型 cpu核数*2
- cpu密集型 cpu核数
- 混合型 经验
1.3 线程使用的场景
- 网络连接tomcat,mysql,一个连接对一个一个线程, one connection one thread
- 文件操作,文件下载,后台启动一个线程异步执行长时间的任务
2 多线程实战
2.1 Thread
- 定义子类继承Thread类。
- 类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法。
启动一个线程,在线程中执行1-100的偶数打印工作
Mythread类:
//Thread类 适用于简单的线程场景,但是如果已经继承了其他类,就不能再继承Thread类了,所以有一定的限制。
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
if (i%2==0){
// System.out.println(getName()+":"+i);
System.out.println(Thread.currentThread().getName() + ":主线程");
}
}
}
}
Test1类:
/*
* 线程:
* 多线程:
* 1.继承Thread类
* 2.重写run方法
* 3.创建子类对象
* 4.调用start方法
* 5.调用start方法后,线程才会启动,执行run方法
* 6.一个线程只能执行一次start方法
*/
public class Test1 {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
/* myThread1.setName("线程1");*/ //设置线程名字
myThread1.start();
MyThread myThread2 = new MyThread();
/*myThread2.setName("线程2");*/
myThread2.start();
//获取线程名
System.out.println(Thread.currentThread().getName() + ":主线程");
}
}
- 如果子线程执行,进程不会停止
2.2 Runable
- 定义子类,实现Runnable接口。
- 类中重写Runnable接口中的run方法。
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程, 调用Runnable子类接口的run方法。
MyTask类:
public class MyTask implements Runnable{
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
if (i%2==0){
//System.out.println(getName()+":"+i);
System.out.println(Thread.currentThread().getName() + ": "+i);
//线程礼让 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
//若队列中没有同优先级的线程,忽略此方法
Thread.yield();
}
//sleep使线程阻塞 指定线程休眠的时间,单位毫秒,让出cpu时间片,其他线程可以抢占cpu时间片
// try {
// Thread.sleep(100);
// }
// catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
}
}
}
Test1类:
//Runnable接口的实现类
public class Test1 {
public static void main(String[] args) {
MyTask myTask = new MyTask();
Thread thread1 = new Thread(myTask,"线程1.。。。。。。。。");
thread1.setPriority(5);//设置线程优先级 1-10 1最低 10最高
thread1.start();
Thread thread2 = new Thread(myTask,"线程2");
thread2.setPriority(5);//设置线程优先级 1-10 1最低 10最高
thread2.start();
}
}
2.3 Thread常见方法
- 构造函数:
-
- Thread(): 创建新的Thread对象
- Thread(String threadname): 创建线程并指定线程实例名
- Thread(Runnable target): 指定创建线程的目标对象,它实现了Runnable接口中的run方法
- Thread(Runnable target, String name): 创建新的Thread对象
- void start(): 启动线程,并执行对象的run()方法
- run(): 线程在被调度时执行的操作
- String getName(): 返回线程的名称
- **void setName(String name)😗*设置该线程名称
- static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
- static void yield(): 线程让步
-
- 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
- 若队列中没有同优先级的线程,忽略此方法
- join() : 当某个程序执行流中调用其他线程的 join() 方法时, 调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止
- static void sleep(long millis): (指定时间:毫秒) 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
- stop(): 强制线程生命期结束,不推荐使用
- boolean isAlive(): 返回boolean,判断线程是否还活着
2.4 sleep
指定线程休眠的时间,单位毫秒,让出cpu时间片,其他线程可以抢占cpu时间片。
//sleep使线程阻塞 指定线程休眠的时间,单位毫秒,让出cpu时间片,其他线程可以抢占cpu时间片
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
2.5 线程优先级
- 线程的优先级等级
-
- MAX_PRIORITY: 10
- MIN _PRIORITY: 1
- NORM_PRIORITY: 5
//Runable接口的实现类
public class Test1 {
public static void main(String[] args) {
MyTask myTask = new MyTask();
Thread thread1 = new Thread(myTask,"线程1.。。。。。。。。");
thread1.setPriority(5);//设置线程优先级 1-10 1最低 10最高
thread1.start();
Thread thread2 = new Thread(myTask,"线程2");
thread2.setPriority(5);//设置线程优先级 1-10 1最低 10最高
thread2.start();
}
}
2.6 守护线程
- 其他线程都执行结束,守护线程自动结束
- 守护启动子线程,也是守护线程
- 守护线程的语法
thread.(*setDaemon(true)*
设置守护线程
public class Test2 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
//设置守护线程 一般用于后台线程 当其他非后台线程结束后,该子线程也会结束
myThread.setDaemon(true);
myThread.start();
JOptionPane.showMessageDialog(null,"是否向下执行?");//弹出对话框 阻塞
System.out.println("main线程终止");
}
static class MyThread extends Thread
{
@Override
public void run() {
while (true)
{
System.out.println("子线程正在执行");
}
}
}
}
运行结果:
main线程、子线程正在运行
点击确定后,主线程(main)结束,由于将子线程设置为了守护线程,子线程不再运行,程序进程结束:
2.7 yield(礼让线程)
让出cpu的时间分片,让出cpu的执行权
暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法。
//Runnable接口的实现类 Runnable接口创建线程的好处是,可以避免Java单继承的限制
public class MyTask implements Runnable{
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
if (i%2==0){
//System.out.println(getName()+":"+i);
System.out.println(Thread.currentThread().getName() + ": "+i);
//线程礼让 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
//若队列中没有同优先级的线程,忽略此方法
Thread.yield();
}
}
}
}
总结
三种线程启动方式:
public class ThreadDemo {
//线程的三种启动方式
public static void main(String[] args) {
//1.方式1 继承Thread启动线程
Mythread mythread = new Mythread();
mythread.start();
//2.方式2 实现 Runable接口启动
Mytask mytask = new Mytask();
Thread thread = new Thread(mytask);
thread.start();
//3.方式3 匿名内部类启动 与方式2本质上相同只是简洁写法
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Thread.currentThread().setName("匿名内部类启动线程");
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}).start();
}
//方式1 继承Thread类启动
static class Mythread extends Thread
{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Thread.currentThread().setName("继承Thread方式启动线程");
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}
//方式2 实现Runable接口方式启动
static class Mytask implements Runnable
{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Thread.currentThread().setName("Runable方式启动线程");
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}
}
Thread类和Runable类的区别
Thread类:
适用于简单的线程场景,但是如果已经继承了其他类,就不能再继承Thread类了,所以有一定的限制。
Runable类:
Runnable接口的实现类 Runnable接口创建线程的好处是,可以避免Java单继承的限制