多线程1(Thread)

认识线程(Thread)

在进程中,要创建一个进程和销毁一个进程所消耗的硬件和软件资源是巨大的,因此为了优化上述过程,我们引入了“线程”。

线程是系统调度的基本单位。

1)线程和进程的关系

可以认为进程包含线程,一个进程里最少有一个线程,每个线程都可以单独执行一段逻辑,并且可以单独在CPU上调度,因此线程可以叫做“轻量级进程”。

在一个进程中,多个线程共享进程资源,而在后面在这一进程中创建的线程就直接使用进程创建的资源,可以省下申请资源的开销。

2)线程安全问题

当两个线程同时对同一个变量进行修改时,可能会引发线程之间的冲突,这时就会引发线程安全问题(后面详解)。

使用代码实现线程

线程是操作系统中的概念,操作系统中实现了线程这样的机制,因此操作系统对用户层提供了一些API供用户调用,JAVA中就是用Thread这个类进行封装。

Thread类:Thread是JAVA中标准库里的一个类,操作系统本身提供了一些函数进行操作线程,JVM就把这些函数封装成JAVA版本的Thread,这里操作Tread就是操作线程。

五种创建线程的方法:

1)定义一个静态内部类继承自Thread,实现Thread中的Run方法,创建一个这个静态内部类的实例。

public class dome2 {
    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("hah");
        }
    }

    public static void main(String[] args) {
    Thread dome = new MyThread();
    dome.start();
    }
}

Run方法就是进行执行一段逻辑的入口,run不需要代码显性调用。

注意点1:Start方法就是真正在操作系统中创建一个线程,run方法就是这个线程要执行逻辑的入口,这里如果只是调用run方法就会只执行main这个主线程而不会创建一个新的线程,以一段代码为例:

上述代码调用了start方法,创建了一个新的线程,所以创建的线程和主线程会并发执行,两个打印操作会同时进行。

如果将start去掉,直接调用run,就会只执行主线程,将run中的逻辑执行完才会执行下面,但是run中是死循环,因此只会打印出hah。

注意点2:主线程和新线程之间的执行顺序是由操作系统随机执行的,我们可以通过第三方工具JDK中的jconsole进行观察线程的执行情况。

2)创建一个子类,实现Runnable接口,重写Run方法,搭配Thread进行start

3)创建一个匿名内部类,重写run方法,start方法

4)创建一个runnable的匿名内部类,重写run方法,调用start

5)最简单的写法,在Thread中使用lambda表达式

线程中的几个常见属性

JAVA中对线程的状态进行了进一步扩充(后面细说)

1)后台线程:线程没执行完,进程可以结束(线程不可以结束进程)。

前台线程:线程没执行完,进程不可以结束(线程可以组织进程结束)。

main线程和我们自己创建的线程是前台线程,其他的都是后台线程。

我们可以通过setDaemon设置前台线程为后台线程(必须在线程start前设置),后台线程结束程序就结束了。

(这个程序当主线程运行三次之后结束进程,我们创建的后台线程就会随进程结束而结束)

2)是否存活

当线程没结束时就是true,结束或还没开始执行就是false

启动一个线程

启动一个线程要调用start,调用start才是真正调用系统中创建线程的api,而线程启动后会自动调用run中的内容。

1.调用start是非常快的,不会有任何的阻塞等待。

这里调用完start后就立刻调用下面得sout,打印出main和thread,但是要注意这里先打印出的是main,这是因为调用start后要创建线程。并不是一直是这样的,可能会有例外,这是因为系统调用线程是随机调度的。

2.一个线程只能start一次

一个线程start后就是就绪/阻塞状态,而对于阻塞/就绪状态的线程,就不能start了。

对于一个Thread而言,对应着操作系统中的一个线程。

中断(打断)一个线程

1.通过变量

通过修改线程内的变量进行打断

2.使用isInterrupted和interrupt中断

这里的currentThread是获取到调用此方法对象的引用,isInterrupted是默认false,下面的interrupt是将条件改为true。

注意事项:

如果在线程里加上sleep,再在main中调用interrupt,就会提前唤醒线程,触发异常,同时将isInterrupted重置为false。

总结:1).没使用sleep等阻塞操作时,interrupt会将isInterrupted从false改为true。

2).使用sleep的阻塞操作时,调用interrupt会抛出interruptException异常,将isInterrupted重置为false,同时提前唤醒sleep,但此时我们可以在catch中进行进一步操作,手动决定线程是否结束。

线程的等待

操作系统调用线程是随机调度(抢占式执行),线程等待就是约定好线程的结束顺序。

1.使用join进行等待

这里就是main等待线程t结束,再执行mian中的逻辑,这里的join是死等。

main等待线程t也是可以的,但是要获取的main的引用。

join不一定执行阻塞,当执行join时线程结束,join就不会阻塞等待。

join还有一个重载版本,设置等待的最大时间,最多等待xxx毫秒。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值