5.3 线程间的通信

public class ThreadCommunation {
    public static void main(String[] args) {
        Q q = new Q();
        new Thread(new Producer(q)).start();
        new Thread(new Consumer(q)).start();
    }
}
class Q{
    String name="陈琼";
    String sex="女";
}
class Producer implements Runnable{
    Q q=null;
    public Producer(Q q){
        this.q=q;
    }
    public void run() {
     int i=0;
     while (true){
         if (i==0){
             q.name="张孝祥";
             try {
                 Thread.sleep(10);
             }catch (Exception e){
                 System.out.println(e.getMessage());
             }
             q.sex="男";
         }else {
             q.name="陈琼";
             q.sex="女";
         }
         i=(i+1)%2;
     }
    }
}
class Consumer implements Runnable{
    Q q=null;
    public Consumer(Q q){
        this.q=q;
    }
    public void run() {
        while (true){
            System.out.println(q.name+"---->"+q.sex);
        }
    }
}
public class ThreadCommunation1 {
    public static void main(String[] args) {
        QQ q = new QQ();
        new Thread(new Producer1(q)).start();
        new Thread(new Consumer1(q)).start();
    }
}
class QQ{
    String name="陈琼";
    String sex="女";
}
class Producer1 implements Runnable{
    QQ q=null;
    public Producer1(QQ q){
        this.q=q;
    }
    public void run() {
        int i=0;
        while (true){
            synchronized (q) {
                if (i == 0) {
                    q.name = "张孝祥";
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    q.sex = "男";
                } else {
                    q.name = "陈琼";
                    q.sex = "女";
                }
            }
            i=(i+1)%2;
        }
    }
}
class Consumer1 implements Runnable{
    QQ q=null;
    public Consumer1(QQ q){
        this.q=q;
    }
    public void run() {
        while (true){
            synchronized (q){
                System.out.println(q.name+"---->"+q.sex);
            }
        }
    }
}
public class ThreadCommunation1 {
    public static void main(String[] args) {
        QQ q = new QQ();
        new Thread(new Producer1(q)).start();
        new Thread(new Consumer1(q)).start();
    }
}
class QQ{
     private String name="陈琼";
     private String sex="女";
     public synchronized void put(String name,String sex){
         this.name=name;
         try {
             Thread.sleep(10);
         }catch (Exception e){
             System.out.println(e.getMessage());
         }
         this.sex=sex;
    }
    public synchronized void get(){
        System.out.println(name+"--->"+sex);
    }
}
class Producer1 implements Runnable{
    QQ q=null;
    public Producer1(QQ q){
        this.q=q;
    }
    public void run() {
        int i=0;
        while (true){
            if (i==0)
                q.put("张孝祥","男");
            else
                q.put("陈琼","女");
            i=(i+1)%2;
        }
    }
}
class Consumer1 implements Runnable{
    QQ q=null;
    public Consumer1(QQ q){
        this.q=q;
    }
    public void run() {
        while (true){
            q.get();
        }
    }
}

我们通过这样的一个应用来讲解线程间的通信。有一个数据存储空间,划分为两部分部分用于存储人的姓名,另一部分用于存储人的性别。我们的应用包含两个线程,一个线程向数据存储空间添加数据(生产者),另一个线程从数据存储空间中取出数据(消费者)

第一个意外,假设生产者线程刚向数据存储空间中添加了一个人的姓名,还没有加入这个人的性别,CPU就切换到了消费者线程,消费者线程将把这个人的姓名和上一个人的性别联系到了一起

Consumer 线程对 Producer 线程放入的一次数据连续读取了多次,并不符合我们的期望。我们要求的结果是,Producer 放一次数据,Consumer 就取一次,反之,Producer 也必须等到 Consumer 取完后才能放入新的数据,这就是我们要讲到的线程间的通信问题,Java 是通过Object类的 wait、notify、notifyAll这几个方法来实现线程间的通信的,由于所有的类都是从 Obiect 继承的,因此在任何类中都可以直接使用这些方法。

wait: 告诉当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用notify 为止
notify:唤醒同一对象监视器中调用 wait 的第一个线程。用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况。

notifyAll:唤醒同一对象监视器中调用 wait 的所有线程,具有最高优先级的线程首先被唤醒并执行。用于类似某个不定期的培训班终于招生满额后,通知所有学员都来上课的情况。

wait、notify、notifyAl1 这三个方法只能在 synchronized 方法中调用,即无论线程调用个对象的 wait 还是 notify 方法,该线程必须先得到该对象的锁旗标,这样,notify 只能唤醒同一对象监视器中调用 wait 的线程,使用多个对象监视器,我们就可以分组有多个wait、notify 的情况,同组里的 wait 只能被同组的notify 唤醒。

5.4 线程的生命周期

一个线程的产生是从我们调用了 start 方法开始进入 Runnable 状态即可以被调度运行状态,并没有真正开始运行,调度器可以将 CPU分配给它,真正运行其中的程序代码

