多线程的方法和生产者消费者问题

多线程2

守护线程:

守护线程相对于正常线程来说,是比较特殊的一类线程。它的作用就是用来守护非守护线程的

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

在官方文档中提到:

当 JVM 中不存在任何一个正在运行的非守护线程时,则 JVM 进程即会退出。

即非守护线程一旦结束,守护线程就会自动消失。因此一个应用程序中必须至少有一个非守护线程。

守护线程代码的书写

package com.lyc.lesson;

class MyTest implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程正在执行:"+i);
        }
    }
}

public class Lesson07 {
    public static void main(String[] args) {
        MyTest myTest = new MyTest();
        Thread thread = new Thread(myTest);
        //setDaemon():将线程设置为守护线程,true :守护线程; false:非守护线程
        thread.setDaemon(true);
        thread.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main线程正在执行:"+i);
        }
    }
}

输出结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DJpiiUb1-1660220628212)(day23作业.assets/image-20220811192246968.png)]

在主线程结束后,我们发现守护线程并没有立即结束,是因为,在JVM中,除了main主线程,还有一个垃圾回收线程,只有当所有的非守护线程结束,守护线程才会结束

死锁:

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YODfsgF2-1660220628214)(day23作业.assets/image-20220811193559580.png)]

如图:
线程1 有锁1 ------> 线程1等待锁2的释放去获取锁2的资源
线程2 有锁2 ------> 线程2等待锁1的释放去获取锁1的资源
这样就会产生死锁。
实现代码如下:

package com.lyc.lesson;

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() {
        if (flag) {   //为true,让线程1进入
            synchronized (obj1){
                System.out.println(Thread.currentThread().getName()+"拿到了锁1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("等待锁2释放....");
                //线程1执行锁2
                synchronized (obj2){
                    System.out.println(Thread.currentThread().getName()+"拿到锁2");
                }
            }
        }
        if (!flag) { //为false,让线程2进入
            synchronized (obj2){
                System.out.println(Thread.currentThread().getName()+"拿到了锁2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("等待锁1释放....");
                //线程2执行锁1
                synchronized (obj1){
                    System.out.println(Thread.currentThread().getName()+"拿到锁1");
                }
            }
        }
    }
}

public class Lesson04 {
    public static void main(String[] args) {
        Object obj1 = new Object();
        Object obj2 = new Object();
        DeadLock deadLock = new DeadLock(true,obj1 ,obj2);
        new Thread(deadLock,"线程一").start();
        DeadLock deadLock1 = new DeadLock(false,obj1 ,obj2);
        new Thread(deadLock1,"线程二").start();
    }
}

输出结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VCg5YVD2-1660220628214)(day23作业.assets/image-20220811194518504.png)]

我们从图中可以看出,线程1、2分别在等待对方锁的释放,这就产生了死锁,且程序一直会一直运行,不会停止。

线程的生命周期:

线程的生命周期包含5个阶段,包括:新建就绪运行阻塞销毁

  • 新建: 就是刚使用 new 方法,new 出来的线程;
  • 就绪: 就是调用的线程的 start() 方法后,这时候线程处于等待 CPU 分配资源阶段,谁先抢到 CPU 资源,谁开始执行;
  • 运行: 当就绪的线程被调度并获得 CPU 资源时,便进入运行状态,run() 方法定义了线程的操作和功能;
  • 阻塞: 在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如 sleep()、wait() 之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用 notify 或者 notifyAll() 方法。唤醒的线程不会立刻执行run 方法,它们要再次等待 CPU 分配资源进入运行状态;
  • 销毁: 如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;

图示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eIaYvj4F-1660220628215)(day23作业.assets/image-20220811195913927.png)]

和线程相关的Object类下的方法:
返回值方法名详情
voidwait()导致当前线程等待
voidnotify()唤醒正在等待对象监视器的单个线程
voidnotifyAll()唤醒正在等待对象监视器的所有线程。

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

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

案例:

package com.lyc.lesson;

class Message {
    private String name;

    public Message(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Message{" +
                "name='" + name + '\'' +
                '}';
    }
}
//等待线程
class WaitThread implements Runnable{
    private Message meg;

    public WaitThread(Message meg) {
        this.meg = meg;
    }

    public Message getMeg() {
        return meg;
    }

    public void setMeg(Message meg) {
        this.meg = meg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+"等待唤醒时间:"+System.currentTimeMillis());
        synchronized (meg){
            try {
                meg.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("123");
            System.out.println(name+"被唤醒的时间:"+System.currentTimeMillis());
        }
    }
}
//唤醒线程
class NotifyThread implements Runnable{
    private Message meg;

