当前位置: 首页 > news >正文

全球搜 建设网站wordpress 第一张图片

全球搜 建设网站,wordpress 第一张图片,网站可以在外地备案吗,深圳罗湖区网站建设文章目录 多线程案例1、单例模式1#xff09;饿汉模式2#xff09;懒汉模式3#xff09;线程安全吗#xff1f;#xff1f;4#xff09;解决懒汉模式线程安全问题5#xff09;解决懒汉模式内存可见性问题 2、阻塞队列1) 阻塞队列是什么#xff1f;2) 生产者消费者模型1… 文章目录 多线程案例1、单例模式1饿汉模式2懒汉模式3线程安全吗4解决懒汉模式线程安全问题5解决懒汉模式内存可见性问题 2、阻塞队列1) 阻塞队列是什么2) 生产者消费者模型1.生产者消费者模型的优势2.标准库中的阻塞队列 3) 拟实现阻塞队列 3、定时器1) 标准库中的定时器2) 模拟实现定时器 4、线程池1) 工厂模式2) 标准库中的线程池1.ThreadPoolExecutor类 3) 模拟实现线程池 多线程案例 1、单例模式 单例模式是校招中最常考的设计模式之一。 啥是设计模式? 设计模式好比象棋中的 “棋谱”. 红方当头炮, 黑方马来跳. 针对红方的一些走法, 黑方应招的时候有 一些固定的套路. 按照套路来走局势就不会吃亏. 软件开发中也有很多常见的 “问题场景”. 针对这些问题场景, 大佬们总结出了一些固定的套路. 按照 这个套路来实现代码, 也不会吃亏. 应用场景有时候希望对象在程序中只有一个实例对象也就是说只能new一次。由此就出现了单例模式。 单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例. 单例模式具体的实现方法分成“懒汉”和“饿汉”两种。 1饿汉模式 含义当前代码中是比较迫切的在类加载的时候就立刻把实例给创建出来。 class Singleton {private static Singleton instance new Singleton();private Singleton(){}public static Singleton getInstance() {return instance;} } public class Demo3 {public static void main(String[] args) {Singleton s1 Singleton.getInstance();Singleton s2 Singleton.getInstance();//Singleton s3 new Singleton(); 无法创建出来由于上面的构造方法是private的System.out.println(s1 s2);//System.out.println(s1 s3);} }上述代码的执行时机是 Singleton 类被 jvm 加载的时候。Singleton类会在 JVM 第一次使用的时候被加载。也不一定是在程序一启动的时候. 用static修饰的雷属性由于每个类的类对象是单例的。类对象的属性(static)也就是单例的了。 单例模式中对我们的代码做出一个限制禁止别人去new这个实例也就是把构造方法改成private。 此时我们要是去new这个实例就会报错。 这时我们能够使用的实例有且只有一个就是类字被加载出来的同时会进行实例化。 2懒汉模式 含义在真正用到的时候来进行创建实例不用到则不会产生其他开销。更推荐使用 class SingletonLazy {private static SingletonLazy instance null;private SingletonLazy() {}public static SingletonLazy getInstance() {if(instance null) {instance new SingletonLazy();}return instance;} } public class Demo4 {public static void main(String[] args) {SingletonLazy s1 SingletonLazy.getInstance();SingletonLazy s2 SingletonLazy.getInstance();System.out.println(s1 s2);} }懒汉模式与饿汉模式极其相似只是在实例化的时机上有所不同。 3线程安全吗 接下来我们要去考虑 懒汉模式 和 恶汉模式 在多线程模式下会不会出问题呢在多线程调用 getInstacne 的情况下哪个模式是线程安全的没有Bug 对于饿汉模式多个线程同时读取同一个变量就不会出现线程安全问题。 对于懒汉模式则会出现线程安全问题。 简单理解当我们按照顺序来执行就会发现此时创建出两个对象程序不再是单例模式了 深入解读第二次 new 操作就把原来 instacne 的引用给修改了之前 new 出来的对象就立刻被 gc 回收了最后只剩下一个对象。 在通常印象中new 一个对象并不是一个很复杂的过程但实际上 new 一个对象开销是非常大的如果服务器一启动就需要加载 100G 的数据存到内存中都是通过一个对象来管理那我们 new 这个对象时就需要消耗 100G 的内存花费很长时间。 4解决懒汉模式线程安全问题 提到如何解决线程安全问题想到的就是加锁以及内存可见性问题。 先考虑一种加锁方式 这种写法是错误的没有解决上述线程安全问题。因为这个操作本身就是原子的再加锁也起不到实质上的作用。 正确加锁方式是把锁加到外面 如图所示此时再执行线程2的时候就能发现 instance 是一个非 null 的值。 上述代码还会出现一个问题程序中频繁的出现加锁操作。 加锁是一个成本很高的操作加锁可能会引起阻塞等待。因此得到加锁的基本原则就是非必要不加锁。不能无脑加锁频繁加锁会影响程序执行效率。 思考后续每次调用getInstance都要加锁了是必要的嘛 不是的。每次调用都会加锁。实际上只有第一次调用有必要加锁后面都不需要。这里把不该加锁的地方给加锁了就会很影响程序的效率。 懒汉模式线程不安全主要是在首次 new 对象的时候才存在问题。一旦把对象new好了之后后续再调用getInstance都没事了 上述代码在首次调用和后续调用都加锁了因此降低了程序的执行效率。 对代码进行调整先判定是否要加锁再决定是不是真的加锁。 public static SingletonLazy getInstance() {if (instacne null) {synchronized (SingletonLazy.class){if(instance null) {instance new SingletonLazy();}}}return instance; }可以看到在外层又加了一个判断条件。第一个条件是为了判断是否要加锁第二个条件是判断是否要创建对象。 同一个条件写了两遍这个合理嘛 合理的不得了。 如果在单线程中两个同样的判定结果一定是一样的。但对于多线程来说可能会涉及到阻塞。阻塞多久并不知道第二个条件和第一个条件之间可能会间隔非常长的时间沧海桑田在这个很长的时间间隔里可能别的线程就把 instance 给改了。 5解决懒汉模式内存可见性问题 public static SingletonLazy getInstance() {if (instance null) {synchronized (SingletonLazy.class) {if (instance null) {instance new SingletonLazy();}}}return instance; }在多线程模式中第一个线程在修改完 instance 之后就结束了代码块释放了锁。第二个线程就能够获取到锁了从阻塞中恢复了。 问题出现了第二个线程这里进行的“读操作”一定能读取到第一个线程修改之后的值吗 答案是不一定可能会出现内存可见性问题。这里只是分析了一下可能存在这样的风险。编译器实际上在这个场景中是否会触发优化打上问号。因此给 instance 加上一个 volatile 是更为稳妥的做法。 加上 volatile 还有另外一个用途就是避免此处赋值操作的指令重排序 指令重排序也是编译器优化的一种手段是保证原有执行逻辑不变的前提下对代码执行顺序进行调整。使调整之后的执行效率提高。 单线程中这样的重排序一般没事。但是多线程下就会出现问题。 instance new SingletonLazy()像上面这句代码一句代码在编译器中由三句指令构成。 给对象创建出内存空间得到内存地址。在空间上调用构造方法对对象进行初始化。把内存地址赋值给 instance 引用。 给 instacne 加上 volatile 之后此时针对 instance 进行的赋值操作就不会产生上述的指令重排序了。必须按照 1 2 3 的顺序执行不会出现 1 3 2 的执行顺序。 2、阻塞队列 1) 阻塞队列是什么 阻塞队列是一种特殊的队列带有阻塞功能。也遵守 “先进先出” 的原则。 阻塞队列能是一种线程安全的数据结构并且具有以下特性 当队列满的时候 继续入队列就会阻塞 直到有其他线程从队列中取走元素。当队列空的时候,继续出队列也会阻塞直到有其他线程往队列中插入元素。 阻塞队列的一个典型应用场景就是 “生产者消费者模型”。这是一种非常典型的开发模型。 2) 生产者消费者模型 在该模型中生产者负责生产数据并将其放入一个缓冲区中。消费者负责从缓冲区中取出数据并进行消费。 本质上来说就是通过一个容器来解决生产者和消费者的强耦合问题。 生产者和消费者彼此之间不直接通讯而通过阻塞队列来进行通讯所以生产者生产完数据之后不用等待消费者处理直接扔给阻塞队列消费者不找生产者要数据而是直接从阻塞队列里取。 1.生产者消费者模型的优势 减少任务切换开销减少锁竞争。 阻塞队列会使生产者和消费者之间 解耦合。 平衡了生产者和消费者的处理能力。(削峰填谷) 阻塞队列就相当于一个缓冲区平衡了生产者和消费者的处理能力. 服务器收到的来自于客户端/用户的请求不是一成不变的。可能会因为一些突发事件引起请求数目暴增~~ 一台服务器同一时刻能处理的请求数量是有上限不同的服务器承担的上限是不一样的。 在一个分布式系统中就经常会出现有的机器能承担的压力更大有的则更小~~ 正因为生产者消费者模型这么重要虽然阻塞队列只是一个数据结构但我们会把这个数据结构(阻塞队列)单独实现成一个服务器程序并且使用单独的主机/主机集群来部署。 此时这个所谓的则塞队列就进化成了“消息队列”。 2.标准库中的阻塞队列 java标准库中已经提供了现成的阻塞队列的实现了有基于链表、堆、优先级、数组实现的。 Array 这个版本的速度要更快前提是得先知道最多有多少元素。对于 Array 版本来说频繁扩容是一个不小的开销 如果不知道有多少元素使用 Linked 更合适。 对于BlockfingQueue来说offer 和 poll 不带有阻塞功能而 put 和 take 带有阻塞功能。 3) 拟实现阻塞队列 这里我们基于数组循环队列来简单实现一个阻塞队列。 循环队列就像是把数组首尾连接起来组成一个环状以此提高使用效率。 下面简单实现阻塞队列的两个主要功能put和take。 put入队列。take出队列。 阻塞队列的构成 循环队列 用 size 来标记队列长度可以判断队列是否为满。实现循环效果头(head) 尾(hail) 相连。 实现阻塞 入队列时。当队列满的时候再进行 put 就会产生阻塞。出队列时。当队列空的时候再进行 take 也会产生阻塞。 写完代码需要考虑线程安全问题。内存可见性、加锁 内存可见性加锁 wait 搭配 while 循环使用 原因 意外被 interrupted 唤醒。多线程下出现线程安全问题。 下面是实现代码 class MyBlockingQueue {private String[] items new String[1000];volatile private int head 0;volatile private int tail 0;volatile private int size 0;public void put (String elem) throws InterruptedException {synchronized (this) {while (size items.length) {//队列满开始阻塞等待。System.out.println(队列满开始阻塞等待);this.wait();//return;}items[tail] elem;tail ;if (tail items.length) {tail 0;}size ;this.notify();}}public String take () throws InterruptedException {synchronized (this) {//判断队列是否为空若为空则不能出队列。while (size 0) {//队列空开始阻塞等待System.out.println(队列空开始阻塞等待);this.wait();//return null;}String elem items[head];head ;if (head items.length) {head 0;}size --;//使用 notify 来唤醒队列满的阻塞情况。this.notify();return elem;}} }public class Demo1 {public static void main(String[] args) throws InterruptedException {MyBlockingQueue queue new MyBlockingQueue();//生产者Thread t1 new Thread(() -{int count 0;while (true) {try {queue.put(count );System.out.println(生产元素 count);count ;//Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});//消费者Thread t2 new Thread(() - {while (true) {try {String count queue.take();System.out.println(消费元素 count);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();t2.start();} }/*结果 先生产1000个元素阻塞等待消费元素。 之后消费一个元素即生产一个元素。 */3、定时器 什么是定时器 ⏰ 定时器也是软件开发中的一个重要组件。类似于一个 “闹钟”。达到一个设定的时间之后就执行某个指定好的代码。 1) 标准库中的定时器 标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .schedule 包含两个参数。第一个参数 (TimerTask) 指定即将要执行的任务代码 第二个参数 (delay) 指定多长时间之后执行 (单位为毫秒). Timer timer new Timer(); timer.schedule(new TimerTask() {Overridepublic void run() {System.out.println(定是任务执行);} }, 3000);System.out.println(程序开始运行);当我们运行代码就会发现打印完 hello 程序并没有结束而是在持续执行。 原因就在于Timer 内部有自己的线程。为了保证随时可以处理新安排的任务这个线程会持续执行。并且这个线程还是个前台线程。 上述代码中可以看到 schedule 有两个参数 其中的一个参数 TimeTask 类似于 Runnable。在那里会记录时间以及执行什么样的任务。 下图中的源代码 TimerTask 也实现了 Runnable 接口。 2) 模拟实现定时器 一个定时器里,是可以有很多个任务的!! 先要能够把一个任务给描述出来再使用数据结构把多个任务组织起来。 定时器的构成思路 创建一个TimerTask这样类表示一个任务。这个任务就需要包含两方面任务的内容任务的实际执行时间。 使用一定的数据结构把多个 TimerTask 给组织起来。 如果使用 List (数组,链表)组织 TimerTask 的话。在任务特别多的情况下如何确定哪个任务何时能够执行呢这样就需要搞一个线程不停的对上述的List进行遍历。看这里的每个元素是否到了时间时间到就执行时间不到就跳过扫描下一个。 这个思路并不科学如果这些任务的时间都还为时尚早。在时间到达之前此处的扫描线程就需要一刻不停的反复扫描。 这里可以使用优先级队列来组织所有的任务。因为队首元素就是时间最小的任务。 搞一个扫描线程负责监控队首元素的任务是否时间到如果到时间了就执行 run 方法来完成任务。 注意问题 代码中所使用的优先级队列不是线程安全的在此需要针对 queue 的操作进行加锁。 扫描线程中阻塞等待直接使用 sleep 进行休眠是否合适呢 非常不合适的 sleep 进入阻塞后不会释放锁。影响其他线程去执行这里的 schedule.sleep 在休眠的过程中不方便提前中断。(虽然可以使用 interrupt 来中断。但是 interrupt 意味着线程应该要结束了)这里可以使用 wait 和 notify 来阻塞和唤醒程序。 当队列为空时需要阻塞等待。当确定了最早需要执行任务时也需要阻塞等待等待任务执行当 queue 中添加新任务时就需要唤醒正在阻塞等待的程序来重新进行判定哪个是最新的任务。 随便写一个类它的对象都能放到优先级队列中嘛有没有什么特别的要求 要求放到优先级队列中的元素是“可比较的。 通过 Comparable 或者 Comparator 定义任务之间的比较规则。此处我们在 MyTimerTask 这里让他实现 Comparable。 下面是实现代码 import java.util.PriorityQueue;//模拟实现定时器 class MyTimerTask implements ComparableMyTimerTask{//执行任务时间private long time;//执行任务内容private Runnable runnable;public MyTimerTask(Runnable runnable, long delay) {time System.currentTimeMillis() delay;this.runnable runnable;}public long getTime() {return time;}public Runnable getRunnable() {return runnable;}Overridepublic int compareTo(MyTimerTask o) {return (int)(o.time - this.time);} }class MyTimer {//使用优先级队列private PriorityQueueMyTimerTask queue new PriorityQueue();//定义锁对象private Object locker new Object();public void schedule (Runnable runnable, long delay) {synchronized (locker) {MyTimerTask task new MyTimerTask(runnable, delay);queue.offer(task);locker.notify();}}public MyTimer() {Thread t new Thread(() - {while (true) {synchronized (locker) {try {if (queue.isEmpty()) {locker.wait();}long curTime System.currentTimeMillis();MyTimerTask task queue.peek();if (curTime task.getTime()) {queue.poll();task.getRunnable().run();} else {locker.wait(task.getTime() - curTime);}} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();} } public class Demo2 {public static void main(String[] args) {MyTimer myTimer new MyTimer();myTimer.schedule(new Runnable() {Overridepublic void run() {System.out.println(hello 3);}},3000);myTimer.schedule(new Runnable() {Overridepublic void run() {System.out.println(hello 2);}},2000);myTimer.schedule(new Runnable() {Overridepublic void run() {System.out.println(hello 1);}},1000);System.out.println(开始执行!!!);} }4、线程池 池(Poll) 是一个非常重要的思想方法。包括内存池、进程池、连接池、常量池……这些“池”概念上都是一样的。 线程池可以管理和重用多个线程以提高应用程序的性能和资源利用率。线程池维护了一个线程队列当需要执行任务时可以从线程池中获取一个空闲线程来执行任务而不需要每次都创建新的线程。这样可以减少线程创建和销毁的开销并且可以限制并发线程的数量避免资源耗尽。 为啥从池子里取就比从系统这里创建线程更快更高效呢? 如果是从系统这里创建线程需要调用系统api。由操作系统内核来完成线程的创建过程。这里的内核是给所有的进程来提供服务的除了创建线程这个任务外还有其他任务。因此效率低下不可控的如果是从线程池这里获取线程上述的内核中进行的操作都提前做好了现在的取线程的过程纯粹的用户代码完成纯用户态可控的 因此线程池最大的好处就是减少每次启动线程、销毁线程的损耗。 1) 工厂模式 什么是工厂模式 工厂模式是一种创建对象的设计模式它通过将对象的创建委托给一个工厂类来解决对象创建的问题。工厂模式提供了一种灵活的方式来创建对象使得客户端不需要使用new关键字来创建对象而是通过调用工厂类的方法来获得所需的对象。 说完概念后再来简单描述一下工厂模式。 一般创建对象都是通过 new 通过构造方法。但是构造方法存在重大缺陷此时就可以使用工厂模式来解决问题了。 使用工厂模式来解决上述无法重载问题。不使用构造方法了。而是使用普通的方法来构造对象。这样的方法名字就可以是任意的了。普通方法内部再去 new 对象。由于普通方法目的是为了创建出对象来因此这样的方法一般得是静态的。 //工厂模式 class Point {public static Point makePointXY(double x, double y) { //工厂方法return new Point();}public static Point makePointRA(double r, double a) { //工厂方法return new Point();} } public class Demo1 {public static void main(String[] args) {Point point Point.makePointXY(5, 6);Point point Point.makePointAR(7, 8);} }我们再来看一看下面源代码中是如何运用工厂模式的。 在使用的时候就直接去调用方法即可。 Executors被称为“工厂类“。 newFixedThreadPool被称为“工厂方法”。 2) 标准库中的线程池 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.返回值类型为 ExecutorService.线程池对象创建好后可以通过 submit方法把任务添加到线程池中. ExecutorService executorService Executors.newFixedThreadPool(4);for (int i 0; i 1000; i) {executorService.submit(new Runnable() {Overridepublic void run() {System.out.println(hello);}}); }MethodsDescriptionnewCachedThreadPool()创建出一个线程数目动态变化的线程池.newFixedThreadPool(int nThreads)创建一个固定线程数量的线程池.newScheduledThreadPool(int corePoolSize)类似于定时器在后续的某个时刻再执行。newSingleThreadExecutor()包含单个线程比原生创建的线程 api 更简单一点 1.ThreadPoolExecutor类 除了上述这些线程池之外标准库还提供了一个接口更丰富的线程池类。 为了方便使用将上述的几个方法进一步封装形成一个新的 ThreadPoolExecutor 类以此更好的满足实际的需求。 我们可以在官方文档中查看 ThreadPoolExecutor 类在 java.util.concurrent 这个包中 谈谈Java标准库里的线程池构造方法的参数和含义。 当线程池无法接受新任务时会调用一下方式来拒绝执行任务。 第一个拒绝策略 ThreadPoolExecutor.AbortPolicy 默认的拒绝策略会抛出RejectedExecutionException异常 第二个拒绝策略 ThreadPoolExecutor.CallerRunsPolicy 会将任务回退给调用者线程来执行 第三个拒绝策略 ThreadPoolExecutor.DiscardOldestPolicy 会丢弃最早加入队列的任务然后尝试重新添加新任务 第四个拒绝策略 ThreadPoolExecutor.DiscardPolicy 会直接丢弃该任务 上述谈到的线程池 一组线程池是封装过的Executors一组线程池ThreadPoolExecutor 原生的 二者用哪个都可以主要还是看实际需求。 3) 模拟实现线程池 线程池中所用到的数据结构还是阻塞队列。 实现其主要功能 submit. 下面是代码实现 import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque;//简单模拟实现 线程池 class MyPollThread {static BlockingDequeRunnable queue new LinkedBlockingDeque();public void submit (Runnable runnable) throws InterruptedException {queue.put(runnable);}public static MyPollThread newMyPollThread(int n) {for (int i 0; i n; i) {Thread t new Thread(()- {while (true) {try {Runnable runnable queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}return new MyPollThread();} }public class Demo2 {public static void main(String[] args) throws InterruptedException {MyPollThread myPollThread MyPollThread.newMyPollThread(4);for (int i 0; i 1000; i) {myPollThread.submit(new Runnable() {Overridepublic void run() {System.out.println(Thread.currentThread().getName() hello);}});}} }关于线程池还有一个问题在创建线程池的时候线程个数是咋来的呢 不同的项目中线程要做的工作是不一样的~~ 有的线程的工作是CPU密集型线程的工作全是运算 大部分工作都是要在CPU上完成的. CPU得给他安排核心去完成工作才可以有进展~~ 如果CPU是N个核心当你线程数量也是N的时候 理想情况下每个核心上一个线程. 如果搞很多的线程线程也就是在排队等待不会有新的进展. 有的线程的工作是IO密集型读写文件等待用户输入网络通信 涉及到大量的等待时间。等的过程中没有使用 cpu 这样的线程就算更多一些也不会给CPU造成太大的负担。 比如CPU是16个核心写32个线程。 由于是IO密集的这里的大部分线程都在等都不消耗CPU反而CPU的占用情况还很低~ 实际开发中一个线程往往是一部分工作是cpu密集的一部分工作是io密集的。此时一个线程几成是在cpu上运行几成是在等待io这说不好 这里更好的做法是通过实验的方式来找到合适的线程数。通过性能测试尝试不同的线程数目。实验过程中找到性能和系统资源开销比较均衡的数值。
http://www.zqtcl.cn/news/477645/

