day22-线程

今天的内容【重要!!!】

1.进程

2.线程【重点】

1.什么是进程

是独立的运行程序

​ 比如咱们电脑软件,你启动起来以后,他就是一个进程。qq idea

进程需要windows系统的分配。可以获取当前的系统的网卡,内存,显卡等

​ 1.独立性

​ 各个进程之间是相互的独立的互不影响 的。录屏软件和idea没有关系的

​ 2.互斥性

​ 每个软件系统都会分配一个独立端口号,如果启动一个软件以后他的端口号97。

​ 如果再启动另外一个软件,另外一个软件如果端口也是97,这个程序就启动不了,端口被占用的

脑海里面知道开启的软件就是一个进程 即可!!!

2.什么是线程

进程是由多个或者一个线程组成的。每个进程至少得有一个线程的支撑。

脑海里面这样来想,一个进程(qq),qq里面很多个线程在执行。线程的执行

支撑起来了进程的执行。

把一个人比作一个进程,那么你身体里面的细胞就是线程。如果没有细胞。这个人还存在吗?不存在的!!!

进程包含了线程,线程是组成进程的最小基本单位

​ 特性:

​ 1.抢占式运行的【重要】

​ CPU在执行的时候,按照时间片来执行的,单位的时间片是抢占是执行

​ 比如 idea qq 抢占CPU的,比如qq的线程抢到cpu,idea线程等待

​ 我是一个cpu。你们问我问题。75个线程。同时来问我问题吗?不是

​ 抢着问。一个问。然后其他人等待。这个人甚至还没有问完,其他的某一个人

​ 抢到我了,他问我。大概就是效果

​ 2.资源共享性

​ 一个线程可以共享当前CPU, 网卡等

Java程序:

​ 一个Java程序就是一个进程 Demo1 就是一个应用程序 就是一个进程

​ 一个Java程序Demo1里面至少 几个线程?

​ 两个:

​ main主函数线程

​ JVM垃圾回收器线程

3.线程和进程的区别【面试题】

进程是一个应用程序,是独立的
线程是进程中最小的基本单位。
把进程比作生产车间,每个流水线就是一个线程
进程有独立性和互斥性
线程有抢占式资源共享特性

4.并发和并行

并发:同时发生,轮流交替执行

并行:真正意义的同时执行

比如:

​ 你去饭店点了两个菜,生活中拿个筷子轮流夹菜哦这就是并发场景

​ 端起盘子,同时倒到嘴里面,这就是并行场景

5.创建线程的两种方式【重点】

创建线程的两种方式

  1. 一个是将一个类声明为Thread的子类。 这个子类应该重写run方法 。 然后可以分配并启动子类的实例。
package com.qfedu.a_thread;

// 一个是将一个类声明为Thread的子类。
// 这个子类应该重写run类的方法Thread 。
// 然后可以分配并启动子类的实例。
class MyThread1 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("我是myThread1线程:" + i);
        }
    }
}
class MyThread2 extends Thread {
    @Override
    public void run() {//run方法中写功能代码  就是一个线程中执行的一个功能
        for (int i = 0; i < 500; i++) {
            System.out.println("我是mythread2线程:" + i);
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //官方手册中说,要去实例化Thread的子类,并启动线程
        MyThread1 myThread1 = new MyThread1();
        //启动线程 使用start方法  在主线程中开启子线程
        myThread1.start();
        MyThread2 myThread2 = new MyThread2();
        myThread2.start();
        //现在有几个线程?  3个
        //一个是MyThread1线程  一个是主线程(main)  一个垃圾回收机制线程
        for (int i = 0; i < 500; i++) {
            System.out.println("主函数线程:" + i);
        }
        //发现先执行了主线程,然后再执行子线程,然后又执行主线程
        //这就线程的抢占式的运行
        //三个线程:
        //你自己吃三盘菜,咋吃?
        //一盘菜代表一个线程,一盘菜夹一下,随机的吧。
        //开启一个线程,就是在执行一个任务。
        //上面这个代码,你们执行结果和我执行的结果一样吗?绝对不一样的
        //抢占式的,随机执行线程的!!!

    }
}
练习:
            main主线程 打印100遍的吃大盘鸡
            子线程1 打印100遍的吃水煮肉片
            子线程2 打印100遍的吃毛血旺
            一定要注意打印的结果,多执行几遍,看看每次执行的结果是否一样!!!

另一种方法来创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。

package com.qfedu.a_thread;

//另一种方法来创建一个线程是声明实现类Runnable接口。
// 那个类然后实现了run方法。 
// 然后可以分配类的实例(创建类的对象),在创建Thread实例时作为参数传递,并启动。
class MyThread3 implements Runnable {


    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread3:" + i);
        }
    }
}
class MyaThread4 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread4:" + i);
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        MyThread3 myThread3 = new MyThread3();
        //Thread(Runnable target)  参数是Runnable这个接口对象
        //分配一个新的 Thread对象。
        Thread thread = new Thread(myThread3);
        thread.start();
        MyaThread4 myaThread4 = new MyaThread4();
        Thread thread1 = new Thread(myaThread4);
        thread1.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main主线程:" + i);
        }
        //有几个线程做任务的线程  3个 一个是main主线程  一个是MyThread3
        //一个MyThread4这个线程
        //可以发现运行的结果是随机执行的!!!

    }
}

