多线程&JUC:线程的生命周期与安全问题

‍作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
上期文章:多线程&JUC:多线程的实现和常用成员方法(守护、礼让、插入线程)
订阅专栏:多线程&JUC
希望文章对你们有所帮助

这一部分内容似乎有在面经中看到过,所以做一个小小的总结,线程的安全问题的解决方案在这里不给出解决,给出一个最常见的例子给大家看看,解决方法放在下一篇文章。

线程的生命周期和安全问题

  • 线程的生命周期
  • 线程的安全问题

线程的生命周期

所谓生命周期,无非就是线程的所有状态的转化,就像人的一生一样,线程的状态有如下5种:

1、新建:创建线程对象
2、就绪:有执行资格,没有执行权(正在抢CPU执行权,但是不一定抢到)
3、运行:有执行资格,有执行权(抢到了CPU的执行权)
4、死亡:线程死亡,变成垃圾
5、阻塞:没有执行资格,没有执行权(不能去抢CPU的执行权)

而整个过程中,状态转换图如下所示:
多线程&JUC:线程的生命周期与安全问题_第1张图片
sleep方法会让线程睡眠,睡眠时间到了之后,立马就会执行下面的代码吗?

不会,因为sleep时间到了以后,变回就绪状态,具有抢夺CPU执行权的权利,但是没有抢到之前没办法执行下面的代码。

线程的安全问题

这类问题其实我们之前教javase的老师稍微讲过了一点,演示了会发生的安全问题,但是并没有给出解决的方法,而是当作一个课程练习了。
相信大家也是经常听到这样类似的例子,也就是同时买票的例子,假设有100张票,3个买票窗口,模拟3个线程同时抢票。

线程任务:

public class MyThread extends Thread {

    int ticket = 0;

    @Override
    public void run() {
        while(true){
            if(ticket < 100){
                ++ticket;
                System.out.println(getName() + "正在卖" + ticket + "张票!");
            }else{
                break;
            }
        }
    }
}

测试类:

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

最后的运行结果显示,出现了超卖现象:
多线程&JUC:线程的生命周期与安全问题_第2张图片
很显然,ticket这个属性根本没有在共用,所以可以尝试将ticket设置为静态变量使得其变得共用:

static int ticket = 100;

但是运行后发现,除了还会发生多个窗口卖同一张票的问题,ticket甚至还超出了100,后续将会使用同步代码块、同步方法、锁等方式解决这类问题。

其实这类问题放在项目中,做Redis的时候就解决过了,可以看下面文章:
Redis:原理速成+项目实战——Redis实战7(优惠券秒杀+细节解决超卖、一人一单问题)
除了单体情况下,集群情况下也会发生这类线程安全问题,也有相应的解决方案:
Redis:原理速成+项目实战——Redis实战8(基于Redis的分布式锁及优化)

你可能感兴趣的:(多线程&JUC,安全,java,JUC,面试,javase)