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

第六天第七天

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

第六天第七天

Author:老九
计算机专业
可控之事 沉重冷静 不可控之事 乐观面对
85180586@qq.com
         ☺️  

      ❓  ❤️ ☕️       ❗️ 
————————————————
版权声明:本文为CSDN博主「浦上青天」的原创文章

文章目录

第五天 第六天复习

线程安全问题阻塞队列定时器

定时器构成

构造方法快捷键 线程池

核心操作 线程总结
知识点

第五天 第六天复习

单例模式:某个类,不应该有多个实例,此时就可以使用单例模式
实现方式:1.懒汉模式2.饿汉模式
饿汉模式:在类加载的时候立刻实例化对象(实例化时机早)
懒汉模式:首次调用getInstance的时候再实例化对象(效率更高)

线程安全问题

1.饿汉模式,是线程安全的,多线程调用getInstance只涉及读操作
2.懒汉模式,在实例化对象之前是线程不安全的(涉及到多线程修改),一旦实例化之后,又是线程安全的
懒汉模式改进方式:
1.加锁(读取判断和new实例操作是原子的)
2.双重if(避免在实例化之后,再去调用getInstance频繁触发不必要的加锁操作)
3.volatile(在instance实例之前加)

阻塞队列

可以用来实现生产者消费者模型
阻塞队列是一个先进先出的队列
出队列的时候,如果发现队列空了,也会阻塞,直到有其他线程调用入队列操作,让队列中有元素,才能继续出队列
入队列的时候如果发现队列满了,就会阻塞,直到有其他线程调用出队列操作让队列中有空位的时候,才能继续入队列

public class ThreadDemo24 {
    static class BlockingQueue {
        private int[] array = new int[1000];
        private int head = 0;
        private int tail = 0;
        //head 和 tail 构成一个前闭后开区间
        //区分空还是满
        private int size = 0;

        //阻塞版本入队列
        public void put(int value) throws InterruptedException {
            synchronized (this) {
                if (size == array.length) {
                    wait();
                }

                array[tail] = value;
                tail++;
                if (tail == array.length) {
                    tail = 0;
                }
                size++;
                notify();
            }
        }

        public int take() throws InterruptedException {
            int ret = -1;
            synchronized (this) {
                if (size == 0) {
                    wait();
                }
                ret = array[head];
                head++;
                if (head == array.length) {
                    head = 0;
                }
                size--;
                notify();
            }
            return ret;
        }
    }

}

这两个wait不可能同时被调用

定时器

多线程编程中一个重要/常用组件
好比一个闹钟,有些逻辑,并不想立刻执行,而是等一定的时间之后,再来执行
例如,用户服务器,浏览器内部,淘宝服务器都有定时器,就跟闹钟是一样的,如果定时器时间到了,就不等了

定时器构成

1.使用一个类Task来描述“一段逻辑”(一个要执行的任务),同时也要记录这个任务在啥时候执行
阻塞优先队列:既支持阻塞的特性,又支持按优先级“先进先出”
本质上是一个“堆”
2.使用一个阻塞优先队列来组织若干个Task
3.还需要一个扫描线程,扫描线程要循环的检测队首元素需要执行,如果需要执行的话,就执行这个任务
java.util.concurrent 简称juc,包含很多并发编程的包
wait()死等,一直等到notify的通知过来
wait(time),等待是有上限,如果有notify就被提前唤醒,或者跟sleep一样

import javafx.concurrent.Worker;

import java.sql.Time;
import java.util.concurrent.PriorityBlockingQueue;


public class ThreadDemo25 {
    //优先队列中的元素必须是可比较的
    //1.让Task 实现Comparable接口
    //2.让优先队列构造的时候,传入一个比较器对象(Comparator)
    static class Task implements Comparable {
        //Runnable 中有一个run方法,就可以借助这个run方法,来描述要执行的具体的任务
        private Runnable command;
        //time表示啥时候来执行command,是一个绝对时间
        private long time;

        //构造方法的参数表示,多少毫秒之后执行,(相对时间)
        public Task(Runnable command, long after) {
            this.command = command;
            this.time = System.currentTimeMillis() + after;
        }

        //执行任务的具体逻辑
        public void run() {
            command.run();
        }

        @Override
        public int compareTo(Task o) {
            return (int) (this.time - o.time);
        }
    }

    static class Worker extends Thread {
        private PriorityBlockingQueue queue = null;
        private Object mailBox = null;

        public Worker(PriorityBlockingQueue queue, Object mailBox) {
            this.queue = queue;
            this.mailBox = mailBox;
        }