6.线程下面的几个方法

构造方法

Thread()分配一个新的 Thread对象。 无参构造方法
Thread(Runnable target)分配一个新的 Thread对象。 有参构造
Thread(Runnable target, String name)分配一个新的 Thread对象。并起名字

线程方法:

static ThreadcurrentThread()返回对当前正在执行的线程对象的引用
StringgetName()返回此线程的名称。
voidsetName(String name)将此线程的名称更改为等于参数 name
intgetPriority()返回此线程的优先级。
voidsetPriority(int newPriority)更改此线程的优先级。
设置优先并不一定优先,只是增加了执行的概率。最小值是1,最大值是10,默认的是5
static voidsleep(long millis)使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
package com.qfedu.b_threadfun;

class MyThread1 implements Runnable {
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        //此时这个thread对象是MyThread1这个线程
        //对子线程设置名字
        thread.setName("mythread1子线程");
        System.out.println(thread.getName());//Thread-0
    }
}
class MyThread2 implements Runnable {
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());//狗蛋
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //currentThread() 获取当前线程对象
        Thread thread = Thread.currentThread();
        //现在thread  这个对象是哪个线程?主线程
        //为啥是main主线程
        //给main主线程设置名字
        thread.setName("主线程");
        //获取的是main主线程的名字,Jvm会给主线程还有其他线程一个默认的名字

        System.out.println(thread.getName());//main
//        MyThread1 myThread1 = new MyThread1();
//        Thread thread1 = new Thread( myThread1);
//        thread1.start();

        new Thread(new MyThread1()).start();
        //Java中默认的主线程叫main  子线程叫Thread-0  Thread-1....
        //默认的有名字,咱们能不能对线程自定义名字?能

        //我在启动MyThread2的这个线程的时候顺便起名字
        MyThread2 myThread2 = new MyThread2();
        //myThread2线程的名字就叫狗蛋,是在创建线程的时候就已经起好名字了
        Thread thread1 = new Thread(myThread2, "狗蛋");
        thread1.start();


    }
}

package com.qfedu.b_threadfun;

