• Java多线程


    前言

    在拥有多核CPU的情况下,Java的线程可以被同时调度到不同的CPU上,同时执行;这是Python线程和Java线程的区别;

    Java默认会有3个线程分别为main方法对应的线程(主线程)、处理异常的线程(异常处理线程)和垃圾收集器线程(GC线程);

    其中异常处理线程可以中断主线程的执行;

    无论使用什么语言,在并行和并发编程模式下都会遇到同类线争抢同1个资源这样的内部矛盾,也会遇到不同类线程如何完成同步通信这样的外部矛盾;

    在Golang中使用锁解决Gorutine争抢资源的问题,使用channel解决Gorutine之间数据通信,注意这是在通过2种方法解决2种不同的问题;

    如果多个线程之间要想通信就需要有1个共同的通信介质,这就又回到多个线程争抢同1个资源的问题上,所以锁是实现线程间通信的基础。 

    一、线程创建

    在Java中我们可以通过3种方式创建线程。

    1.继承Thread类 

    我们可以通过继承java.lang.Thread类的方式开生成线程对象;

    package Threads;
    
    //1):定义一个类A继承于java.lang.Thread类.
    class MusicThread extends Thread {
        //2):在A类中覆盖Thread类中的run方法.
        public void run() {
            //3):在run方法中编写需要执行的操作
            for (int i = 0; i < 50; i++) {
                System.out.println("播放音乐" + i);
            }
        }
    }
    
    class CreateThread01 {
        public static void main(String[] args) {
            for (int j = 0; j < 50; j++) {
                System.out.println("运行游戏" + j);
                if (j == 10) {
                    //4):在main方法(线程)中,创建线程对象,并启动线程.
                    MusicThread music = new MusicThread();
                    music.start();
                }
            }
        }
    }

    1.2.设置和读取线程名称

    (1)setName和getName

    我们可以调用setName和getName方法,给线程设置名称以帮助我们区分线程。

    package Threads;
    
    //定义一个类A继承于java.lang.Thread类.
    class MusicThread extends Thread {
        public void run() {
            for (int i = 0; i < 50; i++) {
                //4.获取子线程名称
                String Threadname = super.getName();
                System.out.println(Threadname + "播放音乐" + i);
            }
        }
    }
    
    class CreateThread01 {
        public static void main(String[] args) {
            for (int j = 0; j < 50; j++) {
                //1.主线程设置线程名称
                Thread.currentThread().setName("Game线程");
                //2.主线程获取线程名称
                System.out.println(Thread.currentThread().getName() + "运行游戏" + j);
                if (j == 10) {
                    MusicThread music = new MusicThread();
                    //3.设置子线程的名称
                    music.setName("Muisc线程");
                    music.start();
                }
            }
        }
    }

    (2)构造器设置线程名称

    还可以在构造线程对象时,就给线程设置名称。

    package Threads;
    
    //定义一个类A继承于java.lang.Thread类.
    class MusicThread extends Thread {
        public MusicThread(String name) {
            super(name); //2.调用父类的有参构造器
        }
    
        public void run() {
            for (int i = 0; i < 50; i++) {
                //3.获取子线程名称
                System.out.println(this.getName() + "播放音乐" + i);
            }
        }
    }
    
    class CreateThread01 {
        public static void main(String[] args) {
            for (int j = 0; j < 50; j++) {
                System.out.println("运行游戏" + j);
                if (j == 2) {
                    //1.利用构造器设置子线程名称
                    MusicThread music = new MusicThread("音乐线程" + j);
                    music.start();
                }
            }
        }
    }

     1.3.抢火车票

    package Threads;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    //购票窗口
    class TicketWindow extends Thread {
        //一共10张车票:多个线程对象共享10张票;
        static int ticketNum = 10;
        //加锁
        Lock lock = new ReentrantLock();
    
        public TicketWindow(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            //100个人抢10张票
            for (int i = 0; i <= 100; i++) {
                //判断是否售罄?
                if (ticketNum > 0) {
                    lock.lock();
                    ticketNum--;
                    System.out.printf("我在%s买到了从北京到哈尔滨的第%s张车票。\n", super.getName(), ticketNum);
                    lock.unlock();
                }
    
            }
        }
    }
    
    
    public class BuyTicket02 extends Thread {
        public static void main(String[] args) {
            TicketWindow t1 = new TicketWindow("窗口1");
            t1.start();
            TicketWindow t2 = new TicketWindow("窗口2");
            t2.start();
            TicketWindow t3 = new TicketWindow("窗口3");
            t3.start();
        }
    
    }

    2.实现Runnable接口

     我们可以通过实现Runabke接口,制造线程对象。

    package Threads;
    
    //实现Runnable接口
    class TestThread implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
    
    
    public class CreateThread02 {
        public static void main(String[] args) {
            //创建子线程对象
            TestThread tt = new TestThread();
            Thread t = new Thread(tt, "子线程");
            t.start();
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
    
    
        }
    
    }

    2.1.抢火车票

    package Threads;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    //实现Runnable接口
    class TestThread implements Runnable {
        int ticketNumber = 10;
        private Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                lock.lock();
                if (ticketNumber > 0) {
                    ticketNumber--;
                    System.out.printf("我在%s抢到了北京到保定的第%d张票\n", Thread.currentThread().getName(), ticketNumber);
    
                }
                lock.unlock();
    
    
            }
        }
    }
    
    
    public class CreateThread02 {
        public static void main(String[] args) {
            //创建1个线程对象共享票和锁
            TestThread tt = new TestThread();
            //开启线程1
            Thread t1 = new Thread(tt, "窗口1");
            t1.start();
            //开启线程2
            Thread t2 = new Thread(tt, "窗口2");
            t2.start();
            //开启线程3
            Thread t3 = new Thread(tt, "窗口3");
            t3.start();
    
    
        }
    
    }

    3.实现Calable接口

    以上两种创建线程的方式,都需要有1个run(),run方法的不足之处就是无法有返回值也无法抛出异常

    package Threads;
    
    import java.util.Random;
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    class TestThread03 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            return new Random().nextInt(100);
        }
    }
    
    public class CreateThread03 {
        public static void main(String[] args) throws Exception {
            //定义1个线程对象
            TestThread03 tr = new TestThread03();
            FutureTask ft = new FutureTask(tr);
            Thread t3 = new Thread(ft);
            t3.start();
    
            //获取线程得到的返回值
            Object obj = ft.get();
            System.out.println(obj);
    
    
        }
    }

    4.线程的生命周期

     

    二、线程的常用方法

    线程创建完成之后,我们可以调用线程对象中封装的一些API对线程进行操作;

    1.setPriority()

    我们可以通过setPriority设置线程被CPU调度的优先级。

        * The minimum priority that a thread can have.
         */
        public final static int MIN_PRIORITY = 1;
    
       /**
         * The default priority that is assigned to a thread.
         */
        public final static int NORM_PRIORITY = 5;
    
        /**
         * The maximum priority that a thread can have.
         */
        public final static int MAX_PRIORITY = 10;

    正常优先级 为5,最小优先级为1,最大优先级为10;

    package Threads;
    
    class Task01 extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("task01--" + i);
            }
        }
    }
    
    class Task02 extends Thread {
        @Override
        public void run() {
            for (int i = 20; i < 30; i++) {
                System.out.println("task02--" + i);
            }
        }
    }
    
    public class ThreadMthods {
        public static void main(String[] args) {
            Task01 t1 = new Task01();
            //设置线程1的优先级别为1
            t1.setPriority(1);
            t1.start();
    
            //设置线程2的优先级别为10
            Task02 t2 = new Task02();
            t1.setPriority(10);
            t2.start();
        }
    }

    2.join()

    当一个线程调用了join方法之后,这个线程就会优先被执行;

    当它执行结束后,CPU才可以去执行其他线程;

    注意:线程必须先执行start()再执行join(),才能生效;

    package Threads;
    
    
    class Task03 extends Thread {
        Task03() {
        }
    
        Task03(String name) {
            super.setName(name);
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(this.getName() + i);
            }
        }
    }
    
    
    public class ThreadMthods {
        public static void main(String[] args) throws Exception {
            for (int i = 0; i < 100; i++) {
                if (i == 6) {
                    Task03 t3 = new Task03("子线程");
                    t3.start();
                    t3.join(); //半路杀出个程咬金
    
                }
                Thread.currentThread().setName("main线程");
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }

    3.sleep()

    我们可以通过sleep()方法阻塞当前线程;

    package Threads;
    
    public class ThreadMthods {
        public static void main(String[] args) throws Exception {
            System.out.println("开始");
            Thread.sleep(6000); //线程阻塞6秒,再进入就绪状态,在被CPU调度
            System.out.println("结束");
        }
    }

    4.setDaemon(true)

    在古代皇帝死了,他的妃子通常会哭的很惨,因为接下来她也要殉葬;

    在Python中子线程默认就是主线程的守护线程,一旦主线程提前结束,子线程即使没有结束也要强制其结束; 

    而Java中的线程是跑在不同的CPU上的,主线程和子线程的地位是平等的;

    在Java中,默认情况下即使主线程结束了,子线程也可以继续执行;

    但是只要我们把子线程设置为守护线程,一旦主线程结束,子线程会立即结束;

    package Threads;
    
    class Task extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("子线程" + i);
            }
        }
    }
    
    
    public class ThreadMthods {
        public static void main(String[] args) throws Exception {
            Task t1 = new Task();
            t1.setDaemon(true);//注意先设置守护线程,在启动该线程
            t1.start();
            //在Python中子线程默认就是主线程的守护线程,一旦主线程提前与子线程结束,子线程即使没有结束也要强制其结束;
            //而Java的线程可以跑在不同的CPU上,主线程和子线程是平等的;默认情况下即使主线程结束了,子线程也可以继续执行;
            for (int i = 0; i < 10; i++) {
                System.out.println("主线程" + i);
            }
        }
    }

    5.stop()

    我们可以通过调用stop方法,来结束当前线程;

    package Threads;
    
    
    public class ThreadMthods {
        public static void main(String[] args) throws Exception {
            for (int i = 1; i < 11; i++) {
                if (i == 7) {
                    Thread.currentThread().stop();
                }
                System.out.println("主线程" + i);
            }
        }
    }

    三、线程安全问题(内部矛盾)

    我这里所说的线程安全问题即 N个执行相同任务的线程,争抢同1个资源;

    例如:100个人在北京西站去抢10张北京到哈尔滨的火车票;

    在Java中我们可以通过同步代码块、静态同步方法、显示锁解决这种问题;

    1.synchronized ()同步代码块

    package Threads;
    
    
    class TicketAgency extends Thread {
        static int tickets = 10;
    
        TicketAgency() {
        }
    
        TicketAgency(String name) {
            super.setName(name);
        }
    
        @Override
        public void run() {
            //确保每个线程对象,使用的都是同一把锁。不要使用this
            synchronized ("zhanggen") {
                for (int i = 0; i <= 100; i++) {
                    if (tickets > 0) {
                        System.out.printf("我在%s,抢到了北京到哈尔滨的第%d张票!\r\n", super.getName(), tickets--);
                    }
                }
            }
        }
    }
    
    
    public class ThreadMthods {
        public static void main(String[] args) throws Exception {
            TicketAgency agency01 = new TicketAgency("窗口1");
            agency01.start();
            TicketAgency agency02 = new TicketAgency("窗口2");
            agency02.start();
            TicketAgency agency03 = new TicketAgency("窗口3");
            agency03.start();
    
        }
    }

    2.静态同步方法

    public static synchronized
    package Threads;
    
    
    class TicketAgency extends Thread {
        static int tickets = 10;
    
        TicketAgency() {
        }
    
        TicketAgency(String name) {
            super.setName(name);
        }
    
        @Override
        public void run() {
            //必须确保多个线程对象,使用的都是同1把锁。不要使用this
            for (int i = 0; i <= 100; i++) {
                buyTicket();
            }
    
        }
    
        //同步方法:增加static修饰符,确保锁住的不是this
        public static synchronized void buyTicket() {
            if (tickets > 0) {
                System.out.printf("我在%s,抢到了北京到哈尔滨的第%d张票!\r\n", Thread.currentThread().getName(), tickets--);
            }
        }
    }
    
    
    public class ThreadMthods {
        public static void main(String[] args) throws Exception {
            TicketAgency agency01 = new TicketAgency("窗口1");
            agency01.start();
            TicketAgency agency02 = new TicketAgency("窗口2");
            agency02.start();
            TicketAgency agency03 = new TicketAgency("窗口3");
            agency03.start();
    
        }
    }

    3.Lock类

    synchronized是Java中的关键字,这个关键字的识别是依靠JVM来识别完成的,是虚拟机级别的;

    在JKD1.5之后我们可以通过API级别的Lock显示锁(自己lock+unlock)实现同步,这种方式更加灵活。

    package Threads;
    
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class TicketAgency extends Thread {
        static int tickets = 10;
        //拿来一把锁
        Lock lock = new ReentrantLock(); //多态 接口=实现类
    
        TicketAgency() {
        }
    
        TicketAgency(String name) {
            super.setName(name);
        }
    
        @Override
        public void run() {
            for (int i = 0; i <= 100; i++) {
                lock.lock();
                if (tickets > 0) {
                    System.out.printf("我在%s,抢到了北京到哈尔滨的第%d张票!\r\n", Thread.currentThread().getName(), tickets--);
                }
                lock.unlock();
    
            }
    
        }
    
    }
    
    
    public class ThreadMthods {
        public static void main(String[] args) throws Exception {
            TicketAgency agency01 = new TicketAgency("窗口1");
            agency01.start();
            TicketAgency agency02 = new TicketAgency("窗口2");
            agency02.start();
            TicketAgency agency03 = new TicketAgency("窗口3");
            agency03.start();
    
        }
    }

    四、线程同步通信问题(外部矛盾)

    我这里所说的线程通信问题 即 2个执行不同任务的线程之间如何完成通信,和以上的线程安全问题不是同1个问题;

    例如:生产者和消费者模式 A生产10件产品,B消费者来消费这10见商品,并保证供需平衡;

    所以A和B阵营主内除了保证自身阵营内部的线程安全,还需要在A和B两个阵营之间建立1种顺序的通信机制;

     A阵营生产完了通知B阵营来消费,B阵营消费完了通知A阵营继续生产;

    1.使用同步代码块

    package zhanggen.com;
    
    //商品类
    public class Product {
        //品牌
        private String band;
        //名字
        private String name;
    
        public Product() {
        }
    
        //set和get方法
        public Product(String band, String name) {
            this.band = band;
            this.name = name;
        }
    
    
        public String getBand() {
            return band;
        }
    
        public String getName() {
            return name;
        }
    
        public void setBand(String band) {
            this.band = band;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    Product

    ---------

    package zhanggen.com;
    
    public class Producer extends Thread {
        //生产的商品
        private Product p;
    
        public Producer(Product p) {
            this.p = p;
        }
    
        @Override
        public void run() {
            //生产10个产品
            for (int i = 1; i <= 10; i++) {
                //利用同步代码块解决供需争抢的问题
                synchronized (p) {
                    if (i % 2 == 0) {
                        //生产费列罗巧克力
                        p.setBand("巧克力");
                        try {
                            Thread.sleep(100);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
    
                        p.setName("费列罗巧克力");
    
    
                    } else {
                        //生产哈尔滨啤酒
                        p.setBand("啤酒");
                        try {
                            Thread.sleep(100);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        p.setName("哈尔滨啤酒");
    
                    }
                    //将生产信息做1个打印
                    System.out.printf("生产者生产出: %s品牌 的 %s\r\n", p.getBand(), p.getName());
                }
    
            }
        }
    }
    Producer.java

    --------------

    package zhanggen.com;
    
    //消费者线程
    public class Cousumer extends Thread {
        //共享商品
        private Product p;
    
        Cousumer(Product p) {
            this.p = p;
        }
    
        @Override
        public void run() {//消费者消费10次
            //利用同步代码块解决供需争抢的问题
            synchronized (p) {
                for (int i = 1; i <= 10; i++) {
                    System.out.printf("消费者消费了:%s的%s\n", p.getBand(), p.getName());
                }
            }
    
        }
    }
    Consumer.java

    -----------------

    package zhanggen.com;
    
    public class Market {
        public static void main(String[] args) {
            //共享商品
            Product p = new Product();
            //创建生产者线程
            Producer producer = new Producer(p);
    
            //创建消费者
            Cousumer cousumer = new Cousumer(p);
    
            producer.start();
            cousumer.start();
    
        }
    }
    Market.java

    2.使用同步方法

    package zhanggen.com;
    
    //商品类
    public class Product {
        //品牌
        private String band;
        //名字
        private String name;
    
        public Product() {
        }
    
        //set和get方法
        public Product(String band, String name) {
            this.band = band;
            this.name = name;
        }
    
    
        public String getBand() {
            return band;
        }
    
        public String getName() {
            return name;
        }
    
        public void setBand(String band) {
            this.band = band;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        //利用同步方法解决供需争抢的问题:要么生产!要么消费! 此时调用make锁住的就是谁
        public synchronized void make(int i) {
            if (i % 2 == 0) {
                //生产费列罗巧克力
                this.setBand("巧克力");
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
    
                this.setName("费列罗巧克力");
    
    
            } else {
                //生产哈尔滨啤酒
                this.setBand("啤酒");
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                this.setName("哈尔滨啤酒");
    
            }
            //将生产信息做1个打印
            System.out.printf("生产者生产出: %s品牌 的 %s\r\n", this.getBand(), this.getName());
    
        }
    
    
        //消费
        public synchronized void cost() {
            System.out.printf("消费者消费:%s的%s\n", this.getBand(), this.getName());
    
        }
    
    }
    Product.java

    --------------

    package zhanggen.com;
    
    public class Producer extends Thread {
        private Product p;
    
        public Producer(Product p) {
            this.p = p;
        }
    
    
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                p.make(i);
            }
        }
    }
    Producer.java

    ---------------------

    package zhanggen.com;
    
    //消费者线程
    public class Cousumer extends Thread {
        //共享商品
        private Product p;
    
        Cousumer(Product p) {
            this.p = p;
        }
    
        @Override
        public void run() {//消费者消费10次
            for (int i = 1; i <= 10; i++) {
                p.cost();
            }
    
    
        }
    }
    Consumer.java

    ------------------

    package zhanggen.com;
    
    public class Market {
        public static void main(String[] args) {
            //共享商品
            Product p = new Product();
            //创建生产者线程
            Producer producer = new Producer(p);
    
            //创建消费者
            Cousumer cousumer = new Cousumer(p);
    
            producer.start();
            cousumer.start();
    
        }
    }
    Market.java

    3.wait()和notify()

    在Java对象中有2种池:锁池(synchrnized)和等待池(wait()、notify()、notifyAll())

    如果1个线程对象调用了某个对象的wait()方法,那么该线程进入到该对象的等待池中(并且将锁释放出来,也就是让别的线程先执行。);

    如果未来的某一刻,另外一个线程调用了同一个对象的notify()或者notifyAll()方法,那么等待池中的线程将被唤起,然后进入到对象的锁池里面去获取该对象的锁;

    如果获得锁成功之后,该线程就会沿着wait()方法之后的继续执行。注意是沿着wait()方法之后。

    wait()和notify()必须放在synchronized代码块或者由synchronized修饰的方法内;

    package zhanggen.com;
    
    
    //商品类
    public class Product {
        //品牌
        private String band;
        //名字
        private String name;
    
        //生产和消费指示灯:0正在生产,1可消费
        boolean flag = false;
    
        public Product() {
        }
    
        //set和get方法
        public Product(String band, String name) {
            this.band = band;
            this.name = name;
        }
    
    
        public String getBand() {
            return band;
        }
    
        public String getName() {
            return name;
        }
    
        public void setBand(String band) {
            this.band = band;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        //利用wait和notice进行线程通信,实现多线程同步执行;
        public synchronized void make(int i) {
            //如果消费线程正在消费,生产线程进入等待池。释放锁让给消费线程;
            if (flag == true) {
                try {
                    wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (i % 2 == 0) {
                //生产费列罗巧克力
                this.setBand("巧克力");
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
    
                this.setName("费列罗");
    
    
            } else {
                this.setBand("啤酒");
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                this.setName("哈尔滨");
    
            }
            System.out.printf("生产者生产出%s,品牌为%s\r\n", this.getBand(), this.getName());
            flag = true;
            // 唤醒等待池中的1个消费线程,来消费。
            notify();
        }
    
    
        //利用wait和notice进行线程通信,实现多线程同步执行;
        public synchronized void cost() {
            //如果生产线程正在生产,消费线程进入等待词,让出锁给生产线程;
            if (flag == false) {
                try {
                    wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.printf("消费者消费了%s,品牌为%s\n", this.getBand(), this.getName());
            flag = false;
            //唤醒等待池中的1个生产线程,去生产。
            notify();
    
        }
    
    }

    4.await

    以上我们只是完成1个生产者线程和1个消费者线程之间1对1的顺序通信;

    在JDK1.5之后我们可以使用await()和signal(),实现多个消费者线程 和多个生产者线程之间,多对多通信的顺序通信;

    package zhanggen.com;
    
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    //商品类
    public class Product {
        //品牌
        private String band;
        //名字
        private String name;
    
        //生产和消费指示灯:0正在生产,1可消费
        boolean flag = false;
    
        //声明1把Lock锁
        Lock lock = new ReentrantLock();
        //生产者的等待队列
        Condition producersCondition = lock.newCondition();
        //消费者的等待队列
        Condition consumersCondition = lock.newCondition();
    
        public Product() {
        }
    
        //set和get方法
        public Product(String band, String name) {
            this.band = band;
            this.name = name;
        }
    
    
        public String getBand() {
            return band;
        }
    
        public String getName() {
            return name;
        }
    
        public void setBand(String band) {
            this.band = band;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void make(int i) {
            //如果消费线程正在消费,生产线程进入生产者的等待池等待
            lock.lock();
            try {
                if (flag == true) {
                    try {
                        //生产线程进入生产者的等待队列
                        producersCondition.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                if (i % 2 == 0) {
                    this.setBand("巧克力");
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
    
                    this.setName("费列罗");
    
    
                } else {
                    this.setBand("啤酒");
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    this.setName("哈尔滨");
    
                }
                System.out.printf("生产者生产出%s,品牌为%s\r\n", this.getBand(), this.getName());
                flag = true;
                // 唤醒消费队列中1个消费线程,来消费。
                consumersCondition.signal();
            } finally {
                lock.unlock();
            }
        }
    
    
        public void cost() {
            lock.lock();
    
            try {
                if (flag == false) {
                    try {
                        //消费线程进入消费者的等待队列
                        consumersCondition.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.out.printf("消费者消费了%s,品牌为%s\n", this.getBand(), this.getName());
                flag = false;
                //唤醒生产者队列中的1个生产线程,去生产
                producersCondition.signal();
    
            } finally {
                lock.unlock();
            }
        }
    
    }

    参考

  • 相关阅读:
    方差、协方差、相关系数的理解
    yii2原生sql
    Oracle中日期作为条件的查询
    IDEA 中tomcat图片储存和访问虚拟路径(图片和程序分家)
    nginx配置静态资源:配置绝对路径
    一般spring配置上下文
    spring boot 集成 redis lettuce(jedis)
    windows下面同时部署多个tomcat的方法
    oracel: 通过特殊表序列来实现oracle自增id (mybatis实现自增id)
    使用fastjson 进行jsonObject转实体类对象
  • 原文地址:https://www.cnblogs.com/sss4/p/15598507.html
Copyright © 2020-2023  润新知