❣博主主页: 33的博客❣
▶️文章专栏分类:JavaEE◀️
🚚我的代码仓库: 33的代码仓库🚚
🫵🫵🫵关注我带你了解更多线程知识
目录
- 1.前言
- 2.进程
- 3.线程
- 4.线程和进程的区别
- 5.Thread创建线程
- 5.1继承Thread创建线程
- 5.2实现Runnable接口
- 5.3lambda表达式
- 6.Thread常见方法
- 6.1Thread常见属性
- 6.2 中段一个线程- interrupt()
- 6.3等待线程-jion()
- 7.线程的状态
- 2.1代码实现
- 2.2运行结果
1.前言
我们设想:一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。此时,我们就把这种情况称为多线程,那么在Java中该如何实现多线程呢?我们一起来学习吧!!
2.进程
提到线程,我们就不得不先了解进程了,那么什么是进程呢?进程就是运行起来的一个个程序。 引入进程有什么作用呢?是为了实现并发编程。但进程有很多,系统是如何管理它们的呢?那就是"先描述,再组织"。
描述:使用PCB结构表示进程的各种属性。
PCB中的重要特性: 1.pid(进程标识符)2.内存指针(表示进程持有的内存资源,比如内存空间具体在哪里,有哪些部分组成,每个部分干啥的等等)3.文件描述表(表示进程持有的硬盘资源,类似于顺序表)4.CPU资源(状态、优先级、上下文、记账消息,这些都主要来完成进程调度)
组织:利用双显链表,把PCB的这些结构串起来。
进程是资源分配的基本单元,创建进程需要给他分配内存和硬盘资源需要消耗的时间太多,销毁一个进程销毁的时间很多,调度一个进程消耗的时间也很多,如果频繁的创建或者销毁一个资源,这个时候的开销就是非常大的,为了解决这个问题,我们引入了线程(Thread)。
3.线程
线程也叫做“轻量级进程”,创建线程,销毁线程,调度线程都比进程更快。但线程不能独立存在,要依附于进程,即一个进程至少包含一个线程!但每一个线程都是独立执行的,一个进程,最开始的时候必须要包含一个线程,这个线程负责完成执行代码的工作,也可以创建出多个线程,完成并发执行的效果。
线程的特点:
1.每一个线程都可以独立的去cpu上执行
2.同一个进程的多个线程之间公用一份内存空间和文件资源
3.同一个进程中,采用多线程的方式能提高效率,但有一定限度,如果无限度的引入线程可能是调度开销增大,导致效率反而降低
4.同一个进程中,一个线程抛出异常,如果没有妥善处理可能会导致整个进程崩溃。
4.线程和进程的区别
1.一个进程包含一个或多个线程
2.进程和线程都是用来实现并发编程的,但线程比进程更轻量,更高效
3.进程和进程之间具有独立性,如果一个进程异常不会影响到其他进程,但线程和线程之间可能会相互影响
4.进程是资源分配的基本单位,线程是调度执行的基本单位
5.Thread创建线程
线程是操作系统的概念,操作系统提供了一些API可以操作线程,Java对系统API进行了封装,使用Thread类就可以创建出一个线程.
5.1继承Thread创建线程
方法1:
package Thread;
class MyThread extends Thread{
@Override
//线程的入口
public void run() {
while (true) {
System.out.println("hello thread");
//隔1s轮转
try {
Thread.sleep(1000);//1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Thread t=new MyThread();
//start和run都是Thread的成员
//run只是描述线程的入口,线程主要做什么
//start则是真正的调用了系统API创建了线程,让线程再调用run()
t.start();
while (true){
System.out.println("hello main");
Thread.sleep(1000);
}
}
}
方法2:匿名内部类
package thread;
public class Demo3 {
public static void main(String[] args) {
Thread t = new Thread() {
//匿名内部类
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
注意
1.Thread在java.lang这个包下并不用导包
2.start方法内部会调用系统的API生成一个线程再调用run函数,和run函数的效果类似,但本质区别为:是否会创建一个线程
5.2实现Runnable接口
方法1:
package thread;
class MyRunnable implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
t.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
方法2:匿名内部类
package thread;
public class Demo4 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5.3lambda表达式
package thread;
public class Demo6 {
public static void main(String[] args) {
//lambda表达式
Thread t = new Thread(() -> {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"第一个线程");
t.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
6.Thread常见方法
6.1Thread常见属性
属性 | 获取方法 |
---|---|
ID | getId |
名称 | getName |
状态 | getState |
优先级 | getPriority |
是否后台线程 | isDaemon |
是否存活 | isAlive |
是否被中断 | isInterrupted |
ID 是线程的唯一标识,不同线程不会重复
名称是各种调试工具用到
状态表示线程当前所处的一个情况,下面我们会进一步说明
优先级高的线程理论上来说更容易被调度到
后台线程:守护线程,后台线程结束与否不影响整个程序,但如果前台线程没有结束,进程也不会结束。
是否存活:判断内核线程是否已经销毁
6.2 中段一个线程- interrupt()
首先我们要知道,一个线程如果时间特别长,那么大概率在线程的内部有循环再一直执行。如果想要中断此线程,那么我们就想办法尽快让run函数执行完成,那么怎么能让循环快速执行完呢?其实我们只需要在循环处添加一个条件,条件不成立就结束了。
例如:
public class Demo7 {
public static boolean flag=false;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while (!flag){
System.out.println("t进程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("工作完毕");
});
t.start();
Thread.sleep(5000);
flag=true;
System.out.println("打断进程");
}
}
注意
定义flag的时候只能定义为成员变量,不能定义为局部变量,因为在lambda有一个语法规则:变量捕获。把当前作用域的变量在lambda中复制了一份,在变量捕获时有一个前提限制:必须只能捕获final修饰的变量或者变量不能做任何修改。如果把flag设置为成员变量,就不再是变量捕获的与法律,而是内部类访问外部类的属性。
但是如果我们每次都专门定义一个标志位来打断线程是非常麻烦的,而且当处于睡眠模式下还不能立即就想应,在Thread类中有有一个标志位isInterupted来判定线程是否被打断。
public class Demo8 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("t进程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//唤醒sleep有3种操作
//1.不管它,继续执行t线程
//2.立即执行打断
break;
//3.进行一些其他操作
}
}
});
t.start();
System.out.println("main进程");
Thread.sleep(5000);
System.out.println("t进程打断");
t.interrupt();//打断
}
}
说明
1.Thread.currentThread()表示当前线程即t线程
2.在正常情况下,sleep休眠时间完成才会被唤醒,如果调用interrupt()那么就提提前唤醒它触发InterruptedException异常
观察下图:虽然打断了t线程并且触发了InterruptedException异常,但t线程依然在执行。
出现上述情况是因为:interr唤醒sleep之后会抛出异常当同时也会清除标志位,这就使打断效果像没有生效一样。Java期望当线程收到“要中断”的信号使,由本身来决定接下来该怎么做。
6.3等待线程-jion()
等待一个线程即使指,当一个线程执行完毕时才能执行另外一个线程。
public class Demo9 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
for (int i=0;i<5;i++){
System.out.println("t线程正在执行");
}
});
t.start();
//等待t线程执行结束再执行main线程
t.join();
System.out.println("t线程执行结束");
System.out.println("main线程");
}
}
7.线程的状态
在进程中最核心的状态就是就绪和阻塞状态,在线程中同样适用,同时java又赋予了线程一些其他的状态:
NEW:创建Thread对象安排了工作,但没有启动。
RUNNABLE:指就绪状态,即线程正在执行或者线程等待执行
TERMINATED: 工作完成了
TIMWD_WAITING:阻塞,由于sleep这种固定的时间方式而阻塞
WAITING: 阻塞,由于wat这种不固定的时间方式而阻塞
BLOCKED: 阻塞,由于锁竞争导致的阻塞
在Java中可以通过getState()方法来查看此时线程的状态。
2.1代码实现
public class Demo10 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
for (int i=0;i<3;i++){
System.out.println("t线程在执行");
}
});
System.out.println(t.getState());
t.start();
System.out.println(t.getState());
Thread.sleep(3000);
System.out.println(t.getState());
}
}
2.2运行结果
总结:本篇文章主要介绍了线程的概念,为什么要引入线程,要理解线程和进程的区别,创建线程的几种方法,会运用Thread的一些常见方法。
下期预告:线程安全问题