class MyThread3 implements Runnable {
    @Override
    public void run() {
        //想看一下MyThread3这个线程的优先级是几
        Thread thread = Thread.currentThread();
        thread.setPriority(10);
        System.out.println(thread.getPriority());
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread3线程:" + i);
        }
    }
}
class MyThread4 implements Runnable {
    @Override
    public void run() {
        //想看一下MyThread3这个线程的优先级是几
        Thread thread = Thread.currentThread();
        thread.setPriority(1);
        System.out.println(thread.getPriority());
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread4线程:" + i);
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {

        //默认的优先级都是5,能不能手动去修改某一个线程的优先级?
        //可以
        Thread thread = Thread.currentThread();
        //主线程的优先级设置1
        //thread.setPriority(1);
        //获取主线程的优先级
        System.out.println(thread.getPriority());//5
        //优先级  1 ~10  1的优先级最低  10的优先级最高
        //jvm默认线程的优先级是5
//        for (int i = 0; i < 100; i++) {
//            System.out.println("主线程:" + i);
//        }
        new Thread(new MyThread3()).start();
        new Thread(new MyThread4()).start();
        //所以优先级不要用啦!!!并不一定真正的优先!!!
        //线程执行的结果不可控!!!很尴尬!!!
    }
}

package com.qfedu.b_threadfun;

class MyThread5 implements Runnable {
    @Override
    public void run() {
        try {
            //发现 Thread.sleep 有一个运行时异常,
            //但是发现没有抛出,只有try-catch 为啥?
            //sleep方法写在了run方法中了,因为
            //run方法是重写的, 父类的 public abstract void run();
            //父类有抛出吗?没有抛出,重写是比较严格的
            //父类没有抛出,子类也同样不能抛出
            Thread.sleep(10000);//10秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 500; i++) {
            System.out.println("MyThread5:" + i);
        }
    }
}
class MyThread6 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("MyThread6:" + i);
        }
    }
}
public class Demo3 {
    public static void main(String[] args) {
        new Thread(new MyThread5()).start();
        new Thread(new MyThread6()).start();
        //发现运行的结果是不可控的,是随机的抢占式的
        //咱们接下来学习一个东西叫sleep  让某一个线程睡一会儿
        //这个线程在睡觉的期间不会去抢占cpu  不执行
        //现在我让MyTread5睡了一会儿。就意味着绝对MyThread6线程先执行
        //实现可控的效果
        //思考:sleep方法再开发的时候敢用不敢用?
        //不敢用。睡多久你知道吗?睡多久合适?不能确定
        //cpu最大利用化。不可能让cpu闲置
        //如果sleep睡眠时间少的话,还是抢占
        //如果sleep睡眠你时间太长的话,就会cpu就会闲置
        //没有办法把控的!!! 接下来要学习锁!!!
    }
}

下午提问方法

7.线程的同步和锁

为什么要进行线程的同步?

Java是允许多线程,当多个线程操作同一个资源的时候,会导致得到或者打印的数据不准确。从而发生冲突。咋解决?加同步锁。

"F:\qianqian\HEIMA\1、Java就业工程师【千F】\01、第一阶段\day22\code\code\1.png"

分析结果:
package com.qfedu.a_thread;

class MySycn implements Runnable {
    //卖10张票
    int ticket = 50;
    @Override
    public void run() {
        while (true) {

            if (ticket > 0) {
                //线程1进到循环中了 在没有执行--之前,线程2 进到循环里面
                //ticket  线程1 是50   ticket 线程2也是50

                System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket-- + "票");
            }
        }

    }
}
public class Demo4 {
    public static void main(String[] args) {
        MySycn mySycn = new MySycn();
        Thread thread = new Thread(mySycn, "线程1");
        thread.start();
        Thread thread1 = new Thread(mySycn, "线程2");
        thread1.start();
        Thread thread2 = new Thread(mySycn, "线程3");
        thread2.start();
        //打印出来的数据有重复的数据出现,符合咱们的场景吗?
        //不符合,生活场景的
        //咋解决?
        //对代码加锁:
        //只能保持一个线程进来,其他线程在外面等待。等这个线程结束以后,其他线程再进来
        
    }
}

解决方案:

​ 1.同步方法:使用一个关键字synchronized修饰方法。因为Java对象都有一个内置的锁对象。当使用这个关键字的时候,修饰方法的时候,这个方法就会被锁保护

public synchronized  void run () {

}
package com.qfedu.a_thread;

class MySycn implements Runnable {
    //卖10张票
    int ticket = 50;
    @Override
    public  synchronized void run() {
        while (true) {

            if (ticket > 0) {
                //线程1进到循环中了 在没有执行--之前,线程2 进到循环里面
                //ticket  线程1 是50   ticket 线程2也是50

                System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket-- + "票");
            }
        }

    }
}
public class Demo4 {
    public static void main(String[] args) {
        MySycn mySycn = new MySycn();
        Thread thread = new Thread(mySycn, "线程1");
        thread.start();
        Thread thread1 = new Thread(mySycn, "线程2");
        thread1.start();
        Thread thread2 = new Thread(mySycn, "线程3");
        thread2.start();
        //打印出来的数据有重复的数据出现,符合咱们的场景吗?
        //不符合,生活场景的
        //咋解决?
        //对代码加锁:
        //只能保持一个线程进来,其他线程在外面等待。等这个线程结束以后,其他线程再进来
//          对run方法加锁了, 但是出现一家独大的情况。
        //也是 不符合咱们应用的场景的!!!
        //咱们 不能方法中加锁,在其他地方加锁
    }
}