    public NotifyThread(Message meg) {
        this.meg = meg;
    }

    public Message getMeg() {
        return meg;
    }

    public void setMeg(Message meg) {
        this.meg = meg;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String name = Thread.currentThread().getName();
        System.out.println(name+"开始唤醒等待线程");
        synchronized (meg){
            meg.setName("修改之后的message的值");
            meg.notify();
        }
    }
}

public class Lesson6 {
    public static void main(String[] args) {
        Message message = new Message("我是message的属性");
        WaitThread waitThread = new WaitThread(message);
        Thread thread = new Thread(waitThread,"等待线程");
        NotifyThread notifyThread = new NotifyThread(message);
        Thread thread1 = new Thread(notifyThread,"唤醒线程");

        thread.start();
        thread1.start();
    }
}

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

jion()方法:

Thread类join方法
join()方法,因为join()方法底层是就是通过wait()方法实现的。
让"主线程"等待(WAITING状态),一直等到其他线程不再活动为止,然后"主线程"再执行
你可以简单的理解为,当线程使用了jion()方法后,这个线程就可以比没有使用join()的线程(‘‘主线程’’)先执行,类似于插队的感觉。当调用join()方法的线程都执行完成后,才轮到’‘主线程’'。

案例:

package com.lyc.lesson;

class JoinTest implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("使用了join方法的线程:"+i);
        }
    }
}

public class Lesson08 {
    public static void main(String[] args) throws InterruptedException {
        JoinTest test = new JoinTest();
        Thread thread = new Thread(test);
        thread.start();
        thread.join();
        
        for (int i = 0; i < 20; i++) {
            System.out.println("主线程:"+i);
        }
    }
}

输出结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qUeRF5gX-1660220628216)(day23作业.assets/image-20220811201521240.png)]

生产者消费者模式:

生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。
该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。

生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与比同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

需求:

生产者生产车,消费者买车,假设消费者想买一辆车 ,告知汽车厂商我要买车。这时消费者会进入到等待状态(wait())。等到生产者造完车以后,再通知(notify())消费者来提车。如果生产者有现车,消费者就直接提车。

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

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

代码如下:

package com.lyc.lesson;

//商品,作为生产者线程和消费和线程的桥梁,将两者联系起来
class Goods{
    private String name;
    private double price;
    private boolean 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 Costumer implements Runnable{
    private Goods goods;

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

    public Goods getGoods() {
        return goods;
    }

    public void setGoods(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        while (true){  //一直消费
            synchronized (goods) {
                if (goods.isProduct()) {   //true ,有商品
                    System.out.println("消费者购买了:" + goods.getName()
                            + ",花费了:" + goods.getPrice() + "元");
                    //购买过后假设没有商品了,这是就要改变isProduct()的状态
                    goods.setProduct(false);  //false表示没有商品了

                    //没有商品,要唤醒生产者去生产
                    goods.notify();
                } else {
                    //没有商品,消费者就进入阻塞状态,等待有商品
                    try {
                        goods.wait();   //使用了wait()就要加锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
//生产者线程
class Product implements Runnable{
    private Goods goods;

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

    public Goods getGoods() {
        return goods;
    }

    public void setGoods(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        int count = 0;
        while (true){  //一直生产商品
            synchronized (goods){
                if (!goods.isProduct()){   //!goods.isProduct()  没有车
                   if (count % 2 == 0){
                        goods.setName("劳斯莱斯");
                        goods.setPrice(1000000);
                   }else {
                        goods.setName("玛莎拉蒂");
                        goods.setPrice(800000);
                   }
                    goods.setProduct(true);   //将isProduct()状态改为有
                    System.out.println("生产者生产了:"+goods.getName()+",价格为:"+goods.getPrice());
                    count++;
                    //产品生产好之后,要唤醒消费者消费
                    goods.notify();
                }else {
                    //产品生产好之后,生产者自身要进入到阻塞状态
                    try {
                        goods.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}


public class Lesson05 {
    public static void main(String[] args) {
        Goods goods = new Goods("五菱宏光", 30000, true);
        //不需要生产,生产者阻塞
        Product product = new Product(goods);
        new Thread(product).start();

        Costumer costumer = new Costumer(goods);
        new Thread(costumer).start();
    }
}

e) {
e.printStackTrace();
}
}
}
}
}
}

public class Lesson05 {
public static void main(String[] args) {
Goods goods = new Goods(“五菱宏光”, 30000, true);
//不需要生产,生产者阻塞
Product product = new Product(goods);
new Thread(product).start();

    Costumer costumer = new Costumer(goods);
    new Thread(costumer).start();
}

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值