线程的生产值者与消费者问题

线程的状态转换图及常见的执行情况

1665530-20190708192840695-496756034.jpg

1- 线程通信
/**
    取钱的线程类。
 */
public class DrawThread extends Thread {
    // 定义一个成员变量接收账户对象
    private Account acc;
    public DrawThread(String name , Account acc){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 小明和小红来取钱。
        while(true){
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            acc.drawMoney(10000);
        }
    }
}

/**
    存钱的线程类。
 */
public class SaveThread extends Thread {
    // 定义一个成员变量接收账户对象
    private Account acc;
    public SaveThread(String name , Account acc){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 3个爸爸来反复的存钱。
        while(true){
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            acc.saveMoney(10000);
        }
    }
}

/**
 * 账户对象:
 */
public class Account {
    private String cardId ;
    private double money ; // 余额

    // 亲爹 干爹 岳父
    public synchronized void saveMoney(double money) {
        try{
            String name = Thread.currentThread().getName();
            // 判断是否有钱
            if(this.money == 0){
                // 没钱,需要存钱
                this.money += money ;
                System.out.println(name + "来存钱" + money+"成功,剩余"+this.money);

                // 钱已经取完了!!暂停自己
                this.notifyAll(); // 唤醒其他线程!
                this.wait(); // 等待自己
            }else{
                this.notifyAll(); // 唤醒其他线程!
                this.wait(); // 等待自己
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    // 小明/小红
    public synchronized void drawMoney(double money) {
        try{
            // 1.先拿到是谁来取钱:拿到当前线程的名字即可,名字是谁就是谁来取钱
            String name = Thread.currentThread().getName();
            // 2.判断余额是否足够 :
            if (this.money >= money) {
                // 钱够了
                // 3.更新余额
                this.money -= money;
                System.out.println(name + "来取钱,余额足够,吐出:" + money+",剩余"+this.money);

                // 钱已经取完了!!暂停自己
                this.notifyAll(); // 唤醒其他线程!
                this.wait(); // 等待自己

            }else{
                // 没钱了
                this.notifyAll(); // 唤醒其他线程!
                this.wait(); // 等待自己
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public Account() {

    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}

/**

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

线程通信的经典模型:生产者与消费者问题。
     生产者负责生成商品,消费者负责消费商品。
     生成不能不剩,消费不能没有,是一个同步模型。

线程通信必须先保证线程安全,否则代码会报出异常!!

线程通信的核心方法:
    public void wait()` : 让当前线程进入到等待状态 此方法必须锁对象调用.
    public void notify()` : 唤醒当前锁对象上等待状态的某个线程  此方法必须锁对象调用
    public void notifyAll()` : 唤醒当前锁对象上等待状态的全部线程  此方法必须锁对象调用

 */
public class ThreadCommunication {
    public static void main(String[] args) {
        // 1.创建一个共享资源账户对象
        Account acc = new Account("ISBC-110" , 0);

        // 2.定义2个取钱线程代表小明和小红
        new DrawThread("小明",acc).start();
        new DrawThread("小红",acc).start();

        // 3.定义3个存钱的线程代表 亲爸,岳父,干爹
        new SaveThread("亲爸",acc).start();
        new SaveThread("干爹",acc).start();
        new SaveThread("岳父",acc).start();
    }
}
2-线程休眠
/**
    Thread.sleep(5000):参数是毫秒,让当前所在线程对象休眠5s。
 */
public class ThreadDemo {
    public static void main(String[] args) {
        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println("输出:"+i);
            if(i == 5){
                try {
                    // 让当前线程休眠5s,休眠是不释放锁的。
                    // 项目经理让我加上这行代码,如果用户交钱了就注释掉。
                    Thread.sleep(5000); //
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
3-死锁
/**

 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

 java 死锁产生的四个必要条件:
     1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
     2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
     3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
     4、循环等待,即存在一个等待队列:p1要p2的资源,p2要p1的资源。这样就形成了一个等待环路

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失实现死锁一般需要进行锁资源的嵌套才会出现死锁。
 */
public class ThreadDead {
    // 至少要两个资源
    public static Object resources1 = new Object();
    public static Object resources2 = new Object();

    public static void main(String[] args) {
        // 至少要2个线程。
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resources1){
                    System.out.println("线程对象1对资源1上锁占用资源1");
                    System.out.println("线程对象1开始请求资源2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (resources2){
                        System.out.println("线程对象1对资源2上锁占用资源2");
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resources2){
                    System.out.println("线程对象2对资源2上锁占用资源2");
                    System.out.println("线程对象2开始请求资源1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (resources1){
                        System.out.println("线程对象2对资源1上锁占用资源1");
                    }
                }
            }
        }).start();
    }
}
4-线程池的创建
/**
 线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建和销毁线程对象的操作,无需反复创建线程而消耗过多资源。

合理利用线程池能够带来三个好处
1.降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.提高响应速度
3.提高线程的可管理性(线程池可以约束系统最多只能有多少个线程,不会因为线程过多而死机)

线程池的核心思想:线程复用,同一个线程可以被重复使用线程池启动后是不会死亡的,因为后续还要重复使用的。void shutdown():会等全部线程执行完毕才关闭。比较友好!
List<Runnable> shutdownNow():立即关闭,不管是否执行完毕
 */
public class ThreadPoolsDemo02 {
    public static void main(String[] args) {
        // 1.创建一个线程池 : 线程池固定放置三个线程
        ExecutorService pools = Executors.newFixedThreadPool(3);
        // 2.给线程池提交任务,提交任务的时候会自动创建线程对象了。
        Runnable target = new MyRunnable();
        pools.submit(target); // 这里提交任务会自动创建线程对象,并自动启动!
        pools.submit(target); // 这里提交任务会自动创建线程对象,并自动启动!
        pools.submit(target); // 这里提交任务会自动创建线程对象,并自动启动!
        pools.submit(target); // 这里不会再创建线程了,因为线程池已经满了,这里会复用之前的线程!
//        pools.shutdown();
//        pools.shutdownNow();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i = 0 ; i < 3 ; i++ ){
            System.out.println(Thread.currentThread().getName()+" => " +i);
        }
    }
}

转载于:https://www.cnblogs.com/Guard9/p/11153242.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值