(1)没有遇到任何阻隔,运行完成直接结束,也就是run0方法执行完毕。
(2)调度器将CPU分配给其他线程,这个线程又变为 Runnable 状态。
(3)请求锁旗标,却得不到,这时候它要等待对象的锁旗标,得到锁旗标后又会进入Runnable状态开始运行。
(4)遇到wait方法,它会被放入等待池中继续等待,直到有notify0或interrupt0方法执行,它才会被唤醒或打断开始等待对象锁旗标,等到锁旗标后进入 Runable 状态继续执行

了解了线程的生命周期,就不难理解如何控制线程生命的办法了吧?其实,控制线程生命周期的方法有很多种,如:suspend 方法、resume 方法和 stop 方法。但我们不推荐使用这三个方法,其中,不推荐使用 suspend 和resume是因为:
(1)会导致死锁的发生
(2)它允许一个线程(甲) 通过直接控制另外一个线程(乙)的代码来直接控制那个线程(乙)
虽然 stop 能够避免死锁的发生,但是带来了另外的不足,如果一个线程正在操作共享数据段,操作过程没有完成就 stop 了的话,将会导致数据的不完整性。因此 stop 方法也被不提倡使用了

public class ThreadLife {
    public static void main(String[] args) {
        ThreadTest t = new ThreadTest();
        new Thread(t).start();
        for (int i = 0; i < 100; i++) {
            if (i == 50)
                t.stopMe();
            System.out.println("mainThread is running");
        }
    }
}
class ThreadTest implements Runnable{
    private boolean bFlag=true;
    public void stopMe(){
        bFlag=false;
    }
    public void run() {
        while (bFlag){
            System.out.println(Thread.currentThread().getName()+"is running");
        }
    }
}

上面的程序中定义了一个计数器i,用来控制 main 线程的循环打印次数,在i的值从0到 50 的这段时间内,两个线程是交替运行的,但当计数器的取值变为 50 的时候,程序调用了ThreadTest 类的 stopMe 方法,而在 stopMe 方法中,将 bFlag 变量赋值为 false,也就是终止了 while 循环,run 方法结束,Thread-1线程随之结束。main 线程在计数器i等于50的时候,调用了ThreadTest 类的 stopMe 方法后,CPU 不一定会马上切换到 Thread-1线程上,也就是说Thread-1 线程不一定会马上终止,main 线程的计数器i可能到达五十几甚至六十几后,Thread-1 线程才真正结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JMeter是一个开源的负载测试工具,它可以模拟多种协议的行为,如HTTP、HTTPS、FTP、SMTP、POP3、IMAP等,从而测试服务端在不同负载下的性能表现。JMeter 5.3是其最新版本,它带来了一些新特性和改进。 JMeter 5.3的教程主要包括以下内容: 1. 安装和配置:教程首先会介绍如何下载、安装和配置JMeter。它可以在Windows、Linux和Mac OS等操作系统上运行。 2. 创建测试计划:JMeter的核心概念是测试计划,教程会详细介绍如何创建测试计划,并添加线程组、取样器、断言等组件。 3. 建立脚本:在JMeter中,我们可以使用GUI方式建立脚本,也可以通过编写脚本文件来建立脚本。教程会介绍这两种方式,并演示如何建立不同类型的脚本,如Web测试、API测试等。 4. 执行测试:教程会详细介绍如何配置测试的参数,如并发用户数、循环次数等,并演示如何启动测试并收集结果。 5. 结果分析:JMeter提供了强大的结果分析工具,教程会介绍如何使用这些工具来分析测试结果,如图表、报告等。 此外,JMeter 5.3还带来了一些改进。例如,它增加了对TLS1.3的支持,提供了更好的HTTP/2性能,并改进了GUI的响应速度等。教程中也会介绍这些改进,让用户了解JMeter的最新功能和优化。 总结起来,JMeter 5.3教程是一个全面的指南,帮助用户轻松上手使用JMeter进行性能测试。无论是初学者还是有经验的用户,都可以通过教程学习如何有效地使用JMeter,并获得准确的测试结果。 ### 回答2: JMeter是一个流行的开源负载测试工具,用于评估一个应用程序的功能和性能。JMeter版本5.3是JMeter的最新版本,具有许多强大的功能和改进。 首先,JMeter 5.3引入了对Java 11的全面支持。这意味着可以在最新的Java环境下运行JMeter,并且可以利用Java 11的新特性和性能优化。 其次,JMeter 5.3中增加了更多的插件和功能。其中包括了多个新增的监听器,用于收集和分析测试结果,如InfluxdbWriter、Response Time Distribution、Response Time Percentiles等。此外,新的断言器和前置处理器被引入,用于更精确地检查响应数据和处理预处理步骤。 JMeter 5.3还增强了对非HTTP协议的支持。现在,JMeter可以与WebSocket、JMS、MongoDB等非HTTP协议进行通信和测试。这为开发者提供了更多的测试选项和灵活性。 另外,JMeter 5.3的界面进行了改进和优化。现在,用户可以更方便地创建和管理测试计划。另外,还优化了测试报告的生成和导出,使分析和分享测试结果更加方便。 总体而言,JMeter 5.3是一个功能强大、灵活性高的负载测试工具。它提供了丰富的功能和插件,支持各种协议和测试类型。无论是初学者还是专业人士,都可以通过JMeter 5.3来评估应用程序的性能和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值