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、生产者代码
/**
* @author Ran
* @since JDK 1.8
*
* 生产者
*/
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、消费者代码
/**
* @author Ran
* @since JDK 1.8
*
* 消费者
*/
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、奶茶代码
/**
* @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;
}
@Override
public 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();
}
}