大家好,又见面了,我是你们的朋友全栈君。
文章目录
一、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中的值进行更改,那么子线程取到的值还是旧值。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132261.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...