4-线程通信,线程状态

2022-10-27 13:23:02 浏览数 (1)

线程通信

多个线程因为在同一个进程中,所以互相通信比较容易

线程通信的经典模型:生产者与消费者问题

生产者负责生成商品,消费者负责消费商品,生产不能过剩(仍有数据未被消费时不能生产),消费不能没有(不能消费还没有生产的数据)

模拟案例:

两名消费者拥有一个共享账户,共享资源,三名生产者负责生产资源。

两名消费者去获取资源,资源存在就取出,不存在就等待,唤醒生产者继续生产资源。

生产者生产资源时,发现仍然存在资源就不继续生产,如果没有资源就生产,然后等待,唤醒消费者来消费

注意:

  • 线程通信一定是多个线程操作同一个资源才需要进行通信
  • 线程通信必须先保证线程安全,否则毫无意义,代码也会报错

线程通信的Object提供三种核心方法

  • wait()方法:让当前线程进入等待状态,此方法必须由锁对象调用
  • notify()方法:唤醒当前锁对象上等待状态的某个线程,此方法必须由锁对象调用
  • notifyAll()方法:唤醒当前锁对象上等待状态的全部线程,此方法必须由锁对象调用

代码实现

账户类,定义了存钱和取钱的操作
代码语言:javascript复制
package ThreadSafety;


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//定义账户类
public class Account {
    private int cardID;
    private double Money;


    public synchronized void DrawMoney(int m){
        String name =Thread.currentThread().getName();

        try {
            //判断余额是否足够
            if (Money >= m) {
                //开始支付
                System.out.println(name   "用户执行取钱操作,余额充足,支付"   m   "元成功!");
                Money -= m;


            } else {
                //余额不足
                System.out.println(name   "用户执行取钱操作,余额不足,支付失败!");
            }
            System.out.println(name   "用户结束操作,余额"   Money   "元");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //取完钱后,唤醒别人,等待自己
            this.notifyAll();
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    //新定义的存钱类
    public synchronized void SaveMoney(int m){
        String name =Thread.currentThread().getName();

        try {
            //账户没钱的情况下才执行存钱操作
            if (Money == 0) {
                System.out.println(name   "用户执行存钱操作,充入"   m   "元成功!");
                Money  = m;
            } else {
                System.out.println(name   "用户执行存钱操作,余额充足,充值失败!");
            }
            System.out.println(name   "用户结束操作,余额"   Money   "元");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //取完钱后,唤醒别人,等待自己
            this.notifyAll();
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public Account() {
    }

    public Account(int cardID, double money) {
        this.cardID = cardID;
        Money = money;
    }

    public int getCardID() {
        return cardID;
    }

    public void setCardID(int cardID) {
        this.cardID = cardID;
    }

    public double getMoney() {
        return Money;
    }

    public void setMoney(double money) {
        Money = money;
    }
}
线程类:分别规定了存钱线程和取钱线程
代码语言:javascript复制
package ThreadSafety;


//线程类:将存钱行为看作是一条单独的线程创建
public class SaveThread extends Thread{

    //定义一个成员变量,接收账户对象
    private Account acc;

    public SaveThread(Account acc,String name){
        super(name);
        this.acc=acc;
    }


    @Override
    public void run() {
        //执行存钱操作,每个用户不断尝试存1000元
        while (true) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            acc.SaveMoney(1000);
        }
    }
}
代码语言:javascript复制
package ThreadSafety;


//线程类:将取钱行为看作是一条单独的线程创建
public class DrawThread extends Thread{

    //定义一个成员变量,接收账户对象
    private Account acc;

    public DrawThread(Account acc,String name){
        super(name);
        this.acc=acc;
    }


    @Override
    public void run() {
        //执行取钱操作,每个用户不断尝试取1000元
        while (true) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            acc.DrawMoney(1000);
        }
    }
}
主线程
代码语言:javascript复制
package ThreadSafety;


//模拟经典案例:生产者与消费者模型
public class Demo2 {
    public static void main(String[] args) {
        //创建共享账户
        Account acc=new Account(111,0);

        //创建两个消费者对象
        Thread consumer1=new DrawThread(acc,"consumer1");
        consumer1.start();
        Thread consumer2=new DrawThread(acc,"consumer2");
        consumer2.start();
        //创建三个生产者
        Thread producer1=new SaveThread(acc,"producer1");
        producer1.start();
        Thread producer2=new SaveThread(acc,"producer2");
        producer2.start();
        Thread producer3=new SaveThread(acc,"producer3");
        producer3.start();


    }
}
运行结果
代码语言:javascript复制
producer2用户执行存钱操作,充入1000元成功!
producer2用户结束操作,余额1000.0元
consumer2用户执行取钱操作,余额充足,支付1000元成功!
consumer2用户结束操作,余额0.0元
consumer1用户执行取钱操作,余额不足,支付失败!
consumer1用户结束操作,余额0.0元
producer3用户执行存钱操作,充入1000元成功!
producer3用户结束操作,余额1000.0元
producer1用户执行存钱操作,余额充足,充值失败!
producer1用户结束操作,余额1000.0元
consumer2用户执行取钱操作,余额充足,支付1000元成功!
consumer2用户结束操作,余额0.0元
producer2用户执行存钱操作,充入1000元成功!
producer2用户结束操作,余额1000.0元
producer3用户执行存钱操作,余额充足,充值失败!
producer3用户结束操作,余额1000.0元
consumer1用户执行取钱操作,余额充足,支付1000元成功!
consumer1用户结束操作,余额0.0元

······

线程状态

线程状态

导致状态发生条件

NEW(新建)

线程刚刚被创建,但是尚未启动(没有调用start()方法)。只有线程对象,没有线程特征

Runnable(可运行的)

线程可以在Java虚拟机中运行的状态,可能正在运行自己的代码,也可能没有,取决于操作系统处理器。调用了start()方法。

Blocked(锁阻塞)

当一个线程试图获取一个对象锁,而该对象锁被其他的线程锁持有,则该线程进入Blocked状态,当该线程持有锁时,状态将改变为Runnable

Waiting(无限等待)

一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态,进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify()方法或notifyAll()方法才能将线程唤醒

Timed Waiting(计时等待)

同Waiting()状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态,这一状态将一致保持到超时期满或者接收到唤醒通知,带有超时参数的常用方法有:Thread.sleep,Object.wait

Terminated(被终止)

因为run()方法正常退出而死亡,或者因为没有捕获的异常终止了run()方法而死亡

注意:

可运行状态还可以被细分为两个状态:就绪状态和运行状态,就绪状态只是成功开启线程还没有真正运行,运行状态表示开始正常执行

sleep()和wait()区别在于sleep()休眠后不释放当前锁对象,所以在当前线程苏醒后可以直接继续当前锁对象内容,但是wait()表示释放当前锁对象,所以即便在苏醒后,也需要与其他线程争抢当前锁对象,成功抢到则可以执行,否则又变为锁阻塞状态

0 人点赞