JAVA | 线程(四)线程通信

一、传统的线程通信

  • 要借助于Object类提供的wait()、notift()、notifyAll(),这三个方法都要由同步监视器来调用,分下面两种情况:

    • synchronized 修饰的同步方法,同步监视器就是该类的默认实例(this),所以可以直接在同步代码块中调用这三个方法
    • synchronized 修饰的同步代码块,同步监视器是synchronized 括号中的对象,所以要用对象调用这三个方法。
  • wait():导致线程等待,直到其他线程调用该**同步监视器的notify()/motifyAll()方法

  • notify:唤醒此同步监视器上的单个线程,如果所有线程都在此同步监视器下等待,则随机唤醒一个,只有当前线程放弃对该同步监视器的锁定后(调用wait()),才可以执行被唤醒的线程

  • notifyAll():唤醒此同步监听器上的所有等待的线程,只有当前线程放弃对该同步监视器的锁定后(调用wait()),才可以执行被唤醒的线程

public class Account {
    // 银行账户
    private String accountNo;
    //余额
    private int balance;
    // 是否有钱
    private boolean flag = false;
    
    public Account(String accountNo, int balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }
    
    /**
     * 取钱
     */
   public synchronized void draw(int monny) throws InterruptedException {
       if (!flag){
           // 如果没有钱可取,就等待
           wait();
       }else {
           if (balance>=monny){
               Log.e("testthread",Thread.currentThread().getName()+"取钱成功:"+monny);

               balance-=monny;
               Log.e("testthread","余额:"+balance);
               flag = false;

               // 唤醒等待的线程
               notifyAll();
           }else {
               Log.e("testthread",Thread.currentThread().getName()+"取钱失败,余额不足:"+balance);
           }
       }
   }

    /**
     * 存钱
     */
   public synchronized void deposit(int monny) throws InterruptedException {
       if (flag){
           //有钱就等着
           wait();
       }else {
           Log.e("testthread",Thread.currentThread().getName()+"存钱成功:"+monny);
           balance +=monny;
           Log.e("testthread","余额:"+balance);
           flag = true;
           // 唤醒等待的线程
           notifyAll();
       }
   }
}
// 取钱的线程
public class DrawThread extends Thread {
    // 取钱的账户
    private Account account;

    // 取钱的金额
    private int monny;

    DrawThread(String name,Account account,int monny){
        super(name);
        this.account = account;
        this.monny = monny;
    }

    @Override
    public void run() {
        super.run();
        for (int i=0;i<100;i++){
           account.draw(monny);
        }
    }
}
//存钱的线程
public class DepositThread extends Thread {
    private Account account;
    private int monny;

    DepositThread(String name,Account account,int monny){
        super(name);
        this.account = account;
        this.monny = monny;
    }

    @Override
    public void run() {
        super.run();
        for (int i=0;i<100;i++){
             account.deposit(monny);
        }
    }
}

mBntFun5.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Account account = new Account("admin",0);
               new DrawThread("取钱A",account,800).start();
               new DepositThread("存钱A",account,800).start();
               new DepositThread("存钱B",account,800).start();
               new DepositThread("存钱C",account,800).start();
            }
        });

在这里插入图片描述

二、使用Condition控制线程通信

  • 当使用Lock对象来保证同步的时候,可以使用Condition来保持协调,让那些持有Lock却无法继续执行的线程,释放Lock对象;也可以唤醒其他处于等待的线程。
  • 此时,Lock代替了同步方法或同步代码块的作用,Condition代替了同步监视器的作用
  • Condition被绑在一个Lock对象上
  • 要获得特点Lock对象的Condition,就要调用Lock.newCondition()
  • Condition提供了三个方法:
    • await():导致线程等到,知道Condition的signal、signalAll来唤醒
    • signal:唤醒此Lock对象上等待的单个线程,如果所有线程都在等待,则随机唤醒一个,只有当前线程放弃对Lock对象的锁定(调用await())后,才可以执行被唤醒的线程
    • signalAll:唤醒此Lock对象上等待的所有线程,只有当前线程放弃对Lock对象的锁定(调用await())后,才可以执行被唤醒的线程
public class ConditionAccount {
    private String account;
    private int balance;
    // 是否有钱
    private boolean flag;

    // 显示定义一个LOCK对象
    private ReentrantLock lock = new ReentrantLock();

    // 定义特定Lock对象的Condition对象
    Condition condition = lock.newCondition();
    
    /**
     * 取钱
     * @param monny
     */
    public void draw(int monny) throws InterruptedException {
        lock.lock();
        try {
            if (!flag){
                //没有钱,就等着
                condition.await();
            }else {
                if (balance >= monny){
                    Log.e("testthread",Thread.currentThread().getName()+"取钱成功:"+monny);

                    balance -=monny;

                    Log.e("testthread","余额:"+balance);
                    flag = false;

                    // 唤醒其他线程
                    condition.signalAll();

                }
            }
        }finally {
            lock.unlock();
        }
    }

    /**
     * 存钱
     * @param monny
     */
    public void deposit(int monny) throws InterruptedException {
        lock.lock();
        try {
            if (flag){
                //有钱,就等着
                condition.await();
            }else {
                Log.e("testthread",Thread.currentThread().getName()+"存钱成功:"+monny);

                balance +=monny;

                Log.e("testthread","余额:"+balance);
                flag = true;

                // 唤醒其他线程
                condition.signalAll();
            }
        }finally {
            lock.unlock();
        }
    }
}

// 存钱的线程
public class DepositThread extends Thread {
    private ConditionAccount account;
    private int monny;

