线程问题(守护线程、死锁、线程生命周期、生产者消费者模式)

目录:

1.守护线程

2.死锁

3.线程的生命周期

4.关于Object类下面的方法和线程有关

5.生产者消费者模式【难点】

1、守护线程

方法:    thread.setDaemon(true);

守护线程是用来守护非守护线程的。

非守护线程:就是平常写的线程

一个应用程序中至少有一个非守护线程

非守护线程一旦结束,守护线程就会自动消亡

main主函数是非守护线程

真实开发中:   后台记录操作日志,监控内存,垃圾回收等 都可以使用守护线程

class MyThread1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("子线程正在执行" + i);
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {//在非守护线程中执行的
        //启动线程
        MyThread1 myThread1 = new MyThread1();
        Thread thread = new Thread(myThread1);
        //可以将myThread1设置为守护线程。
        //就会在非守护线程结束的一瞬间,守护线程也会停止执行
        thread.setDaemon(true);//变成了守护线程了
        thread.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("主线程正在执行" + i);
        }
        //在jvm中  main主线程, 还有一个线程 垃圾回收线程
        //等到main主线程和垃圾回收线程 都结束De时候才守护线程才消亡!!!

    }
}

2、死锁

死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁 线程。

开发中禁止使用死锁。

面试会问:

应用场景:并发场景,多线程。线程之间互不相让。得借助锁。

加锁的目的是为了线程安全,但是物极必反。尤其是加了锁以后。

死锁是一种状态,当两个线程互相持有对方的资源的时候,却又不主动释放这个资源的时候。会导致死锁。这两个线程就会僵持住。代码就无法继续执行。

class DeadLock implements Runnable {
    private boolean flag;//标记属性
    private Object obj1;//锁住的对象
    private Object obj2;//锁住的对象

    public DeadLock(boolean flag, Object obj1, Object obj2) {
        this.flag = flag;
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        //true
        if (flag) {//如果设置为true,就让线程1进入到if语句中
            synchronized (obj1) {//锁住的是obj1对象
                //线程1持有obj1锁
                System.out.println(Thread.currentThread().getName() + "拿到了锁1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("等待锁2的释......");
                //我想在线程1中去使用线程2中的那个锁2 obj2
                //线程1里面想用obj2锁对象
                //也走不下去了
                //线程1也没有释放obj1
                synchronized (obj2) {
                    System.out.println("123");
                    System.out.println(Thread.currentThread().getName() + "拿到了锁1");

                }
            }
        }
        if (!flag) {//如果设置为false,就让线程2进入到if语句中
            synchronized (obj2) {//锁住的是obj2对象
                //线程2持有obj2这个锁
                System.out.println(Thread.currentThread().getName() + "拿到了锁2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("等待锁1的释......");
                //只有obj1释放掉以后,才能在线程2中对obj1加锁
                //想一个问题,如果obj1锁对象没有被释放,那么下面这个代码
                //线程2中去锁obj1
                //在这等着呢 往下走不下去了 线程2没有释放obj2对象
                synchronized (obj1) {
                    System.out.println("456");
                    System.out.println(Thread.currentThread().getName() + "拿到了锁1");
                }
            }
        }

    }
}
public class Demo1 {
    public static void main(String[] args) {
        Object obj1 = new Object();
        Object obj2 = new Object();
        //线程1可以进入到run方法中 if (flag)
        DeadLock deadLock = new DeadLock(true, obj1, obj2);
        new Thread(deadLock, "线程1").start();
        //线程2 可以进入倒run方法中if(!flag)
        DeadLock deadLock1 = new DeadLock(false, obj1, obj2);
        new Thread(deadLock1, "线程2").start();
    }
}

3.线程生命周期【面试】

1)新建:当一个Thread类或其子类的对象被声明并创建时。新生的线程对象属于新建状态。

(2)就绪(可运行状态):处于新建状态的线程执行start()方法后,进入线程队列等待CPU时间片,该状态具备了运行的状态,只是没有分配到CPU资源。

(3)运行:当就绪的线程分配到CPU资源便进入运行状态,run()方法定义了线程的操作。

(4)阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的的执行,进入阻塞状态。

(5)死亡:当线程执行完自己的操作或提前被强制性的终止或出现异常导致结束,会进入死亡状态。

4.和线程相关的Object类下面的方法

Object类下面的方法:

public final void wait()
          throws InterruptedException
voidnotify()唤醒正在等待对象监视器的单个线程。
voidnotifyAll()唤醒正在等待对象监视器的所有线程。

使用对象 . wait( )导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。 换句话说,这个方法的行为就好像简单地执行呼叫wait(0)

总结:至少两个线程,其中一个线程中使用对象.wait() 那么这个线程就会阻塞,代码不会往下执行了。如何想让这个线程往下执行呢?再开另外一个线程,使用对象.notify()去唤醒另外那个等待线程。

//创建这个类的目的,就是实例化出来对象,然后拿这个对象
//调用wait方法和notify方法
//wait方法和notify方法是object对象的方法
class Message {
    private String message;

