多线程(6)——线程通信、管程法、信号灯法、线程池

[学习视频B站狂神java多线程:https://www.bilibili.com/video/BV1V4411p7EF?]

线程通信

让不同线程之间进行交流,而不是独自执行自己的任务,会传递一些信息

Java提供的用于解决线程之间通信问题的方法

  • wait():表示线程一直等待,知道其他线程通知,与sleep不同,wait会释放锁
  • wait():指定等待的毫秒数
  • notify():唤醒一个处于等待状态的线程
  • notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度
  • 所有的方法均是Object类的方法,都只能在同步方法或同步代码块中使用,否则会抛出异常

应用场景:生产者和消费者问题

  • 对于生产者,没有生产产品之前,要通知消费者等待,生产了产品之后,需要马上通知消费者消费
  • 对于消费者,消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费

管程法

  • 生产者:负责生产数据
  • 消费者:负责处理数据
  • 缓冲区:消费者不能直接使用生产者的数据,通过缓冲区进行
  • 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
//生产者消费者:缓冲区(管程法)
public class TestPC1 {
     
    public static void main(String[] args) {
     
        box box=new box();
        Productor p=new Productor(box);
        Customer c=new Customer(box);
        p.start();
        c.start();
    }
}

//生产者
class Productor extends Thread {
     
    box box;

    public Productor(box box) {
     
        this.box = box;
    }

    @Override
    public void run() {
     
        synchronized (box) {
     
            for (int i = 1; i <= 100; i++) {
     
                try {
     
                    box.push(new Chicken(i));
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
                System.out.println("生产者生产第" + i + "只鸡");
            }
        }
    }
}

//消费者
class Customer extends Thread {
     
    box box;
    public Customer(box box){
     
        this.box=box;
    }

    @Override
    public void run() {
     
        synchronized (box){
     
            for(int i=1;i<=100;i++){
     
                try {
     
                    System.out.println("消费者消费第"+box.pop().id+"只鸡");
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            }
        }
    }
}

//缓冲区
class box {
     
    //容器大小
    Chicken[] c = new Chicken[10];
    //容器已有产品个数
    int count = 0;

    //生产者放入产品
    public void push(Chicken chicken) throws InterruptedException {
     
        //判断容器是否满
        if (count == c.length) {
     
            //如果满,生产者等待
            this.wait();
        }
        //如果不满,则放入产品
        c[count++] = chicken;
        this.notifyAll();
    }

    //消费者消费产品
    public Chicken pop() throws InterruptedException {
     
        //判断容器是否为空
        if (count == 0) {
     
            //如果空,等待并通知生产者生产
            this.wait();

        }
        //如果不空,则消费
        this.notifyAll();
        return c[--count];
    }

}

//产品对象
class Chicken {
     
    int id;

    public Chicken(int id) {
     
        this.id = id;
    }
}

信号灯法

设定一个用于判断生产者生产或者消费者消费的信号灯,信号灯的不同状态,进行相应的线程工作

//生产者消费者:信号灯法
//信号灯法可以看作没有缓冲或者缓冲区容量为1,此时将缓冲区容量的判断条件放在一个boolean中,因此不需要缓冲区
//如在两个线程中,线程A工作,B就停止,线程B工作,A就停止,两者不能同时
public class TestPC2 {
     
    public static void main(String[] args) {
     
        Product product = new Product();
        Story story = new Story(product);
        Buyer buyer = new Buyer(product);
        story.start();
        buyer.start();
    }
}

//生产者
class Story extends Thread {
     
    Product product;

    public Story(Product product) {
     
        this.product = product;
    }

    @Override
    public void run() {
     
        for (int i = 0; i < 24; i++) {
     
            if (i % 2 == 0) {
     
                try {
     
                    product.push("面包");
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            } else {
     
                try {
     
                    product.push("巧克力");
                } catch (InterruptedException e) {
     
                    e.printStackTrace();
                }
            }
        }
    }
}

//消费者
class Buyer extends Thread {
     
    Product product;

    public Buyer(Product product) {
     
        this.product = product;
    }

    @Override
    public void run() {
     
        for (int i = 0; i < 24; i++) {
     
            try {
     
                product.pop();
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }
    }
}

//产品对象
class Product {
     
    String thing;
    boolean flag = true;//判断标志位,true代表生产者生产,消费者等待,false代表生产者等待,消费者消费

    public void push(String str) throws InterruptedException {
     
        synchronized (this) {
     
            if (!flag) {
     
                this.wait();
            }
            //通知消费者消费
            System.out.println("生产者生产了"+str);
            this.thing = str;
            this.flag = !this.flag;
            this.notifyAll();
        }
    }

    public void pop() throws InterruptedException {
     
        synchronized (this) {
     
            if (flag) {
     
                this.wait();
            }
            //通知生产者生产
            System.out.println("消费者消费了"+thing);
            this.flag = !this.flag;
            this.notifyAll();
        }
    }
}

线程池

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
  • 思路:提前创建好多个线程,放入线程池,使用时直接获取,使用完放回,避免频繁创建销毁
  • 方法:1、corePoolSize:核心池大小
    2、maximunPoolSize:最大线程数
    3、keepAliveTime:线程没有任务时多长时间会终止

线程池的实现

  • ExecutorService:线程池接口,常见子类ThreadPoolExecutor
    1、void execute(Runnable command):执行命令,没有return,用来执行Runnable
    2、 Future submit(Callable task):执行任务,有return,用来执行Callable
    3、void shutdown():关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型线程池
public class TestThreadPool {
     
    public static void main(String[] args) {
     
        //创建服务,创建线程池
        ExecutorService service= Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //关闭链接
        service.shutdown();
    }
}
class MyThread implements Runnable{
     
    @Override
    public void run() {
     
            System.out.println(Thread.currentThread().getName());
    }
}

多线程(6)——线程通信、管程法、信号灯法、线程池_第1张图片

你可能感兴趣的:(java,多线程)