Java多线程:线程通信:生产者 和 消费者 || 顺序打印

news2024/11/23 18:44:31

Java多线程:线程通信:生产者 和 消费者 || 顺序打印

在这里插入图片描述


每博一文案

靠谱,是最高级的聪明:师父说:人生一回,道义一场。你对人对事的态度,藏着你一生的福报。
千金难买好人缘,人活的就是好口碑,别人怎么对你,是你的因果,你怎么对别人,是你的修行。
和谁在一起,决定了你是谁,而你是谁,就拥有怎样的人生。一个人为人处世最大的资本,不是才华也不是金钱,而是
看他靠不靠谱,良心,是做事的底线,人品,是做人的底牌,这世上从不缺优秀的人,难得的是靠谱的人,聪明的人
只适合聊聊天,但靠谱的人值得常伴心间,聪明只能让你走得快,但靠谱却能决定你走得多远。
所以,一个人最好的底牌就是“靠谱” 二字。
                                     ——————   一禅心灵庙语

文章目录

  • Java多线程:线程通信:生产者 和 消费者 || 顺序打印
    • 每博一文案
    • 1. 线程通信
      • 1.1 wait(), notify(), notifyAll()等方法介绍
      • 1.2 为什么notify(), wait()等函数定义在Object中,而不是Thread中
    • 2. 顺序打印
    • 3. yield() 与 wait()的比较
    • 4. sleep() 与 wait()的比较
    • 5. 生产者/消费者模式
    • 4. 总结:
    • 5. 最后:


1. 线程通信

1.1 wait(), notify(), notifyAll()等方法介绍

在Object.java中,定义了wait(), notify()和notifyAll()等方法。

  • wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁
  • notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;
  • notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。
  • 这 wait(), notify(), notifyAll()方法的调用者必须是同代码块或是同步方法中的同步监视器 “锁” 对象一致,不然报错:IllegalMonitorStateException 异常
  • 这 三个wait(),notify(), notifyAll()方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常
  • 如下是图示说明

在这里插入图片描述

Object类中关于等待/唤醒的API详细信息如下:

  • notify() – 唤醒在此对象监视器上等待的单个线程。
  • notifyAll() – 唤醒在此对象监视器上等待的所有线程。
  • wait() – 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
  • wait(long timeout) – 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
  • wait(long timeout, int nanos) – 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。

1.2 为什么notify(), wait()等函数定义在Object中,而不是Thread中

简单的解释的说明: 因为这三个方法必须有锁对象调用,而任意对象都可以作为 synchronized 的同步锁, 因此这三个方法只能在Object类中声明,因为所有的对象/任意对象就继承于 Object 类,才能对应上面的任意对象都可以作为 synchronized 的同步锁。

复杂逻辑解释:

Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。

wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!
OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。
现在,请思考一个问题: notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”

负责唤醒等待线程的那个线程(我们称为 “唤醒线程” ),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。

总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。

2. 顺序打印

使用两个线程打印 1-100。线程1, 线程2交替打印 : 线程1 打印偶数,线程2打印奇数

package blogs.blog4;

/**
 * 使用两个线程打印1-100。线程1, 线程2 交替打印  : 线程1 打印偶数,线程2打印奇数
 */
public class ThreadTest14 {
    public static void main(String[] args) {
        MyThread14 myThread14 = new MyThread14();

        Thread t1 = new Thread(myThread14);
        Thread t2 = new Thread(myThread14);

        t1.setName("线程一");
        t2.setName("线程二");

        t1.start();
        t2.start();

    }
}

/**
 * 打印偶数
 */
class MyThread14 implements Runnable {
    private int num = 1;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                this.notify();  // 唤醒被 wait()阻塞的线程,打印偶数
                if (num <= 100) {
                    System.out.println(Thread.currentThread().getName() + "--->" + num++);
                    // 奇数打印完了,该线程进入阻塞状态,让另外一个线程进入,打印偶数
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                } else {
                    break;
                }
            }
        }
    }
}

在这里插入图片描述


  • 注意对于这 三个wait(), notify(), notifyAll()方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常

在这里插入图片描述

  • 这 wait(), notify(), notifyAll()方法的调用者必须是同代码块或是同步方法中的同步监视器 “锁” 对象一致,不然报错:IllegalMonitorStateException 异常

在这里插入图片描述


3. yield() 与 wait()的比较

