线程—并发编程(2)

线程—并发编程(基础+1)



一、线程相关属性

1、线程和调用栈

线程和调用栈的关系:每个线程都有自己独立的调用栈

public class addMain {
    //暂且把主方法中的线程称为主线程
    //一个Java应用总是从main ()方法开始运行,mian ()方法运行在一个线程内,它被称为主线程。
    public static void main(String[] args) {
        //创建一个线程对象(这个线程对象执行的是add方法)
        addThread addT=new addThread();
        addT.start();

        //在主线程中也去调用add方法
        Add.add(10,20);
    }
}
//线程
class addThread extends Thread{
    @Override
    public void run() {
        //这个线程要干的事就是调用Add方法
        System.out.println(Add.add(1, 2));
    }
}
//add方法
class Add{
    public static int add(int a, int b){
        return a+b;
    }
}

在main方法中,创建并启动了一个addT线程,同时自己也调用add方法。所以现在共有两个线程:

1、主线程(包含main方法和add方法)
2、addThread线程(包含run方法和add方法)
虽然这两个线程都调用了add方法,但只能说明这两个线程执行的是同一批指令(程序=指令+数据),他们各自有各自的调用栈,意味着他们用同一批指令处理不同的数据。

来看看主线程栈帧的调用情况👇
main线程调用的栈帧

接着再展示一下addThread线程的栈帧调用情况👇
addThread线程调用的栈帧


调试器中看到的方法调用栈,每一行就是一个栈帧(frame),保存的就是方法运行中的临时数据,对我们来说最主要的就是临时变量。每个线程都是独立的执行流,并且都有自己的独立的栈,A线程调用了哪些方法,和B线程一点关系都没有。

2、线程的常见属性

  • 线程 id :本线程(JVM进程)内部分配的唯一的 id,只能 get 不能 set。
  • 线程的name:为了给开发人员看的,JVM并不需要这个属性。这个属性可以 get 也可以 set 。
  • 线程状态:新建(NEW)、就绪(RUNNABLE)、运行(RUNNABLE)、阻塞(BLOCKED/WAITING/TIME_WAITING)、结束(TERMINATED),线程状态也是只能获取,不能设置,是被JVM内部设置的,但是可以通过某些方法改变它的状态(比如就可以通过调用线程.start()方法让某个线程变成RUNNABLE状态)。

3、前台线程&&后台线程(精灵线程/守护线程)

前台线程:一般做一些交互工作;
后台线程:一般做一些支持的工作;
我们自己创建的线程默认都是前台线程,除非把他修改成后台线程。

//如何修改前后台线程
public class daemon {
    static class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("哈哈哈哈");
        }
    }
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        System.out.println(t1.isDaemon());//返回true代表是后台(daemon)线程
        t1.setDaemon(true);//修改自己是前台还是后台线程
        //设置为 true 就成了后台线程,设置为 false 就成了前台线程。
        t1.start();
    }
}

这里有很重要的一个问题,JVM进程什么时候会退出?

所有前台线程都退出了,JVM进程就退出了

  1. 必须要求所有前台线程都退出,和主线程没关系;
  2. 后台线程没关系,即使后台线程还在工作,也正常退出。

二、线程之间的相互控制

1、Thread.join()方法

A线程:

  1. 创建B线程并启动B线程
  2. A线程现在在干别的事……
  3. 等待B线程完成所有工作(等B线程执行结束)
  4. 打印B线程已经结束

B线程:随便做一些工作;

举个例子,总裁现在去吃饭,但忘记带钱包,于是他吩咐他的秘书去办公室拿钱包(总裁只有等秘书拿来了钱包付了钱才可以离开餐厅,可不能吃霸王餐啊)。等秘书拿钱包的过程就是join()的用法及作用。

public class Main {
    static class MyThread extends Thread{
        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            println("秘书说:钱包拿来啦");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread b = new MyThread();
        b.start();
        println("总裁自己先去吃饭");
        // 看一下有 join 和没有 join 的区别
        b.join();
        println("总裁说:秘书给我把钱送来了,结账走人");
    }

    //这个方法是为了让我们更清楚的看到有join和没有join的区别
    public static void println(String msg) {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(date) + ": " + msg);
    }
}

来看一下运行结果:
有join()的时候👇,总裁非常有素质讲道德,结账之后才离开餐厅
有join()

没有join()的时候👇,总裁不讲武德,吃霸王餐,不给钱!
没有join()

注意注意:线程A(总裁)和线程B(秘书)在没有join()之前是没有关系的,是互相独立的执行流(总裁专心吃饭,秘书就去钱包),有了join()之后这两个线程才被关联到一块,总裁必须等秘书拿来钱包之后才能结账走人。

2、Thread.sleep()方法

TimeUnit.SECONDS.sleep(5);
//这俩是等价的,随便挑一个用就行
Thread.sleep(5);

从线程调度的角度来看,调用sleep(多长时间)就是将当前线程从“运行状态 ”改变到“阻塞状态”,阻塞状态就是线程在等某个资源或事件,当条件满足(即过去了多长时间,sleep()结束),线程就从“阻塞状态”变成“就绪状态”,(万事俱备之前CPU),当这个线程被调度器选中时,就接着之前的指令执行。

3、Thread.currentThread()方法

返回一个当前线程的Thread引用,指向一个线程对象(在哪个线程中调用的该方法,就返回哪个线程的对象)。

public class Main {
    static class MyThread extends Thread {
        @Override
        public void run() {
            printCurrentThreadAttributes();
        }
    }
    private static void printCurrentThreadAttributes() {
        // 返回当前(这个方法是在哪个线程中被调用的)线程的引用
        Thread t = Thread.currentThread();
        System.out.println("当前线程的name是:"+t.getName());
    }
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.setName("t1");
        t1.start();
        MyThread t2 = new MyThread();
        t2.setName("t2");
        t2.start();
        MyThread t3 = new MyThread();
        t3.setName("t3");
        t3.start();
        printCurrentThreadAttributes();
    }
}

看一下结果吧👇
Thread.currentThread()演示


总结

哈哈哈哈哈今天就到这里吧,我累了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值