    public Message(String message) {

        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "Message{" +
                "message='" + message + '\'' +
                '}';
    }

}
//导致当前线程等待,直到另一个线程调用该对象的[`notify()`
class WaiterThread implements Runnable {
    //想一个问题?WaiterThread  使用message对象调用
    //wait() 咋解决?
    private Message msg;

    public WaiterThread(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        //先获取当前线程名字
        String name = Thread.currentThread().getName();
        System.out.println(name + "等待唤醒时间:" +System.currentTimeMillis());
        //让等待线程去阻塞,去等待  这个线程执行不下去了
        //锁的是msg对象
        synchronized (msg) {//为啥是哟个wait的时候要加锁?等会将
            try {
                msg.wait();//代码走到这,当前这个线程阻塞,不往下走了
                //咱们得想办法让这个等待线程继续执行下去,咋办?
                //在另外一个线程中去调用notify方法那么等待线程就会执行下去
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("123");
            System.out.println(name + "被唤醒的时间:" + System.currentTimeMillis());
        }


    }
}
//唤醒线程
class NotifyThread implements Runnable {
    //也要用同一个对象是WaiterThread线程中同一个对象调用notify()方法
    private Message msg;

    public NotifyThread(Message msg) {
        this.msg = msg;
    }
    @Override
    public void run() {
        try {
            //我的想法是不能先让唤醒线程执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String name = Thread.currentThread().getName();
        System.out.println(name + "开始唤醒等待线程");
        synchronized (msg) {
            msg.setMessage("我是修改之后的message值");
            //msg.notify();
            msg.notifyAll();//唤醒所有线程
        }

    }
}
public class Demo1 {
    public static void main(String[] args) {
        Message message = new Message("我是message属性");
        WaiterThread waiterThread = new WaiterThread(message);
        NotifyThread notifyThread = new NotifyThread(message);
        //如果等待线程好几个 咋办呢?
        new Thread(waiterThread, "等待线程1").start();
        new Thread(waiterThread, "等待线程2").start();
        new Thread(waiterThread, "等待线程3").start();
        new Thread(notifyThread, "唤醒线程").start();

        //等待线程等待唤醒时间:1660187660718   等待线程
        //唤醒线程开始唤醒等待线程        唤醒线程
        //123  等待线程
        //等待线程被唤醒的时间:1660187661740  等待线程
        //这叫线程之间的通信问题!!!
    }
}

4.1、总结

新建两个线程:
    一个是等待线程:
        线程里面的代码从上往下执行的,但是使用object.wait(),就这个方法一用,你的线程就阻塞了,就处于等待状态。意味着当前的代码到了wait方法以后的代码暂时不执行了
    另外一个是唤醒线程。
        唤醒线程中使用object.notify()方法,这个方法是专门唤醒刚才那个等待线程。让等待线程继续执行

在使用waitnotify的时候都加了锁,是为了知道要对哪个对象进行唤醒的!!

wait需要持有锁的原因是,你肯定需要知道在哪个对象上进行等待,如果不持有锁,将无法做到对象变更时进行实时感知通知的作用。与此同时,为了让其他线程可以操作该值的变化,它必须要先释放掉锁,然后在该节点上进行等待。不持有锁而进行wait,可能会导致长眠不起。而且,如果不持有锁,则当wait之后的操作,都可能是错的,因为可能这个数据已经过时,其实也叫线程不安全了。总之,一切为了安全,单独的wait做不成这事。

扩展:

Thread类join方法

join方法,因为join()方法底层是就是通过wait()方法实现的。
让"主线程"等待(WAITING状态),一直等到其他线程不再活动为止,然后"主线程"再执行
join在英语中是“加入”的意思,join()方法要做的事就是,当有新的线程加入时,主线程会进入等待状态,一直到调用join()方法的线程执行结束为止。
面试题:
    如何先让所有的子线程执行,最后再执行主线程,咋解决?
        join!!!

加join的目的是可以让主线程和子线程可控
如果不加join的话,主线程和子线程随机交叉打印!!!

5.生产者消费者模式【重点难点】

买家想买一辆 比亚迪汉 , 告知汽车厂商我要买车。这个买家会进入到等待状态

等到比亚迪厂家造完车以后,再通知买家来提车。如果比亚迪厂家有现车,买家就直接提车。

如果产品需要生产的话,消费者进入到阻塞状态 wait

如果产品不需要生产的话,消费者直接购买


//为啥要新建这类Goods?
//还记得上午讲的Message类
//因为两个线程要通信。这个Goods就是通信的桥梁!!!
//goods.wait()  消费者等待
//goods.notoify()  生产者唤醒消费者
class Goods {
    private String name;//商品名字
    private double price;//商品价格
    private boolean isProduct;//
    //isProduct是否有这个商品, true  没有这个产品需要生产
    //false  有这个产品,不需要生产
    //有参构造