我们知道,wait() 的作用是让当前线程由 “运行状态” 进入到 “等待(阻塞)状态”的同时,也会释放同步锁 。而 yield() 的作用是让步,它也会让当前线程离开 “运行状态”。它们的区别是:

  • wait() 是让线程由 “运行状态” 进入到 “等待(阻塞)状态”,而不是 yield() 是让线程由 “运行状态” 进入到 “就绪状态”
  • wait() 是会释放 它所持有的对象的同步锁,而 yield() 方法是不会 释放锁 的。
  • 两个方法的声明的位置不同,wait()必须在synchronized同步代码块/同步方法当中,而且调用的对象必须和同步监视器“锁”的对象一致,不然报,yield()任意
  • wait 是 Object 类的成员本地方法,sleep 是Thread 类的静态方法。

下面通过示例演示 yield() 是不会释放锁的

package blogs.blog4;

/**
 * yield() 与 wait()的比较
 */
public class ThreadTest15 {
    public static void main(String[] args) {
        MyThread15 myThread15 = new MyThread15();

        Thread t1 = new Thread(myThread15);
        Thread t2 = new Thread(myThread15);

        t1.setName("线程一");
        t2.setName("线程二");

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


class MyThread15 implements Runnable {
    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
                if (i == 5) {
                    Thread.yield(); // 让当前线程进入 “就绪状态”,但不会释放线程中的锁.
                }
            }
        }
    }
}

在这里插入图片描述

解析:

主线程main中启动了两个线程t1和t2。t1和t2在run()会引用同一个对象的同步锁,即synchronized(this)。在t1运行过程中,虽然它会调用Thread.yield() 让当前线程进入 阻塞状态,但是却不会释放手中的锁 ,导致 t2线程无法拿到 “锁” ,就无法进入 synchronized(this) {} 代码块中执行,这里只有等到 t1线程执行完后,释放手中的锁,t2线程才可以进入到 synchronized(this) {} 代码块中执行。所以就出现了上述的 一个线程一直在执行直到运行完。另外一个线程才可以执行。

下面对比演示:wait()是会释放锁的

package blogs.blog4;

/**
 * yield() 与 wait()的比较
 */
public class ThreadTest15 {
    public static void main(String[] args) {
        MyThread15 myThread15 = new MyThread15();

        Thread t1 = new Thread(myThread15);
        Thread t2 = new Thread(myThread15);

        t1.setName("线程一");
        t2.setName("线程二");

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


class MyThread15 implements Runnable {
    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
                if (i == 5) {
                    try {
                        this.wait(); // 让当前线程进入 “等待/阻塞状态”,并释放线程中的锁.
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                this.notify();   // 唤醒被 wait()阻塞的线程
            }
        }
    }
}

在这里插入图片描述


4. sleep() 与 wait()的比较

我们知道,wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。
但是,wait()会释放对象的同步锁,而sleep()则
不会释放锁

