多线程的有关知识,你一定要懂!!!

多线程

多线程生命周期

六种线程状态

new:这里可以通过new Thread创建一个新的线程

runnable:这里是线程的运行区,他其实jvm里面不区分Ready和running区域的,因为这两者之间切换实在太快,就放在一个区域里面了。

terminated:当线程运行结束,就会在这个区域被终止。

timed_wating:用户调用Thread.sleep的方法的时候会进入这个模式。

wating:与timed_wating不同的点在于,他是没有计时的等待状态,并且两者都会把锁给释放掉。线程如果调用Object.wait或者Thread.join方法也会释放掉锁。

blocked:阻塞状态,当用户进入runnable以后,此时没有多余的锁给线程调用,线程就会进入阻塞状态,这个状态相当于是一个阻塞队列,线程在这里排队获取锁进入到runnable中去。

join和wait方法为什么前者属于Thread中,而后者属于Obejct中?

先说结论:join是控制线程,wait是控制对象上的锁。

如果我们使用Obejct类,我们创建的就是一个针对对象的操作,比如wait吧,我们先理解一个问题,锁是在哪里的?

锁是存在于我们要获取到的对象身上的,假如我们的线程A获取到了对象身上的锁,我们直接使用对象.wait()。

下面这个情况就是线程A获取锁以后,主线程在没有获取锁的情况下进行notify:

@Log4j
public class WaitTest {
 
    public static void main(String[] args) {
        Object lock = new Object();
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                log.info("获取了锁");
                try {
                    log.info("休眠一会儿");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("调用wait..");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
 
                log.info("被唤醒");
            }
        }, "A");
        threadA.start();
 
        lock.notify();
 
    }
}

这样会报错

那我们要怎么调用这个notify呢?

我们要通过线程B先去获取这个锁,然后调用notify,就可以唤醒A。

@Log4j
public class WaitTest {
 
    public static void main(String[] args) {
        Object lock = new Object();
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                log.info("获取了锁");
                try {
                    log.info("休眠一会儿");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("调用wait..");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
 
                log.info("被唤醒");
            }
        }, "A");
        threadA.start();
 
        Thread threadB = new Thread(()->{
            synchronized (lock) {
                log.info("获得了锁");
 
                log.info("叫醒A");
                lock.notify();
            }
        }, "B");
        threadB.start();
    }
}

上面这个例子,就很形象的举例出来了,使用Object获取锁释放锁。

那么join呢?他又是干什么的?

Java多线程中,wait()和join()的区别,带你入门不放弃 - 简书

这篇文章深刻的解析了wait和join的区别,其中有句话,是我们一定要掌握的。

wait就是让当前线程释放锁,join就是让当前释放锁,并且将这把锁交给join的线程,等他运行完毕再继续自己的线程。

public class Main{
    public static void main(String[] args) {
        System.out.println("Main 线程 开始运行!");
         Thread t1 = new Thread(){
             @Override
             public void run(){
                 System.out.println("t1 开始运行!");
                 System.out.println("t1 结束运行!");
             }
         };
         try{
             t1.start();
             t1.join();
         }catch(Exception e){
             
         }
         System.out.println("Main 线程 结束运行!");
    }
}
/*打印结果为:
Main 线程 开始运行!
t1 开始运行!
t1 结束运行!
Main 线程 结束运行!*/

类似于这样。


join的底层

那么现在有个问题,我们可以先join再start嘛?

我们来看看底层源码:

public final void join(long millis) throws InterruptedException {
        synchronized(lock) {//主线程拿到lock锁
        long base = System.currentTimeMillis();

        if (millis == 0) {
            while (isAlive()) { //由于该线程已经start(),所以视为alive
                lock.wait(0);  
                //主线程释放锁,进入无限期的等待状态。
               //直到子线程完成run,释放锁,然后主线程会重新拿到锁头继续运行
               //拿到锁之后,isAlive()不成立了,所以退出while循环!!
            }
        } else {
            }
        }
        }
    }

先说结论,不可以!

为什么不可以?我们调用start()方法的时候,他这个isAlive变量会变成true代表线程存活,如果不调用,那么就不会变成true,join方法整个无法生效。

join的底层就是wait只有线程存活的时候,wait才能生效。

那么读者看到这里可以猜一下,我们刚刚那个例子join和start互换以后,输出的结果是什么呢?

显然join无法生效,线程start以后,主线程会继续执行,结果就是:

Main 线程 开始运行!
Main 线程 结束运行!
t1 开始运行!
t1 结束运行!

有意思吧!这就是底层源码的魅力。

join为什么可以让主线程休息?

因为我们前面说过了,join的本质就是wait,wait干嘛啦?让当前运行线程释放锁了啊!


同样看上面代码:

  • 主线程进入join()方法

  • 主线程拿到子线程的lock锁

  • 进入同步代码快

  • while (isAlive()) 成立,因为先调用了start()方法

  • 调用 lock.wait(0), 主线程释放锁,进入wait状态

  • 子线程开始执行,执行结束会调用lock.notifyAll(),通知主线程获得锁。

  • 主线程重新启动, while (isAlive()) 已经不成立(由于子线程不再是alive状态)

  • 主线程继续往下运行。

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值