    public Goods(String name, double price, boolean isProduct) {
        this.name = name;
        this.price = price;
        this.isProduct = isProduct;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public boolean isProduct() {
        return isProduct;
    }

    public void setProduct(boolean product) {
        isProduct = product;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "name='" + name + '\'' +
                ", price=" + price +
                ", isProduct=" + isProduct +
                '}';
    }
}
//接下来搞两个线程?一个消费者线程  一个是生产者线程
class Customer implements Runnable {
    //为啥要定义这个goods变量? 因为两个线程要共享同一个资源!!!

    private Goods goods;

    public Customer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        //写业务逻辑,业务比较麻烦
        while (true) {//一直消费
            synchronized (goods) {
                //goods.isProduct  true 需要生产,没有商品  false 不需要生产
                if (!goods.isProduct()) {
                    //不需要生产的,消费者直接购买
                    System.out.println("消费者购买了:" + goods.getName() + ",价格为:" + goods.getPrice());
                    //购买完以后 商品没有了 将isProduct这个变量修改为true
                    goods.setProduct(true);
                    //大家有没有想过这个问题?消费者在购买的时候,生产者等待
                    //唤醒生产者去生产车了
                    goods.notify();//可以先防一下,等会再看!!!


                } else {
                    //需要生产的!!!,没商品(咋办)
                    //消费者进入到阻塞状态!!!
                    try {
                        goods.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }

            }
        }

    }
}
class Producter implements  Runnable {
    //为啥要定义这个goods变量?
    private Goods goods;
    public Producter(Goods goods) {
        this.goods = goods;
    }
    @Override
    public void run() {
        int count = 0;
        //生产者,业务逻辑比较麻烦
        while (true) {//你一直消费,我一直生产
            synchronized (goods) {
                if (goods.isProduct()) {//true  需要生产的!!
                    //造车,就是赋值 对goods对象进行赋值
                    //奇数造一种车, 偶数造另外一种车
                    if (count % 2 == 0) {//偶数
                        goods.setName("奥迪A8");
                        goods.setPrice(78.9);
                    } else {//奇数
                        goods.setName("五菱大光");
                        goods.setPrice(12.9);
                    }
                    //生产一辆车,一定要记得有车了
                    //标记就改为 false  就证明不需要再生产了
                    goods.setProduct(false);
                    System.out.println("生产者生产了:" + goods.getName() + ",价格为:" + goods.getPrice());

                    count++;
                    //生产完以后,唤醒消费者。让消费者去提车
                    goods.notify();

                } else {
                    //不需要生产
                    //不需要生产 有货,生产着歇着,阻塞
                    try {
                        goods.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Goods goods = new Goods("东风41", 67.8, false);
        Producter producter = new Producter(goods);
        new Thread(producter).start();

        Customer customer = new Customer(goods);
        new Thread(customer).start();
        /**
         * 谁先抢到线程?消费者?还是生产者?
         * //如果是消费者抢到执行权,不用说!!!直接打印消费者购买了东风
         * //如果生产者抢到执行权,生产者wait,那就意味着必须去执行消费者线程
         * 消费者购买了:东风41,价格为:67.8
         * //此时isProduct是true 需要时生产
         * 还是消费者和生产者抢这个执行权
         * //假如生产者抢到了  就会打印生产者生产了啥。
         * //假如消费者抢到了执行权,消费者进入倒阻塞状态!!!
         * //消费者进入倒阻塞,那么肯定生产者得执行了
         * 生产者生产了:奥迪A8,价格为:78.9
         *
         * 还是两个线程抢这个执行权
         * 消费者购买了:奥迪A8,价格为:78.9
         * 生产者生产了:五菱大光,价格为:12.9
         * 消费者购买了:五菱大光,价格为:12.9
         * 生产者生产了:奥迪A8,价格为:78.9
         * 消费者购买了:奥迪A8,价格为:78.9
         * 生产者生产了:五菱大光,价格为:12.9
         * 消费者购买了:五菱大光,价格为:12.9
         * 生产者生产了:奥迪A8,价格为:78.9
         * 消费者购买了:奥迪A8,价格为:78.9
         */
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值