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{@Overridepublic 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;}@Overridepublic 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、生产者代码

/*** @author Ran* @since JDK 1.8**      生产者*/
public class Produce implements Runnable{private Counter counter;public Produce(Counter counter) {this.counter = counter;}@Overridepublic 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、消费者代码

/*** @author Ran* @since JDK 1.8**          消费者*/
public class Consume implements Runnable{private Counter counter;public Consume(Counter counter) {this.counter = counter;}@Overridepublic void run() {//一个人买20杯奶茶for (int i = 0; i < 20; i++) {counter.output();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

4、奶茶代码

/*** @author Ran* @since JDK 1.8**      奶茶*/
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;}@Overridepublic String toString() {return "MilkyTea{" +"id=" + id +", produceName='" + produceName + '\'' +'}';}
}

5、存放奶茶的柜台代码

/*** @author Ran* @since JDK 1.8*          存放奶茶的柜台*/
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();}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部