join方法的使用

join方法的使用一、join方法1.1jon方法的作用使所属的线程对象x正常执行run()方法中的任务,而使当前线程y无限期的阻塞,直到x线程销毁后再继续执行线程y后面的代码。join方法具有使线程排队运行的作用,有些类似同步的运行的效果。1.2join与synchronized的区别join在内部使用wait()方法进行等待,而synchronized关键字使用的是”对象监视器”原理作为同步。…

大家好,又见面了,我是你们的朋友全栈君。

一、join方法

1.1 jon方法的作用

使所属的线程对象x正常执行run()方法中的任务,而使当前线程y无限期的阻塞,直到x线程销毁后再继续执行线程y后面的代码。

join方法具有使线程排队运行的作用,有些类似同步的运行的效果。

1.2 join与synchronized的区别

join在内部使用wait()方法进行等待,而synchronized关键字使用的是”对象监视器”原理作为同步。

1.3 方法join与异常

在join过程中,如果当前线程对象被中断,则当前线程出现异常。比如join方法遇到interrupt(),可能会出现异常。

1.4 方法join(lang)的使用

lang是时间的参数
myThread类:

public class myThread extends Thread{
    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + "开始时间:" + System.currentTimeMillis());
            Thread.sleep(50000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试代码:

public class test {
    public static void main(String[] args) {
        try {
            myThread myThread = new myThread();
            myThread.start();
            myThread.join(2000);
            System.out.println(Thread.currentThread().getName() + "结束时间:" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

Thread-0开始时间:1557799194421
main结束时间:1557799196422

将join(2000)改成slepp(2000)效果一样,但是sleep()与join()对同步的处理上有区别:

1.5 join(long)与sleep(long)的区别

  • 方法long是在内部使用wait()方法来实现的,所以join(long)具有释放锁的特点
    join方法源码:
public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

当执行到wait()方法后,当前线程的锁被释放,其他线程就可以调用此线程中的同步方法了。
而Thread.sleep(long)却不释放锁。

1.6 join()方法的特点

join()方法大部分时间里先抢到锁,然后快速释放锁。

二、类TheadLoacl的使用

1.1 ThreadLoca类的背景

让每个线程都有自己私有的共享变量,而不是所有的线程都拥有一个共享变量。
该类解决的是变量在不同线程之间的隔离性的问题,也就是不同线程拥有自己的值,不同线程的值是可以放入Threadlocal类中进行保存的。

问题:
用同一个ThreadLocal对象,给不同两个线程设置私有共享变量:

public class test {
    public static void main(String[] args) {
       ThreadLocal local = new ThreadLocal();
        System.out.println(local.get());
        local.set("你好");
        System.out.println(local.get());
    }
}

1.2 验证线程变量的隔离性

子线程:

public class myThread extends Thread{
    ThreadLocal local;
    public myThread(ThreadLocal local){
        this.local = local;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            local.set("次线程" + i);
            System.out.println(local.get());
        }
    }
}

主线程:

public class test {
    public static void main(String[] args) {
        ThreadLocal local = new ThreadLocal();
        myThread my = new myThread(local);
        my.start();
        for (int i = 0; i < 100; i++) {
            local.set("主线程" + i);
            System.out.println(local.get());
        }
    }
}

结果:

"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=53954:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;D:\untitled2\out\production\untitled2" backup.test
主线程0
主线程1
主线程2
主线程3
次线程0
次线程1
次线程2
主线程4
主线程5

三、类InheritableThreadLocal

3.1 作用

在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。通常,子线程的值与父线程的值是一致的;但是,通过重写这个类中的 childValue 方法,子线程的值可以作为父线程值的一个任意函数。

3.2 子线程拿到父线程的值

类线程A继承主线程:

public class threadA extends Thread{
InheritableThreadLocal in = new InheritableThreadLocal();
public threadA(InheritableThreadLocal a){
    this.in = a;
}
    @Override
    public void run() {
        System.out.println(in.get());
    }
}

测试类:

public class test {
    public static void main(String[] args) {
        try {
            InheritableThreadLocal a = new InheritableThreadLocal();
            a.set("给主线程设置的值");
            Thread.sleep(1000);
            threadA threada = new threadA(a);
            threada.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

给主线程设置的值

子线程拿到了父线程设置的值。

3.3 常用方法

  • protected T childValue(T parentValue)
    parentvalue是从父线程继承下来的值,可以重写该函数,对继承下来的数据进行再一步的处理。
  • protected T initialValue()
    返回此线程局部变量的当前线程的“初始值”。线程第一次使用 get() 方法访问变量时将调用此方法,但如果线程之前调用了 set(T) 方法,则不会对该线程再调用 initialValue 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。
    该实现返回 null;如果程序员希望线程局部变量具有 null 以外的值,则必须为 ThreadLocal 创建子类,并重写此方法。通常将使用匿名内部类完成此操作。

3.4 子线程对拿到的父线程的值进行处理

test类:

public class test {
    public static void main(String[] args) {
        try {
            InheritableThreadLocal a = new InheritableThreadLocal(){
                @Override
                protected Object childValue(Object parentValue) {
                    return parentValue + "子线程将得到的父线程的值处理了一下";
                }
            };
            a.set("给主线程设置的值");
            Thread.sleep(1000);
            threadA threada = new threadA(a);
            threada.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:
给主线程设置的值子线程将得到的父线程的值处理了一下

3.4 注意事项

如果子线程在获取值的同时,主线程将InteritableThreadLocal中的值进行更改,那么子线程取到的值还是旧值。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/132261.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python & 0xFFFFFFFF打印输出负数的补码[通俗易懂]

    python & 0xFFFFFFFF打印输出负数的补码[通俗易懂]-7的有符号原码和补码表示-7的原码:0b10000111(8位)-7的补码:0b11111001(8位)python中的输出a=-7print(bin(a))#输出-0b111python直观地打印输出了带负号的原码显示为了能够打印输出对应的补码表示进行如下运算:a=-7b=a&0xFF#-7的补码print(b)#249print(0b11111001)print(bin(b)#输出249//249原码0

    2022年5月17日
    108
  • 关于SM总线控制器驱动的安装

    关于SM总线控制器驱动的安装没有装SM总线控制器的再设备管理器看起来是这样的:虽然说,这个控制器不装对日常简单应用没有多大影响,但是为了保证计算机的性能,避免在使用过程中出现各种奇怪的问题,不装是不行的。下面开始安装,一般的驱动安装也可遵循此过程。首先解压ATISB600南桥驱动。我的版本是7.8的,解压默认再C:\ATI\********然后打开相应文件夹,如下图:红圈画的就是传说中的控制器驱动文件。…

    2022年6月6日
    107
  • 第10月第28天 touchesBegan hittest「建议收藏」

    第10月第28天 touchesBegan hittest「建议收藏」1.-(void)touchesBegan:(NSSet*)toucheswithEvent:(UIEvent*)event{[[selfnextResponder]touchesBegan:toucheswithEvent:event];[supertouchesBegan:toucheswithEvent:event];}-(voi…

    2022年7月25日
    8
  • 用java打印空心菱形_java打印空心直角三角形

    用java打印空心菱形_java打印空心直角三角形打印菱形尤其是空心菱形对于初学者来说有一定难度,熟练掌握了for循环后,还是可以打印出来的分析:1.先打印上半部分,再打印下半部分      2.找准空格与星号之间的关系,然后再打印      3.上半部分每一行数目分别为1、3、5、7、9、7、5、3、1      4.找准关系2*i-1     //练习二:打印菱形与空心菱…

    2022年9月29日
    3
  • sigterm信号_一文吃透 PHP 进程信号处理

    sigterm信号_一文吃透 PHP 进程信号处理背景前两周老大给安排了一个任务,写一个监听信号的包。因为我司的项目是运行在容器里边的,每次上线,需要重新打包镜像,然后启动。在重新打包之前,Dokcer会先给容器发送一个信号,然后等待一段超时时间(默认10s)后,再发送SIGKILL信号来终止容器现在有一种情况,容器中有一个常驻进程,该常驻进程的任务是不断的消费队列里的消息。假设现在要上线,需要关杀掉容器,Docker给容器里跑的常驻进程发送一个…

    2025年8月12日
    3
  • C#并发实战Parallel.ForEach使用

    C#并发实战Parallel.ForEach使用前言:最近给客户开发一个伙食费计算系统,大概需要计算2000个人的伙食。需求是按照员工的预定报餐计划对消费记录进行检查,如有未报餐有刷卡或者有报餐没刷卡的要进行一定的金额扣减等一系列规则。一开始我的想

    2022年7月4日
    26

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号