相关文章:

  • 口碑好的常州网站优化深圳市光明区实验学校
  • 网站怎么做微博认证网页设计代码html作品展示
  • 在线网站建设活动初创企业的建站流程
  • 汨罗哪里有网站开发的公司电话百度首页关键词推广
  • 天津百度整站优化服务政务网站模版
  • 推荐家居企业网站建设用什么工具修改wordpress
  • wix做的网站能扒下来哈尔滨做网站的oeminc
  • 做网站的网络公司门户网站介绍
  • 软件公司网站系统集成建设下拉关键词排名
  • 景翔物流网站建设公司企业网站优化排名
  • 余姚做网站62752762素材网站建设需要多少费用
  • dede网站后台导入文档许昌网站开发哪家好
  • 网站建设宣传psdwordpress 链接关系
  • 宁波网站建设免费咨询深圳做棋牌网站建设找哪家公司好
  • 安阳门户网站html5网页代码
  • 企业建站系统下载广州有几个区几个县级市
  • 时装网站建设的背景广州建设专业网站
  • 来年做那些网站能致富网站建设优化文档
  • 好看的商城网站企业所得税分录
  • 网站建设你懂的网站提高内容的丰富度创意
  • 菏泽做网站设计做一个简单的网站需要多少钱
  • html企业网站怎么做免费crm平台
  • 婚庆公司网站制作如何自己制作首页网站
  • ic外贸网站建设龙岩app制作
  • 长沙企业建网站绵阳市三台县城乡建设局网站
  • 常宁市城市建设规划管理局网站织梦网做网站步骤
  • 对网站开发实训的建议怎么把产品推广到各大平台
  • wap网站是什么意思啊网站建设网站模板
  • 湛江市建设局网站天津网站建设渠道
  • 做图专业软件下载网站深圳营销型网站哪家好