换另外一种解决方法:

​ 同步代码块:就是拥有了synchronized 关键字修饰一个语句块。被修饰的语句块会被加锁。从而实现同步。

synchronized  (this) {

}
package com.qfedu.a_thread;

class MySycn implements Runnable {
    //卖10张票
    int ticket = 50;

    @Override
    public void run() {

        while (true) {
            
            synchronized (this) {
                if (ticket > 0) {
                    //线程1进到循环中了 在没有执行--之前,线程2 进到循环里面
                    //ticket  线程1 是50   ticket 线程2也是50

                    System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket-- + "票");
                }
            }
        }

    }
}

public class Demo4 {
    public static void main(String[] args) {
        MySycn mySycn = new MySycn();
        Thread thread = new Thread(mySycn, "线程1");
        thread.start();
        Thread thread1 = new Thread(mySycn, "线程2");
        thread1.start();
        Thread thread2 = new Thread(mySycn, "线程3");
        thread2.start();
        //打印出来的数据有重复的数据出现,符合咱们的场景吗?
        //不符合,生活场景的
        //咋解决?
        //对代码加锁:
        //只能保持一个线程进来,其他线程在外面等待。等这个线程结束以后,其他线程再进来
//          对run方法加锁了, 但是出现一家独大的情况。
        //也是 不符合咱们应用的场景的!!!
        //咱们 不能方法中加锁,在其他地方加锁
    }
}

针对于同步代码块举个例子:

​ 上厕所的时候,有坑位,这个坑wie就是资源。

​ 三个人去抢这个资源,如果不加锁的话,会出现问题的?是的。加上锁以后就会保证数据准确性。

案例:

加锁的目的为了保证数据的准确性。
卖电影票:
	三个线程:
		淘票票
		美团
		猫眼
		100张票
package com.qfedu.a_thread;

class SaleTicket implements Runnable {
    private static int ticket = 100;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (ticket > 0) {

                    System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");
                    ticket--;

                } else {
                    System.out.println("买完了");
                    break;
                }
            }
        }
    }
}

public class Demo5 {
    public static void main(String[] args) {
        SaleTicket saleTicket = new SaleTicket();
        new Thread(saleTicket, "淘票票").start();
        new Thread(saleTicket, "美团").start();
        new Thread(saleTicket, "猫眼").start();

    }
}

(ticket > 0) {

                System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");
                ticket--;

            } else {
                System.out.println("买完了");
                break;
            }
        }
    }
}

}

public class Demo5 {
public static void main(String[] args) {
SaleTicket saleTicket = new SaleTicket();
new Thread(saleTicket, “淘票票”).start();
new Thread(saleTicket, “美团”).start();
new Thread(saleTicket, “猫眼”).start();

}

}


### 今天的内容

> 1.守护线程
>
> 2.死锁
>
> 3.线程的生命周期
>
> 4.关于Object类下面的方法和线程有关
>
> 5.生产者消费者模式【难点】

### 1.守护线程

> 守护线程是用来守护非守护线程的。
>
> 非守护线程:就是平常写的线程
>
> 非守护线程一旦结束,守护线程就会自动消亡
>
> 守护线程依附非守护线程,如果非守护线程消亡,那么守护线程随之消亡。