        @Override
        public void run() {
            while (true) {
                //1.取出队首元素,检查时间是否到了
                try {
                    Task task = queue.take();
                    long curTime = System.currentTimeMillis();
                    if (task.time > curTime) {
                        //时间还没到
                        queue.put(task);
                        synchronized (mailBox)
                        {
                            mailBox.wait(task.time-curTime);
                        }
                    } else {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        }
    }


    static class Timer {
        //为了避免忙等,需要使用wait方法
        //使用一个单独的对象来辅助进行wait
        //用this也可以
        private Object mailBox = new Object();


        //定时器的基本构成,有三个部分
        //1.用一个类来描述 “任务”
        //2.使用阻塞优先队列来组织若干个任务,让队首元素就是时间最早的任务,
        //如果队首元素时间未到,那么其他元素也肯定不能执行
        private PriorityBlockingQueue queue = new PriorityBlockingQueue<>();

        //3.用一个线程循环扫描当前的阻塞队列的队首元素,如果时间到
        //就执行指定的任务
        public Timer() {
            //创建线程
            Worker worker = new Worker(queue,mailBox);
            worker.start();
        }

        //4.还需要提供一个方法,让调用者能把任务给“安排”进来
        public void schedule(Runnable command, long after) {
            Task task = new Task(command, after);
            queue.put(task);
            synchronized (mailBox)
            {
                mailBox.notify();
            }


        }

        public static void main(String[] args) {
            Timer timer = new Timer();
            timer.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hehe");
                     timer.schedule(this, 2000);
                }
            }, 2000);

        }

    }
}


构造方法快捷键

ALT+INSERT

线程池

已经包含了一些线程,让我们直接去使用
避免了频繁创建/销毁线程的开销

核心操作

execute:把一个任务加到线程池中
shutdown:销毁线程池中的所有线程

线程池的组成部分
1.先有一个类,表示 工作线程
2.还得有一个类,来描述具体线程要做的工作是啥(借助runnable就可以表示
3.还需要有一个数据结构来组织若干个任务,BlockingQueue)
4.还需要一个数据结构,来组织若干个线程List

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.linkedBlockingQueue;


public class ThreadDemo26 {
    //使用这个类来描述当前的工作线程是啥样的
    static class Worker extends Thread
    {
        private int id = 0;

        private BlockingQueue queue = null;

        public Worker(BlockingQueue queue,int id) {
            this.queue = queue;
            this.id = id;
        }

        @Override
        public void run() {
            try{
                while(!Thread.currentThread().isInterrupted())
                {
                    Runnable command = queue.take();
                    System.out.println("thread"+id+"running...");
                    command.run();
                }
            } catch (InterruptedException e) {
                System.out.println("线程被终止");

            }
        }
    }
    static class MyThreadPool
    {
        //这个阻塞队列用于组织若干个任务
        private BlockingQueue queue = new linkedBlockingQueue<>();
        //这个List用来组织若干个工作线程
        private List workers = new ArrayList<>();
        //一个线程池内部应该有多少个线程,需要根据实际情况确定
        private static final int maxWorkerCount  = 10;

        //实现execute方法 和 shutdown方法
        public void execute(Runnable command) throws InterruptedException {
            //也是使用延时加载的方式创建线程
            //当线程池中线程数目比较少,就新创建线程来作为工作线程
            //如果线程数目已经比较多了,就不用新建线程了
            if(workers.size() 
线程总结 

1.进程和线程的基本概念和区别
2.线程控制
a线程创建
b线程终止
c线程等待
d获取线程实例
e线程休眠
3.线程状态
4.线程安全【重中之重】
基本概念:多线程执行某个逻辑出现了逻辑错误
出现的原因:
1.抢占式执行(万恶之源)
2.修改操作不是原子的
3.多线程修改同一个变量
4.内存可见性(volatile,一个线程读,一个线程写,其中读操作被优化成直接取CPU寄存器数据,当写线程进行修改的时候,读线程无法获取到最新的值)
5.指令重排序
解决方案:
最主要的方案:
加锁(原子性)
对象等待集(避免出现线程饿死问题)
wait本质上就是当前线程对应的PCB移动到阻塞队列中,直到notify唤醒的时候再把PCB移动回来
wait内部做了三件事:
1.释放锁
2.等待通知
3.收到通知后尝试重新获取锁

先赞后看,养成习惯!!!^ _ ^♥♥♥
每天都更新知识点哦!!!
码字不易,大家的支持就是我坚持下去的动力。点赞后不要忘记关注我哦!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/727630.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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