zoukankan      html  css  js  c++  java
  • 线程的生产值者与消费者问题

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

    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);
            }
        }
    }
    
  • 相关阅读:
    eclipse里面已经提交的svn提交
    session 失效
    svn版本管理
    前端控制台调试经验
    python001环境搭建及入门 http://python.jobbole.com/81332/
    eclipse自己写makefile 建工程
    编码风格
    算法导论第22章22.2广度优先搜索
    vnc相关
    eclipse相关设置
  • 原文地址:https://www.cnblogs.com/Guard9/p/11153242.html
Copyright © 2011-2022 走看看