```Java
package com.qfedu.a_protect;

class MyThread1 implements Runnable {
    @Override
    public void run() {
        System.out.println("软件更新中......");
        for (int i = 1; i <= 1000; i++) {
            System.out.println("downloading:" + i + "%");
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyThread1());
        //在线程的启动之前调用设置守护线程的
        thread.setDaemon(true);
        thread.start();
        for (int i = 1; i <= 300; i++) {
            System.out.println("主线程正在执行.....");
        }

    }
}

2.死锁

开发中禁止使用死锁。

面试会问:

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

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

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

线程1 有锁1

线程2 锁2

线程1会等待锁2 的释放

线程2会等待锁1的释放

package com.qfedu.b_delock;

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) {//锁的是obj1这个对象
                System.out.println(Thread.currentThread().getName() + "拿到了锁1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("等待锁2的释放....");
                //对obj2对象加锁的时候,结果人家线程2没有释放对obj2加的锁
                synchronized (obj2) {
                    System.out.println(Thread.currentThread().getName() + "->拿到锁2");
                }
            }
        }
        if (!flag) {//如果flag是个false的话,让线程2进来

            synchronized (obj2) {//锁的是obj2这个对象
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "拿到了锁2");

                System.out.println("等待锁1的释放....");
                synchronized (obj1) {

                    System.out.println(Thread.currentThread().getName() + "->拿到锁1");
                }
            }
        }

    }
}
public class Demo1 {
    public static void main(String[] args) {
        Object obj1 = new Object();
        Object obj2 = new Object();
        //第一个线程  flag 是true
        DeadLock deadLock = new DeadLock(true, obj1, obj2);
        new Thread(deadLock, "线程1").start();
        //第二个线程 flag  是flase
        DeadLock deadLock1 = new DeadLock(false, obj1, obj2);
        new Thread(deadLock1, "线程2").start();
    }
}

3.生命周期

1.线程的创建,开启线程

2.可运行状态: CPU等待 CPU抢占

3.运行状态: CPU等待 CPU抢占 CPU使用

4.阻塞状态: 锁 sleep

5.消亡状态

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

Object类下面的方法:

public final void wait()
             throws InterruptedException

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

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

package com.qfedu.c_object;

//为啥要写这个类? wait方法  需要对象来调用的额,先有类
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 + '\'' +
                '}';
    }
}

//现在开始写线程,  这个线程里面有一个 对象.wait()   线程会阻塞 等待
class WaiterThread implements  Runnable {
    private Message msg;

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

    @Override
    public void run() {

        //获取当前线程的名字
        String name = Thread.currentThread().getName();
        synchronized (msg) {
            System.out.println(name + "等待唤醒时间:" + System.currentTimeMillis());
            try {
                //当对象调用wait方法的时候,锁会释放,进入到对象的等待池中。
                msg.wait();//让线程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("123456");
            System.out.println(name + "被唤醒的时间:" + System.currentTimeMillis());
            System.out.println(name + "线程" + msg.getMessage());
        }


    }
}
class NotifierThread implements Runnable {
    private Message msg;

    public NotifierThread(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 + "开始唤醒等待线程");
//        Thread.sleep(1000);
        synchronized (msg) {
            msg.setMessage("我是修改之后的messgae属性的值");
            //msg.notify();
            msg.notifyAll();
        }

    }
}
public class Demo1 {
    public static void main(String[] args) {
        Message message = new Message("我是messag属性");
        WaiterThread waiterThread = new WaiterThread(message);
        new Thread(waiterThread, "wait").start();
        new Thread(waiterThread, "wait1").start();
        new Thread(waiterThread, "wait2").start();
        NotifierThread notifierThread = new NotifierThread(message);
        new Thread(notifierThread, "notify").start();
    }
}

总结:

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

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

生活中例子:

​ 卖家: 汽车厂商

​ 买家: 咱们75名学生

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

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

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

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

package com.qfedu.c_object;

//为啥要写这个类?这个类是作为两个线程之间通信的桥梁
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 Customer implements Runnable {
    private Goods goods;

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

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        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 Productor implements Runnable {
    private Goods goods;

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

    @Override
    public void run() {
        int count = 0 ;
        while (true) {//一直生产商品
            synchronized (goods) {
                if (goods.isProduct()) {//true  需要生产者
                    //造车,奇数造一种车, 偶数的话造另外一种车
                    if (count % 2 == 0) {//偶数
                        goods.setName("玛莎拉蒂");
                        goods.setPrice(200);
                    } else {
                        goods.setName("五菱宏光");
                        goods.setPrice(400);
                    }
                    //生产完以后一定要记得 将标记改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 Demo2 {
    public static void main(String[] args) {
        Goods goods = new Goods("东风", 89, false);
        //false不需要生产的  谁阻塞 生产者阻塞
        //最好是让生产者线程先执行
        Productor productor = new Productor(goods);
        new Thread(productor).start();
        Customer customer = new Customer(goods);
        new Thread(customer).start();
        /**
         * 消费者购买了:东风,价格为:89.0
         * 生产者生产了:玛莎拉蒂,价格为:200.0
         * 消费者购买了:玛莎拉蒂,价格为:200.0
         * 生产者生产了:五菱宏光,价格为:400.0
         * 消费者购买了:五菱宏光,价格为:400.0
         * 生产者生产了:玛莎拉蒂,价格为:200.0
         * isProduct = false
         * 走了else  代码是生产者阻塞了 消费在睡了一秒之后立马执行
         * 看消费者   有商品 买走了,之后   isProduct=true
         * 唤醒生产者了,
         * 生产者生产了:玛莎拉蒂,价格为:200.0  isProduct=false
         *
         */

    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/342067.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

重拾计网-第四弹 计算机网络性能指标

ps&#xff1a;本文章的图片内容来源都是来自于湖科大教书匠的视频&#xff0c;声明&#xff1a;仅供自己复习&#xff0c;里面加上了自己的理解 这里附上视频链接地址&#xff1a;1.5 计算机网络的性能指标&#xff08;1&#xff09;_哔哩哔哩_bilibili ​​​ 目录 &#x…

Mac使用adb调试安卓手机

0x00 背景 最近windows电脑休息&#xff0c;用mac办公比较多&#xff0c;手机用时间长了&#xff0c;不太灵光&#xff0c;准备修理一番。于是要用mac调试下android手机。配置略显麻烦&#xff0c;网上的步骤多参差不齐。估计是入门步骤&#xff0c;大佬们也懒得写的太细。于是…

适用于电动汽车充电站箱变的电气安全物联监测系统设计方案——安科瑞赵嘉敏

摘 要&#xff1a; 基于物联网技术架构提出了一种适用于电动汽车充电站箱变的电气安全物联监测系统设计方案。该系统由 电气安全智能感知设备、通信网关、电气安全物联网监测平台等构成&#xff0c;可支持充电站箱变充电桩出线回路电流、电缆 温度、剩余电流、故障电弧、短路…

java(渣哇)------输入与输出语句(详解) (๑•̌.•๑)

目录 一.java的输出语句&#xff1a; System.out.println() -----输出并换行 System.out.print() -----输出但不换行 System.out.printf() -----类似C语言的printf()输出语句,按格式进行输出 二.java的输入语句&#xff1a; 2.1-----Scanner的基础用法&#xff1a; 2.2…

JadxGUI反编译工具

安卓应用获取App特征信息指导 包名、公钥和签名MD5获取方式有多种&#xff0c;本文以使用JadxGUI工具获取为例。 下载地址&#xff1a; https://download.csdn.net/download/guodashen007/88772567 下载JadxGUI工具&#xff1a;下载安装完成后&#xff0c;使用此工具打开apk包…

聪明购物秘籍:如何规避Shopee账号关联封号风险

在Shopee平台上&#xff0c;规定一个买家只能拥有一个买家号&#xff0c;同时在一台电脑或者一个手机上登录多个买家号可能导致关联封号的风险。为了规避这一风险&#xff0c;许多购物达人正在寻找方法来防止账号关联。下面是一些使用大量Shopee买家号的技巧&#xff0c;帮助您…

决策树的基本构建流程

决策树的基本构建流程 决策树的本质是挖掘有效的分类规则&#xff0c;然后以树的形式呈现。 这里有两个重点&#xff1a; 有效的分类规则&#xff1b;树的形式。 有效的分类规则&#xff1a;叶子节点纯度越高越好&#xff0c;就像我们分红豆和黄豆一样&#xff0c;我们当然…

增速下滑?毫米波雷达的「烦恼」

编者按&#xff1a;传统3D雷达不赚钱&#xff0c;4D雷达还处于市场导入初期&#xff0c;对于不同角色的供应商来说&#xff0c;当下最难的是如何熬过这段艰难时期。 2023年底&#xff0c;蔚来旗舰车型ET9正式亮相&#xff0c;首次导入4D毫米波成像雷达。这款产品由蔚来资本投资…

GLM-4多模态重磅更新!摸着OpenAI过河!

智谱CEO张鹏说&#xff1a;OpenAI摸着石头过河&#xff0c;我们摸着OpenAI过河。 摸来摸去摸了一年&#xff0c;以每3-4个月升级一次基座模型的速度&#xff0c;智谱摸着OpenAI过河的最新成绩到底怎么样&#xff1f;真如所说吗&#xff1f; 听到GLM-4发布的当天&#xff0c;我就…

Android14实战:调整A2DP音量曲线(五十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

Rust采集天气预报信息并实时更新数据

目录 一、引言 二、系统设计 三、系统实现 四、案例分析 五、结论 一、引言 随着互联网技术的发展&#xff0c;人们对于获取实时的天气预报信息的需求越来越强烈。Rust作为一种高效、安全的编程语言&#xff0c;可以用于开发各种应用&#xff0c;包括天气预报采集系统。本…

Unity中URP下的 额外灯 逐像素光 和 逐顶点光

文章目录 前言一、额外灯 的 逐像素灯 和 逐顶点灯1、存在额外灯的逐像素灯2、存在额外灯的逐顶点灯 二、测试这两个宏的作用1、额外灯的逐像素灯2、额外灯的逐顶点灯 前言 在之前的文章中&#xff0c;我们了解了 主光相关的反射计算。 Unity中URP下的SimpleLit的 Lambert漫反…

图解CART分类树评估器的参数

图解CART分类树评估器的参数

微信小程序从入门到进阶(二)

数据请求 wx.request发起网络请求&#xff0c;请求的方式主要分为两种&#xff1a; get 请求 post 请求 // get请求 // html <view><button type"primary" bindtap"onGetClick">发起 get 请求</button> </view> // js // inde…

解决报错:javax.net.ssl.SSLHandshakeException: No appropriate protocol

目录 一、场景二、报错信息三、原因四、排查五、解决 一、场景 使用对象存储进行文件上传时报错 注&#xff1a;该问题只要需要用到http的都有可能出现&#xff0c;不是只针对对象存储 二、报错信息 com.hitachivantara.hcp.common.ex.InvalidResponseException: com.hitach…

47. 全排列 II - 力扣(LeetCode)

题目描述 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 输入示例 nums [1,1,2]输出示例 [[1,1,2], [1,2,1], [2,1,1]]解题思路 解题代码 class Solution {List<List<Integer>> result new ArrayList<>();Deq…

外网ssh远程连接服务器

文章目录 外网ssh远程连接服务器一、前言二、配置流程1. 在服务器上安装[cpolar](https://www.cpolar.com/)客户端2. 查看版本号&#xff0c;有正常显示版本号即为安装成功3. token认证4. 简单穿透测试5. 向系统添加服务6. 启动cpolar服务7. 查看服务状态8. 登录后台&#xff0…

【STM32F103】JDY-31蓝牙模块(USART)

JDY-31 JDY-31蓝牙模块&#xff0c;就是下面这么个小玩意。某宝买大概就七八块&#xff0c;超过十块的不要买。 JDY一共有6根引脚&#xff0c;而我们想要让它工作的话只需要接两根线即可&#xff0c;那就是VCC和GND&#xff0c;给VCC接3.6V~6V的电压&#xff08;推荐是5V&…

Modbus网关BL101 既实现Modbus转MQTT,还能当串口服务器使用

随着工业4.0的迅猛发展&#xff0c;人们深刻认识到在工业生产和生活中&#xff0c;实时、可靠、安全的数据传输至关重要。在此背景下&#xff0c;高性能的工业电力数据传输解决方案——协议转换网关应运而生&#xff0c;广泛应用于工业自动化系统、远程监控和物联网应用应用环境…

【江科大】STM32:定时器中断

文章目录 TIM&#xff08;Timer&#xff09;定时器根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型基本定时器通用定数器 高级定时器 时钟&#xff08;时钟电路&#xff09;的作用是什么&#xff1a;设置定时器触发中断普通方法&#xff1a;预分频器时序…
最新文章