  • 相同点:一旦执行,都可以使得当前线程进入阻塞状态。以及都需要异常处理。
  • 不同点:
    1. 两个方法的声明的位置不同,两个方法的声明的位置不同,wait()必须在synchronized同步代码块/同步方法当中,而且调用的对象必须和同步监视器“锁”的对象一致,不然报,sleep()任意
    2. Wait 是 Object 类的成员方法,sleep 是Thread 类的静态本地方法
    3. sleep()阻塞不会释放手中的同步监视器“锁”,wait()阻塞的同时会释放手中的同步监视器"锁"

下面通过示例演示sleep()是不会释放锁的

package blogs.blog4;

/**
 * yield() 与 wait()的比较
 */
public class ThreadTest15 {
    public static void main(String[] args) {
        MyThread15 myThread15 = new MyThread15();

        Thread t1 = new Thread(myThread15);
        Thread t2 = new Thread(myThread15);

        t1.setName("线程一");
        t2.setName("线程二");

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


class MyThread15 implements Runnable {
    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
                if (i == 5) {
                    try {
                        Thread.sleep(1000 * 1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

在这里插入图片描述

解析:

主线程main中启动了两个线程t1和t2。t1和t2在run()会引用同一个对象的同步锁,即synchronized(this)。在t1运行过程中,虽然它会调用Thread.sleep(1000 * 1) 让线程睡眠 1 s 进入阻塞状态,但是却不会释放手中的锁 ,导致 t2线程无法拿到 “锁” ,就无法进入 synchronized(this) {} 代码块中执行,这里只有等到 t1线程执行完后,释放手中的锁,t2线程才可以进入到 synchronized(this) {} 代码块中执行。所以就出现了上述的 一个线程一直在执行直到运行完。另外一个线程才可以执行。

下面对比演示:wait()是会释放锁的

package blogs.blog4;

/**
 * yield() 与 wait()的比较
 */
public class ThreadTest15 {
    public static void main(String[] args) {
        MyThread15 myThread15 = new MyThread15();

        Thread t1 = new Thread(myThread15);
        Thread t2 = new Thread(myThread15);

        t1.setName("线程一");
        t2.setName("线程二");

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


class MyThread15 implements Runnable {
    @Override
    public void run() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "-->" + i);
                if (i == 5) {
                    try {
                        this.wait(); // 让当前线程进入 “等待/阻塞状态”,并释放线程中的锁.
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                this.notify();   // 唤醒被 wait()阻塞的线程
            }
        }
    }
}

在这里插入图片描述

5. 生产者/消费者模式

生产/消费者问题是个非常典型的多线程问题,涉及到的对象包括“生产者”、“消费者”、“仓库”和“产品”。他们之间的关系如下:

  1. 生产者仅仅在仓储未满时候生产,仓满则停止生产。
  2. 消费者仅仅在仓储有产品时候才能消费,仓空则等待。
  3. 当消费者发现仓储没产品可消费时候会通知生产者生产。
  4. 生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
  • **对于生产方法produce()而言:**当仓库满时,生产者线程等待,需要等待消费者消费产品之后,生产线程才能生产;生产者线程生产完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“消费者线程”,即我们所说的“通知消费者进行消费”。
  • **对于消费方法consume()而言:**当仓库为空时,消费者线程等待,需要等待生产者生产产品之后,消费者线程才能消费;消费者线程消费完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“生产者线程”,即我们所说的“通知生产者进行生产”。

这是一种特殊的业务需求,在这种特殊的情况下需要使用 wait()方法和notify()方法/notifyAll() 方法

如下图示说明:

在这里插入图片描述

举例:先定义仓库最多只能存放 1 个产品,生产 1 个 产品,就消费 1 个产品

package blogs.blog4;

/**
 * 生产者和消费者模型
 */
public class ProductTest {
    public static void main(String[] args) {
        // 创建仓库的实例对象
        Clark clark = new Clark();

        // 创建两个线程:生产者/消费者
        Thread t1 = new Thread(new Product(clark));   // 生产线程,生产和消费是同一个仓库
        Thread t2 = new Thread(new Consumer(clark));  // 消费线程,生产和消费是同一个仓库

        // 设置线程名
        t1.setName("生产者");
        t2.setName("消费者");

        // 创建线程
        t1.start();
        t2.start();
    }
}


/**
 * 仓库
 */
class Clark {
    private int productCount = 0;

    public Clark() {

    }

    /**
     * 生产产品
     */
    public synchronized void produceProduct() {  // 非静态方法 synchronized()默认同监视器"锁"是 this 对象锁,无法修改
        // 仓库最多存放 1 个产品,仓库没有满生产
        if (productCount < 1) {
            // 生产 ++
            this.productCount++;
            System.out.println(Thread.currentThread().getName() + "-->开始生产第" + this.productCount + "个产品");
            // 生产完了,打开消费线程,消费
            this.notifyAll();  // 释放被 wait()阻塞的线程,优先高的优先
        } else {
            // 仓库满了,停止生产,停止生产线程 wait() 进入阻塞状态,并释放手中的锁。
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 消费产品
     */
    public synchronized void consumeProduct() { // 非静态方法 synchronized()默认同监视器"锁"是 this 对象锁,无法修改
        // 仓库中有产品,才可以消费
        if (this.productCount > 0) {
            // 消费
            System.out.println(Thread.currentThread().getName() + "-->开始消费第" + this.productCount + "个产品");
            this.productCount--;
            // 消费完了,打开生产线程,生产
            this.notifyAll();
        } else {
            // 仓库没有产品,停止消费,该消费线程进入 阻塞状态 wait(),并释放手中的锁
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 生产者
 */
class Product implements Runnable {
    // 定义仓库
    private Clark clark = null;

    public Product() {
        super(); // 调用父类中的构造器
    }

    public Product(Clark clark) {
        super(); // 调用父类中的构造器,必须首行(一次)
        this.clark = clark;
    }

    /**
     * 生产产品
     */
    @Override
    public void run() {
        while (true) {
            this.clark.produceProduct();
        }
    }
}


/**
 * 消费者
 */
class Consumer implements Runnable {
    private Clark clark = null;

    public Consumer() {
        super(); // 调用父类的构造器,首行(一次)
    }

    public Consumer(Clark clark) {
        super();  // 调用父类的构造器,首行(一行)
        this.clark = clark;
    }

    /**
     * 消费
     */
    @Override
    public void run() {
        while (true) {
            this.clark.consumeProduct();
        }
    }
}

在这里插入图片描述


举例:定义仓库最多只能存放 20 个产品,生产 20 个 产品,就消费 20 个产品

只需要将: 生产的仓库 if 判断修改一下就好了。

在这里插入图片描述

package blogs.blog4;

/**
 * 生产者和消费者模型
 */
public class ProductTest {
    public static void main(String[] args) {
        // 创建仓库的实例对象
        Clark clark = new Clark();

        // 创建两个线程:生产者/消费者
        Thread t1 = new Thread(new Product(clark));   // 生产线程,生产和消费是同一个仓库
        Thread t2 = new Thread(new Consumer(clark));  // 消费线程,生产和消费是同一个仓库

        // 设置线程名
        t1.setName("生产者");
        t2.setName("消费者");

        // 创建线程
        t1.start();
        t2.start();
    }
}


/**
 * 仓库
 */
class Clark {
    private int productCount = 0;

    public Clark() {

    }

    /**
     * 生产产品
     */
    public synchronized void produceProduct() {  // 非静态方法 synchronized()默认同监视器"锁"是 this 对象锁,无法修改
        // 仓库最多存放 1 个产品,仓库没有满生产
        if (productCount < 10) {
            // 生产 ++
            this.productCount++;
            System.out.println(Thread.currentThread().getName() + "-->开始生产第" + this.productCount + "个产品");
            // 生产完了,打开消费线程,消费
            this.notifyAll();  // 释放被 wait()阻塞的线程,优先高的优先
        } else {
            // 仓库满了,停止生产,停止生产线程 wait() 进入阻塞状态,并释放手中的锁。
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 消费产品
     */
    public synchronized void consumeProduct() { // 非静态方法 synchronized()默认同监视器"锁"是 this 对象锁,无法修改
        // 仓库中有产品,才可以消费
        if (this.productCount > 0) {
            // 消费
            System.out.println(Thread.currentThread().getName() + "-->开始消费第" + this.productCount + "个产品");
            this.productCount--;
            // 消费完了,打开生产线程,生产
            this.notifyAll();
        } else {
            // 仓库没有产品,停止消费,该消费线程进入 阻塞状态 wait(),并释放手中的锁
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 生产者
 */
class Product implements Runnable {
    // 定义仓库
    private Clark clark = null;

    public Product() {
        super(); // 调用父类中的构造器
    }

    public Product(Clark clark) {
        super(); // 调用父类中的构造器,必须首行(一次)
        this.clark = clark;
    }

    /**
     * 生产产品
     */
    @Override
    public void run() {
        while (true) {
            this.clark.produceProduct();
        }
    }
}


/**
 * 消费者
 */
class Consumer implements Runnable {
    private Clark clark = null;

    public Consumer() {
        super(); // 调用父类的构造器,首行(一次)
    }

    public Consumer(Clark clark) {
        super();  // 调用父类的构造器,首行(一行)
        this.clark = clark;
    }

    /**
     * 消费
     */
    @Override
    public void run() {
        while (true) {
            this.clark.consumeProduct();
        }
    }
}

在这里插入图片描述


4. 总结:

  1. wait() ,notify() ,notifyAll() 方法是定义在 Object 类当中的。
  2. wait() 是让当前线程进入阻塞状态,并释放当前线程占用的同步锁,notify() 唤醒被 wait() 阻塞的线程,优先级高的优先唤醒,仅仅只会唤醒一个线程,notifyAll() 唤醒被 wait() 阻塞的所有线程。
  3. wait() ,notify() ,notifyAll() 方法必须在 synchronized 代码块/方法中使用不然报java.lang.IllegalMonitorStateException 异常。
  4. wait(), notify(), notifyAll()方法的调用者必须是同代码块或是同步方法中的同步监视器 “锁” 对象一致,不然报:IllegalMonitorStateException 异常。
  5. 注意 yield() 与 wait() 的区别: yield () 让线程进入 ”就绪状态“,但是不会释放线程中的”锁“,wait() 让线程进入 ”阻塞状态“,并释放线程中的”锁“。
  6. 注意 sleep() 与 wait() 的区别:sleep() 让线程进入”等待/阻塞状态“,但是不会释放线程中的”锁“,wait() 让线程进入 ”阻塞状态“,并释放线程中的”锁“。
  7. 编写 生产者 / 消费者模型:达到 生产 和 消费的一个平衡。

5. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见 !!!


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android编译Skia库

Android编译Skia库 本文档提供两种方法编译Skia库 使用aosp源码进行编译使用skia源码进行编译 两种编译方法都可以编译,并且都可以使用在多个平台中,且可以使用在不同Android版本中 使用aosp源码编译 第一步是拿到aosp的源码.因为写这篇教程,使用的是msm8996的源码,因此,我…

Armitage图形化前端

开源免费图形前端 ● 作者自称是众多不会使用metasploit的安全专家之一&#xff08;命令行&#xff09; ● MSF基于命令行&#xff0c;缺少直观的GUI图形用户接口 Armitage只是调用MSF的漏洞利用能力 ● Armitage的每一个GUI操作都可以对应MSF中的一条命令 红队团队合作模拟…

OSCP vulhub LORD OF THE ROOT: 1.0.1

vulhub LORD OF THE ROOT: 1.0.1靶场信息&安装Description一、信息收集第一种ssh爆破第二种猜二、漏洞探测三、漏洞利用四、提权第二种方法&#xff1a;udf提权第三种缓冲区溢出提权靶场信息&安装 地址&#xff1a;https://www.vulnhub.com/entry/lord-of-the-root-10…

APSIM练习:向管理组件添加额外规则

“标准”作物管理组件没有提供很多选项来决定何时播种作物——很可能应该考虑一系列额外的种植规则&#xff08;例如&#xff0c;土壤是否足够干燥以驱动拖拉机过去&#xff1f;&#xff09;&#xff0c;并且在播种时应采取额外措施&#xff08;例如添加一些肥料&#xff09;。…

Tofu目标识别跟踪模块介绍

1.简介Tofu3 是多波段视频物体识别跟踪模块&#xff0c;支持可见光视频与红外视频的输入&#xff0c;支持激光补光变焦自适应控制&#xff0c;支持视频下的多类型物体检测、识别、跟踪等功能。产品支持视频编码、设备管理、目标检测、深度学习识别、跟踪等功能&#xff0c;提供…

服务器防勒索病毒场景分析经验分享

服务器防勒索病毒 l 场景描述 互联网飞速发展&#xff0c;各企业均搭建了自己的服务器。包括文件服务器、代码服务器、业务系统服务器、数据库服务器等。目前大多数企业使用的服务器系统以Windows Server和Linux&#xff08;Centos&#xff09;居多&#xff0c;但随着国产操作…

python从入门到实践————图形界面(上)

系列文章目录 文章目录系列文章目录前言窗体组件窗体显示显示窗体文本显示项目实践总结前言 图形用户接口&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;是人机交互的重要技术手段&#xff0c;在Python中利用tkinter模块就可以方便地实现图形界面。 在你p…

每日一题:Leetcode203 移除链表元素

文章目录 系列&#xff1a;链表专练 语言&#xff1a;java & go 题目来源&#xff1a;Leetcode203 移除链表元素 难度&#xff1a;简单 考点&#xff1a;头结点 && 虚拟头结点 思路和参考答案文章目录题目描述思路java参考代码go参考代码&#xff1a;题目描述 给…

xcelium——重要特性——提高编译仿真性能

以下图片来自于CadenceTECHTALK研讨会 解决编译仿真的瓶颈问题&#xff0c;提高整体性能 影响performance的因素 xcelium版本access rwccoverage收集snapshot过大&#xff0c;加载时间过长&#xff0c;可通过Zlib加快加快snapshot加载 编译/仿真添加选项&#xff1a; -new…

如何检查自己电脑是否被攻击了?

数据来源 本文仅用于信息安全的学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 通过系统命令排查账户安全 query user # 查看当前登录账户 lo…

21.字符串的概念,字符串的可修改性

字符串和指针 字符串的概念&#xff1a; 字符串就是以 ‘\0’ 结尾的若干字符的集合。字符串的地址&#xff0c;是第一个字符的地址。可以定义一个字符指针变量保存字符串的地址。比如&#xff1a; char* shelloworld"; 字符串的存储形式&#xff1a;数组、文字常量区、…

靶机测试haclabs-no_name

靶机地址https://www.vulnhub.com/entry/haclabs-no_name,429/靶机测试nmap扫描└─$ nmap -T5 -A 192.168.1.100 -oA hl-portsStarting Nmap 7.92 ( https://nmap.org ) at 2023-01-12 11:16 CST Nmap scan report for 192.168.1.100 Host is up (0.00040s latency). Not show…

获工信部认可!腾讯牵头的《零信任系统技术规范》入选工信部百项团标应用示范

近日&#xff0c;工信部发布《关于公布2022年团体标准应用示范项目的通告》&#xff0c;由腾讯安全牵头起草&#xff0c;公安部第三研究所、国家计算机网络应急技术处理协调中心、中国移动设计院等业内16家零信任厂商、测评机构及用户联合编制的中国第一部《零信任系统技术规范…

Webpack搭建本地服务器

1、开启本地服务器 1.1、为什么要搭建本地服务器&#xff1f; 目前我们开发的代码&#xff0c;为了运行需要有两个操作&#xff1a; 操作一&#xff1a;npm run build&#xff0c;编译相关的代码&#xff1b;操作二&#xff1a;通过live server或者直接通过浏览器&#xff0…

【ROS】—— 机器人系统仿真 —URDF语法 (十三)

文章目录前言1. 概述1.1 概念1.2 作用1.3 相关组件1.3.1 URDF1.3.2 RViz1.3.3 gazebo1.4 准备2. URDF集成Rviz基本流程2.1 创建功能包&#xff0c;导入依赖2.2 编写 URDF 文件2.3 在 launch 文件中集成 URDF 与 Rviz3. URDF语法详解3.1 robot3.2 link3.3 joint3.4 URDF练习3.5 …

Lock锁原理解析【Android进阶】

Lock锁原理解析【Android进阶】 Lock简介 Lock接口位于J.U.C下locks包内&#xff0c;其定义了Lock应该具备的方法。 Lock 方法签名&#xff1a; void lock()&#xff1a;获取锁&#xff08;不死不休&#xff0c;拿不到就一直等&#xff09;boolean tryLock()&#xff1a;获…

13.0、Linux-安装Tomcat、以及防火墙常用命令

13.0、Linux-安装Tomcat、以及防火墙常用命令 防火墙常用命令在本文最下方-> 安装 Tomcat -> 第一步&#xff1a;进入官网下载 Tomcat8 -> Apache Tomcat - Apache Tomcat 8 Software Downloads 第二步&#xff1a;将下载好的文件 通过 Xshell 和 Xftp 工具上传到 L…

前端面试题——React重点

文章目录React相关问题1. 函数式组件与Class组件有什么不同&#xff1f;2. 说说React的fiber架构&#xff1f;3. 协调4. 虚拟DOM (Virtual DOM)React相关问题 1. 函数式组件与Class组件有什么不同&#xff1f; 答&#xff1a; ① 函数式组件不需要继承&#xff0c;直接 “fun…

YUV 文件读取、显示、缩放、裁剪等操作教程

系列文章目录 文章目录系列文章目录前言一、Chroma subsampling二、读取 YUV 文件2.1 准备工作2.2 Planar 模式2.2.1 YUV420P 格式读取2.2.2 YUV422P 格式读取2.2.3 NV21 格式读取2.2.4 NV12 格式读取2.2.5 小结2.3 Packed 模式2.3.1 YUYV 格式读取2.3.2 小结三、SDL 显示 YUV …

自动化测试是什么?为什么要做自动化测试?

自动化测试是什么&#xff1f;相信对软件测试有一定了解的朋友都不会感到陌生。自动化测试正如字面上理解得那样&#xff0c;是一种自动完成测试工作的测试方式。虽然是说的自动化&#xff0c;但是也是需要测试员手动编写代码去完成测试工作。那么&#xff0c;为什么要做自动化…