    DepositThread(String name,ConditionAccount account,int monny){
        super(name);
        this.account = account;
        this.monny = monny;
    }

    @Override
    public void run() {
        super.run();
        for (int i=0;i<100;i++){
            try {
                account.deposit(monny);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
// 取钱的类
public class DrawThread extends Thread {
    // 取钱的账户
    private ConditionAccount account;

    // 取钱的金额
    private int monny;

    DrawThread(String name,ConditionAccount account,int monny){
        super(name);
        this.account = account;
        this.monny = monny;
    }

    @Override
    public void run() {
        super.run();
        for (int i=0;i<100;i++){
            try {
                account.draw(monny);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
mBntFun5.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ConditionAccount account = new ConditionAccount("admin",0);
               new DrawThread("取钱A",account,800).start();
               new DepositThread("存钱A",account,800).start();
               new DepositThread("存钱B",account,800).start();
               new DepositThread("存钱C",account,800).start();
            }
        });

在这里插入图片描述

三、使用阻塞队列(BlockingQueue)控制线程通信

java BlockingQueque的多种实现

  • BlockingQueue这个接口是Queue的子接口,但是它并不是作为容器,而是用于线程同步的工具
  • 特征:
  1. 当生产者线程试图向BlockingQueue中插入元素,但是该队列已经满了,则会阻塞
  2. 当消费者线程试图向BlockingQueue中取出元素,但是该队列已经空了,则会阻塞

方法

抛出异常不同返回值阻塞线程指定超时时长
队尾插入元素add(e)offer(e)put(e)offer(e,time,unit)
队头删除元素remove()poll()take()poll(time,unit)
获取,不删除元素element()pick()

实现类

  • DelayQueue< E extends Delayed>:
    —>基于 PriorityBlockingQueue 实现的,要求集合元素都实现Delay接口

  • LinkedBlockingQueue< E>:
    –>基于链表实现的BlockingQueue队列

  • ArrayBlockingQueue< E>

  • Synchronous< E>
    –>同步队列,对该队列的存取操作必须交替进行

  • PriorityBlockingQueue< E>
    –>并不是标准的阻塞队列,用remove()、take()、poll()取出元素时,并不是取出队列中存在时间最长的元素,而是队列中最小的元素

mBntFun5.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String> (2);
                // 在没满时,这里存入元素,用add()、offer()、Put()都可以
                queue.add("android");
                queue.add("android");

                // 会报错:java.lang.IllegalStateException: Queue full
                //queue.add("android");

                //会返回false
                //queue.offer("android");

                // 会阻塞线程:程序无响应
                try {
                    queue.put("android");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Log.e("testqueue","阻塞:"+e.getMessage());
                }
            }
        });
// 生产者
    public class Produce extends Thread{
        private BlockingDeque<String> queue;

        Produce(BlockingDeque<String> queue){
            this.queue = queue;
        }

        String[] array = new String[]{
                "AA","BB","CC"
        };

        @Override
        public void run() {
            super.run();
            for (int i=0;i<1000;i++){
                Log.e("testthread",getName()+"准备生产");
                try {
                    Thread.sleep(200);
                    // 存入元素
                    queue.put(array[i%3]);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("testthread",getName()+" 生产完成"+queue);
            }
        }
    }

    // 消费者
    public class Consume extends Thread{
        private BlockingDeque<String> queue;
        Consume(BlockingDeque<String> queue){
            this.queue = queue;
        }

        @Override
        public void run() {
            super.run();
            for (int i=0;i<1000;i++){
                Log.e("testthread",getName()+" 准备消费");
                try {
                    Thread.sleep(200);
                    // 存入元素
                    queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("testthread",getName()+" 消费完成"+queue);
            }
        }
    }
mBntFun5.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                BlockingDeque<String> queue = new LinkedBlockingDeque<>(1);
                //启动三个生产现场
                new Produce(queue).start();
                new Produce(queue).start();
                new Produce(queue).start();
                //启动一个消费线程
                new Consume(queue).start();
            }
        });

在这里插入图片描述### 四、线程组和未处理的异常

  • ThreadGroup:线程组,可以对一批线程进行分类管理
  • 用户创建的所有线程都属于线程组,如果没有显示指定线程组,则属于默认的线程组,默认情况下和父线程在同一个线程组
  • 一旦线程加入某线程组,知道她死亡,都会一直属于这个线程组,运行过程组不能更改线程组

设置线程属于哪一个线程组

  • public Thread(ThreadGroup group, Runnable target)
  • public Thread(ThreadGroup group, String name)
  • public Thread(ThreadGroup group, Runnable target, String name)

ThreadGroup的构造方法:

  • public ThreadGroup(String name)
  • public ThreadGroup(ThreadGroup parent, String name)

ThreadGroup提供的方法:

  • String getName() 获取线程组的名字
  • boolean isDaemon():是否是后台线程
  • void setDaemon(boolean daemon):设置为后台线程
  • void interrupt():中断此线程组中的所有线程
  • int activeCount():返回活动线程的数目
  • void setMaxPriority(int pri):设置最高优先级
mBntFun5.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               ThreadGroup maingroup = Thread.currentThread().getThreadGroup();
               //主线程组:main
                Log.e("testqueue","主线程组:"+maingroup.getName());

                //创建一个新的线程组
                ThreadGroup newgroup = new ThreadGroup("新线程组");
                newgroup.setDaemon(true);
                Log.e("testqueue",maingroup.getName()+":"+newgroup.isDaemon());
            }
        });
weixin073智慧旅游平台开发微信小程序+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
python017基于Python贫困生资助管理系统带vue前后端分离毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值