• Java 多线程间通信


    JDK 1.5 以后, 将同步和锁封装成了对象, 并将操作锁的隐式方法定义到了该对象中, 将隐式动作变成了显示动作.

    Lock 接口

    1. Lock 接口, 位于 java.util.concurrent.locks 包中, 使用该接口需要导包.
    2. Lock 接口的出现替代了同步代码块或者同步函数, 因为同步代码块对于锁的操作(获取或释放)是隐式的.
      Lock 接口将同步的隐式锁操作变成显式锁操作. 同时,更为灵活, 可以在一个锁上加上多个监视器.
    3. Lock 接口中的方法:
      • lock(): 获取锁
      • unlock(): 释放锁, 这个动作是必须要完成的, 所以通常需要定义在 finally 代码块中
    4. 格式:
    Lock lock = new ReentrantLock(); // Lock 接口的实现类
    void show()
    {
        try
        {
            lock.lock(); //获取锁
            // 执行代码...
        }
        finally
        {
            lock.unlock(); // 释放锁
        }
    }
    

    Condition 接口

    1. Condition 接口的出现替代了 Object 类中的 wait(), notify(), notifyAll()方法,将这些
      监视器方法单独进行了封装, 变成 Condition 监视器对象, 可以与任意锁进行组合.
    2. 常用方法:
      • await(): 让线程处于冻结状态
      • signal(): 唤醒一个等待线程
      • signalAll(): 唤醒所有等待线程
    3. 格式:
      Condition c1 = lock.newCondition(); // 新建一个监视器对象

    JDK 升级以后的多生产者/多消费者

    class Resource
    {
        private String name;
        private int count = 1; // 记录烤鸭的编号
        private boolean flag = false;
    
        // 创建一个锁对象
        Lock lock = new ReentrantLock();
    
        // 通过已有的锁获取两组监视器, 一组监视生产者, 一组监视消费者
        Condition producer_con = lock.newCondition();
        Condition consumer_con = lock.newCondition();
    
        public void set(String name)
        {
            lock.lock(); //获取锁
            try
            {
                while(flag)
                    try{producer_con.wait();}catch(InterruptedException e){}
                this.name = name + count;
                count++;
                System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
                flag = true;
                consumer_con.signal();
            }
            finally
            {
                lock.unlock(); //释放锁
            }
        }
    
        public void out()
        {
            lock.lock(); //获取锁
            try
            {
                while(!flag)
                    try{consumer_con.wait();}catch(InterruptedException e){}
                Sytem.out.println(Thread.currentThread().getName()+ "...消费者.."+ this.name);
                flag = false;
                producer_con.signal();
            }
            finally
            {
                lock.unlock(); //释放锁
            }
        }
    }
    
    class Producer implements Runnable
    {
        Resource r;
        Producer(Resource r)
        {
            this.r = r;
        }
    
        public void run()
        {
            while(true)
            {
                r.set("烤鸭");
            }
        }
    }
    
    class Consumer implements Runnable
    {
        Resource r;
        Consumer(Resource r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
            {
                r.out();
            }
        }
    }
    
    class ProducerConsumerDemo
    {
        public static void main(String[] args)
        {
            // 创建资源
            Resource r = new Resource();
    
            // 创建任务
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);
    
            // 多生产者
            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
    
            // 多消费者
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
            Thread t4 = new Thread(con);
            Thread t5 = new Thread(con);
    
            // 开启线程
            t0.start();
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
        }
    }
    

    wait() 和 sleep() 的区别

    1. wait() 可以指定时间也可以不指定时间
      sleep() 必须指定时间.
    2. 在同步中, 对 CPU 的执行权和锁的处理不同
      • wait(): 释放执行权, 释放锁
      • sleep(): 释放执行权, 不释放锁

    停止线程

    1. run() 方法结束
    2. 怎么控制线程的任务结束呢?
      • 任务中都会有循环结构, 只要控制住循环就可以结束任务.
      • 控制循环通常就用定义标记(条件)来完成.
    class StopThread implements Runnable
    {
        // 定义标记
        private boolean flag = true;
        public void run()
        {
            while(flag)
            {
                System.out.println(Thread.currentThread().getName()+".....");
            }
        }
    
        // 对外提供改变标记的方法
        public void setFlag()
        {
            flag = false;    
        }
    }
    
    class StopThreadDemo
    {
        public static void main(String[] args)
        {
            StopThread st = new StopThread();
    
            Thread t0 = new Thread(st);
            Thread t1 = new Thread(st);
    
            t1.start();
            t2.start();
    
            int num = 1;
            for(;;)
            {
                if(++num==50)
                {
                    st.setFlag(); // 更改标记
                    break;
                }
                System.out.println("main....."+num);
            }
    
            System.out.println("over");
        }
    }
    
    1. 如果线程处于冻结状态, 无法读取标记, 如何结束呢?
      • 可以使用 interrupt() 方法将线程从冻结状态强制恢复到运行状态, 让线程具备 CPU 的执行资格.
      • 该强制动作会发生 InterruptedException, 需要处理.
    class StopThread implements Runnable
    {
        // 定义标记
        private boolean flag = true;
        public synchronized void run()  //此处将函数变为同步函数
        {
            while(flag)
            {
                try
                {
                    wait();//t0, t1 线程执行到这句时, 都会被处于冻结状态
                }
                catch(InterruptedException e)
                {
                    System.out.println(Thread.currentThread().getName()+"..."+e);
                    flag = false; // 更改标记
                }
                System.out.println(Thread.currentThread().getName()+".....");
            }
        }
    
        // 对外提供改变标记的方法
        public void setFlag()
        {
            flag = false;    
        }
    }
    
    class StopThreadDemo
    {
        public static void main(String[] args)
        {
            StopThread st = new StopThread();
    
            Thread t0 = new Thread(st);
            Thread t1 = new Thread(st);
    
            t0.start();
            t1.start();
    
            int num = 1;
            for(;;)
            {
                if(++num==50)
                {
                    t0.interrupt(); // 清除中断状态
                    t1.interrupt(); // 清除中断状态
                    break;
                }
                System.out.println("main....."+num);
            }
    
            System.out.println("over");
        }
    }
    

    线程类的其他方法

    1. setDaemon(boolean b) : 将该线程标记为守护线程(后台线程).
    2. join() : 临时加入一个线程运算时, 可以使用 join() 方法, 需要处理 InterruptedException
    class Demo implements Runnable
    {
        public void run()
        {
            for(int x=0; x<50; x++)
            {
                System.out.println(Thread.currentThread().getName()+"......"+x);
            }
        }
    }
    
    class JoinDemo
    {
        public static void main(String[] args) throws Exception
        {
            Demo d = new Demo();
    
            Thread t1 = new Thread(d);
            Thread t2 = new Thread(d);
    
            t1.start();
            t1.join(); // 临时加入 t1 线程, 执行权移交给 t1, 必须等 t1 运行完成后, 其他线程才运行
            t2.start();
    
            for(int x=0; x<50; x++)
            {
                System.out.println(Thread.currentThread().getName()+"..."+x);
            }
        }
    }
    
    1. toString() : 返回该线程的字符串表示形式, 包括线程名称, 优先级和线程组.
      • 优先级: 获取 CPU 执行权的机率, 分为 1~10个数字, 其中:
      • 最高优先级: MAX_PRIORITY , 代表数值 10
      • 最低优先级: MIN_PRIORITY , 代表数值 1
      • 默认优先级: NORM_PRIORITY , 代表数值 5
    2. yield(): 暂停当前正在执行的线程对象, 并执行其他线程.



  • 相关阅读:
    MFC子窗体、父窗体
    私有云计算安全问题不容忽视
    云计算更多的是一种模式而不是技术
    原型模式向量的原型
    企业发票异常分析分离进项与销项
    考试系统框架搭建
    抽象工厂模式人与肤色
    工厂方法模式加密算法
    简单工厂模式女娲造人
    企业发票异常分析导入,清洗
  • 原文地址:https://www.cnblogs.com/linkworld/p/7458559.html
Copyright © 2020-2023  润新知