栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java基础(二十)——守护线程(setDaemon)、合并线程(join)、读写分离集合(CopyOnWriteArrayList)、生产者消费者模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java基础(二十)——守护线程(setDaemon)、合并线程(join)、读写分离集合(CopyOnWriteArrayList)、生产者消费者模式

Java基础(二十)——守护线程 一、守护线程——setDaemon() 1、概念和用法

守护线程:当非守护线程销毁的时候,守护线程跟着销毁。当运行的唯一线程是守护线程时,Java虚拟机将退出。

用法:

注意:线程启动前必须调用此方法。

2、效果

主线程循环输出10次,子线程循环输出一百次,效果:

可以看到,主线程输出完毕以后,子线程会一直输出。

如果给子线程设置了守护线程以后,主线程执行完毕,子线程会跟着销毁:

3、额外知识:垃圾回收

只有 main 方法的时候,也是有子线程的,这个线程就是垃圾回收线程,也称之为垃圾回收机制。

二、合并线程——join() 1、解释

当把 A 线程并入到 B 线程之后,设置一些条件给 B 线程,当条件到达时,B 线程会暂停执行,会等 A 线程执行完毕之后再执行。

2、效果

代码:

public class Demo01 {
    public static void main(String[] args) {
        //A线程
        MyThread1 thread1 = new MyThread1();
        //B线程
        MyThread2 thread2 = new MyThread2(thread1);     //  传递 A 线程。这里 B 是 A 的守护线程。
        thread1.start();
        thread2.start();
    }
}

//A线程
class MyThread1 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("A线程:"+i);
        }
    }
}

//B线程
class MyThread2 extends Thread{
    private Thread thread;

    public MyThread2(Thread thread) {   // 通过有参构造方法传递 A 线程进来
        this.thread = thread;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(i == 50){        //  当 B 等于 50 的时候,就停止,等 A 执行完毕,再执行。
                try {
                    //把A线程并入到B线程中
                    thread.join();          //  调用守护线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("B:"+i);
        }
    }
}

效果:

三、CopyOnWriteArrayList——读写分离集合 1、迭代时修改结构发生异常

前面学过,ArrayList 是动态数组,当往中间删除或增加的时候,后面数据的下标会发生变化。如果发生变化的时候进行其他操作呢?

看代码:

在迭代的时候,进行增加操作,结果是什么样呢?

结果是会报错:

在使用 ArrayList 迭代数据的时候,如果修改集合结构会发生并发修改异常。

所以做不了一边遍历一边修改集合结构(删除或者添加等等这类操作)。这显然不符合很多业务场景的使用。所以,有其他集合可以完成。

2、CopyonWriteArrayList

CopyonWriteArrayList 是读写分离的集合。


现在换成这个数组,再来看看结果:

这时候可以发现,能够进行相应的操作。

3、原理

从上面的结果中也可以大概猜出来一点了,边修改边读的时候,读的还是原本的数据,修改完了再读,才是修改后的数据。


原理:
在进行操作的时候,会复制出另外一个一模一样的数组出来作为副本,这时候如果有删除或者添加的操作,是作用到新出来的副本那里,但是读的时候,还是读的原来的数组。当所有操作完毕以后,这时候就会指向新的数组这里,这时候再读,就是读新的数组。

四、生产者消费者模式——设计模式 1、概念解释

很好解释:比如去买奶茶,或者买什么东西,商家肯定是先做好一部分先存着,不可能来一个才开始准备一个。所以商家会准备一部分,有消费者来了,就进行售卖,售卖的同时也会制作。如果卖完了,就会暂停售卖,这时候在制作商品。制作到一定份数的商品时,又会开放售卖。

生产者、消费者、奶茶、存放奶茶的柜台、运行的主程序,总共这五个文件。

生产者消费者模式是一个很经典的模式。

2、生产者代码
public class Produce implements Runnable{
    private Counter counter;

    public Produce(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        //一个人做100杯奶茶
        for (int i = 0; i < 100; i++) {
            //生产奶茶
            MilkyTea milkyTea = new MilkyTea(i, Thread.currentThread().getName());
            //放入柜台
            counter.input(milkyTea);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
3、消费者代码
public class Consume implements Runnable{
    private Counter counter;

    public Consume(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        //一个人买20杯奶茶
        for (int i = 0; i < 20; i++) {
            counter.output();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
4、奶茶代码
public class MilkyTea {
    private int id;
    //生产者的名字
    private String produceName;

    public MilkyTea() {
    }

    public MilkyTea(int id, String produceName) {
        this.id = id;
        this.produceName = produceName;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getProduceName() {
        return produceName;
    }

    public void setProduceName(String produceName) {
        this.produceName = produceName;
    }

    @Override
    public String toString() {
        return "MilkyTea{" +
                "id=" + id +
                ", produceName='" + produceName + ''' +
                '}';
    }
}
5、存放奶茶的柜台代码
public class Counter {
    //存放奶茶的数组
    private MilkyTea[] cons = new MilkyTea[10];
    //奶茶的个数
    private int index = 0;

    //生产者放奶茶
    public synchronized void input(MilkyTea milkyTea){
        //判断柜台是否存满,存满了则不生产
        while(index == cons.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //把奶茶存放到数组中
        cons[index++] = milkyTea;
        System.out.println(Thread.currentThread().getName()+"生产了奶茶,编号为:"+milkyTea.getId());
        System.out.println("柜台的奶茶数为:"+index);
        //唤醒消费者
        this.notifyAll();
    }

    //消费者取奶茶
    public synchronized void output(){
        //判断柜台是否空了,如果空了则不取
        while(index == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //获取奶茶
        MilkyTea milkyTea = cons[--index];
        System.out.println(Thread.currentThread().getName()+"拿了"+milkyTea.getProduceName()+"生产的奶茶");
        cons[index] = null;
        //唤醒生产者
        this.notifyAll();
    }
}
6、运行的主函数代码
**
 * @author Ran
 * @since JDK 1.8
 *
 *          生产者消费者模式
 */
public class Demo01 {
    public static void main(String[] args) {
        //柜台
        Counter counter = new Counter();
        //生产者
        Produce produce = new Produce(counter);
        //消费者
        Consume consume = new Consume(counter);
        //创建生产者的线程
        new Thread(produce,"售卖员").start();
        //创建消费者的线程
        new Thread(consume,"----消费者1").start();
        new Thread(consume,"----消费者2").start();
        new Thread(consume,"----消费者3").start();
        new Thread(consume,"----消费者4").start();
        new Thread(consume,"----消费者5").start();
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/582866.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号