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

企业网站建设顾问网站美工做专题尺寸多少

企业网站建设顾问,网站美工做专题尺寸多少,怎么做淘宝客网站,电子商务网店运营一、并发问题 上下文切换#xff1a;CPU通过时间片分配算法来循环执行任务#xff0c;当前任务执行一个时间片后会切换到下一个任务。在切换前会保存上一个任务的状态#xff0c;以便下次切换回这个任务时#xff0c;可以再加载这个任务的状态。所以任务从保存到再加载的过…一、并发问题 上下文切换CPU通过时间片分配算法来循环执行任务当前任务执行一个时间片后会切换到下一个任务。在切换前会保存上一个任务的状态以便下次切换回这个任务时可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。 多线程不一定快多线程有线程创建和上下文切换的开销。减少上下文切换 无锁并发多线程竞争锁时会引起上下文切换所以多线程处理数据时可以用一些办法来避免使用锁如将数据的ID按照Hash算法取模分段不同的线程处理不同段的数据CAS算法使用最少线程避免创建不需要的线程比如任务很少但是创建了很多线程来处理这样会造成大量线程都处于等待状态使用协程在单线程里实现多任务调度并在单线程里维持多个任务间的切换死锁线程A持有资源线程B持有资源他们都想申请对方的资源这两个线程就会相互等待而进入死锁状态。互相等待对方释放锁 代码 public class Test {public static void main(String[] args) {Object o1 new Object();Object o2 new Object();new Thread(() -{synchronized (o1) {try {System.out.println(get o1);Thread.sleep(1000);} catch (Exception ignored){}synchronized (o2) {System.out.println(get o2);}}}).start();new Thread(() -{synchronized (o2) {try {System.out.println(get o2);Thread.sleep(1000);} catch (Exception ignored){}synchronized (o1) {System.out.println(get o1);}}}).start();} } 避免死锁的办法 避免一个 线 程同 时获 取多个锁 避免一个 线 程在 锁 内同 时 占用多个 资 源尽量保 证 每个 锁 只占用一个 资 源 尝试 使用定 时锁 使用 lock.tryLock timeout 来替代使用内部 锁 机制 对 于数据 库锁 加 锁 和解 锁 必 须 在一个数据 库连 接里否 则 会出 现 解 锁 失 败 资源限制 程序的执行速度受限于计算机硬件或者是软件资源。问题导致并发执行变为串行执行开启并发线程速度可能会很慢。因为上下文切换占用了大量的时间。 二、java并发底层实现 Java 代 码 在 编译 后会 变 成 Java 字 节码 字 节码 被 类 加 载 器加 载 到 JVM 里 JVM 执 行字节码 最 终 需要 转 化 为汇编 指令在 CPU 上 执 行 Java 中所使用的并 发 机制依 赖 于 JVM 的 实现和CPU 的指令。 2.1、volatile volatile是轻量级的synchronized保证多处理器开发的共享变量的可见性。可见性的意思是一个线程修改共享变量的时候其它线程可以读到这个值。volatile的成本比synchronized成本更低不会引起线程的上下文切换和调度。 1、volatile的定义与实现原理 如果一个字段被声明为volatile那么java线程的内存模型确保所有线程看到这个变量的值是一致的。volatile相关的CPU术语介绍在下面。 2、volatile如何保证可见性 当一个线程修改了一个volatile变量的值会将这个值刷新到主内存中并且会使用某种机制通知其他线程该变量发生变化。其他线程需要读取这个变量时则会去主内存中读取最新的值。具体机制volatile共享变量进行写操作的时候多出lock前缀的汇编 lock前缀的指令在多核处理器引发的事件 把当前的处理器缓存行的数据写到系统的内存 这个写回内存的操作会使其它CPU里面缓存的该地址的数据无效。 即使写回了内存其它的处理器缓存的数据其实还是旧的。 每个处理器通过嗅探总线传播的数据检查自己的缓存是不是过期了。 如果发现过期了那么就把缓存行设置为无效的状态。并且重新去内存读取数据。 小结volatile的底层原理其实就是通过lock信号和MESI协议通知所有的处理器缓存失效并且把数据更新到了内存。 2.2、Synchronized Java SE1.6对synchronized进行优化引入偏向锁和轻量级锁以及锁存储结构和升级过程。synchronized实现同步的基础 普通方法锁的是当前实例对象静态同步方法锁的是当前类Class对象同步方法块锁住的是synchronized括号里面的对象线程访问同步代码的时候必须要得到锁退出或抛出异常时必须释放锁 锁 到底存在哪里呢 锁 里面会存 储 什么信息呢 同步方法块使用的是monitorenter和monitorexitmonitorenter指令指向同步代码块开始的位置monitorexit指向同步代码块结束的位置线程执行到monitorenter指令时将会尝试获取对象所对应的monitor的所有权即尝试获得对象的锁同步方法使用ACC_SYNCHRONIZED标识指明该方法是一个同步方法 本 质 是 对一个 对 象的 监视 器 monitor 进 行 获 取而 这 个 获 取 过 程是排他的也就是同一 时刻只能有一个线 程 获 取到由 synchronized 所保 护对 象的 监视 器 monitor对象存在每个java对象的对象头中 2.2.1对象头 synchronized 用的 锁 存在 Java 对 象 头 里。如果 对 象是数 组类 型 则 虚 拟机用12个字节 存 储对 象 头 如果 对 象是非数 组类 型 则 用 8个字节 存 储对象头 Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位 ​​​在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。 2.2.2锁升级 锁状态无 锁 状 态 、偏向 锁 状 态 、 轻 量 级锁状态 和重量 级锁 状 态 这 几个状 态 会随着 竞 争情况逐 渐 升 级 。 锁 可以升 级 但不能降 级 目的是 为了提高获 得 锁 和 释 放 锁 的效率 偏向锁 若不存在多线程竞争经常由某个线程获取。为了让次线程获取锁的代价更低引入了偏向锁 线程访问获取锁的时候会在对象头和栈帧的锁记录存储锁偏向的线程ID那么该线程获取锁的时候就不需要去CAS来加锁和解锁。只需要检测Mark Word里面的线程ID是否指向当前线程 偏向锁的撤销 偏向锁是等待要竞争的时候才会释放锁的机制。 如果有其他线程竞争锁那么首先是暂停持有锁的线程并且检查是不是存活如果是拥有偏向锁的栈会被执行遍历偏向对象的锁记录栈中的锁记录和对象头的Mark Word要么重新偏向其他线程。要么就是恢复到无锁或者标记对象不适合作为偏向锁。最后唤醒暂停的线程。 如果持有的线程结束那么就标记为无锁的状态。 另一个线程如果CAS失败那么就会暂停持有的线程持有的线程会解锁并且把锁的线程ID设置为空。然后恢复线程。 轻量级锁 轻量级加锁 线程执行同步块之前JVM会在线程里面创建锁记录的空间并且把对象头的Mark Word复制到锁记录中。 然后通过CAS来把Mark Word替换为指向锁记录的指针。 如果成功那么就获取到轻量级的锁。 如果失败说明有其他线程竞争那么线程就尝试自旋获取锁。 轻量级解锁 通过CAS把Mark Word恢复到对象头。 如果成功那么说明没有竞争失败说明有竞争。这个时候释放锁并且唤醒等待的线程。因为已经锁膨胀所以Mark Word指向的是重量级锁的 如果失败那么就会膨胀为重量级的锁。这个时候竞争的线程2会把锁改为10也就是重量级锁。 锁的优缺点 2.3、java中实现原子操作 使用循环CAS实现原子操作实际上就是检查和赋值合成一个原子操作。通过CMPXCHG指令完成自旋。 CAS实现原子操作的三大问题 ABA问题 循环时间长开销大。如果CAS不成功就会一直自旋。可 只能保证一个共享变量的原子操作对于多个变量它是无法处理的。 使用锁机制实现原子操作保证了只有获取锁的线程才能够操作锁定的内存区域。 三、java内存模型 3.1、JMM基础 3.2、重排序 3.2.1、数据依赖性 如果两个操作对一个共享变量操作而且有一个是写操作那么两个操作就是数据依赖的。编译器和处理器都是遵循数据依赖性的单个处理器中 3.2.2、as-if-serial as-if-serial语义就是不管怎么重排序执行结果都是不会变的。所以编译器和处理器不会对数据依赖的指令进行重排序 3.2.3、程序顺序规则 只要指令之间有可见性的关系那么就不能够重排序。如果前一个操作不需要对后面的操作可见那么就可以重排序 3.2.4、重排序对多线程的影响 重排序可能会把多线程执行的结果修改。 3.3、顺序一致性 3.4、volatile的内存语义 插入内存屏障保证禁止指令重排 3.5、锁的内存语义 3.5.1、 锁的释放-获取建立的happens-before关系 3.5.2、锁的释放和获取的内存语义 线程释放锁的时候JMM会把该线程对应的本地内存共享变量刷新到主内存 线程获取锁的时候,JMM会把线程的本地内存设置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量 可以看到锁释放的内存语义和volatile写的是一样的。获取锁语义和volatile读是一样的 线程A释放锁实质是线程A对某个要获取锁的线程发出要对共享变量修改的消息 线程B获取锁实质是线程B接收了某个线程发出的修改消息 线程A释放锁和线程B获取锁实质上是线程A通过主内存向线程B发送消息 3.6、final域的内存语义 3.7、happens-before 3.7.1、happens-before的定义 如果线程A的写操作a和线程B的读操作b存在happens-before那么JMM可以保证a一定是对于b是可见的。 JSR-133的定义 如果一个操作happens-before另一个操作第一个操作对第二个操作的可见并且第一个操作在第二个操作之前 两个操作存在happens-before关系并不意味着Java平台具体实现要按照这个顺序执行。如果重排序之后结果一致那么这种重排序是可以的。 JMM的承诺 JMM对程序员承诺A happens-before B也就是A的结果对B是可见的。A的执行顺序在B之前。 JMM对编译器和处理器的承诺只要不改变结果就可以优化。 as-if-serial语义保证单线程内执行结果不被改变happens-before关系保证同步的多线程执行结果不会改变。 as-if-serial语义给程序员创建了幻境单线程程序按照程序顺序执行happens-before的幻境就是正确的同步多线程是按照happens-before指定的顺序执行的。 3.7.2、happens-before规则 程序顺序规则监视器锁规则volatile变量规则传递性start()规则join()规则 3.8、DCL和延迟初始化 public class Singleton {private volatile static Singleton uniqueInstance;private Singleton(){}public static Singleton getUniqueInstance(){//判断对象是否实例过if (uniqueInstance null) {//类对象加锁synchronized (Singleton.class) {if (uniqueInstance null) {uniqueInstance new Singleton();}}}return uniqueInstance;} } uniqueInstance new Singleton();分三步 为uniqueInstance分配内存空间 初始化uniqueInstance 将uniqueInstance指向分配好的内存地址 JVM具有指令重排指向顺序可能变成1-3-2。如线程T1执行了1和3T2调用方法发现uniqueInstance不为null直接返回然而uniqueInstance还没被初始化。volatile可以禁止指令重排 3.9、JMM综述 四、多线程 4.1、线程简介 4.1.1、什么是线程 现代操作系 统 在运行一个程序 时 会 为 其 创 建一个 进程。现 代操作系 统调 度的最小 单 元是 线 程也叫 轻 量 级进 程Light Weight Process 在一个 进 程里可以 创 建多个 线 程 这 些 线 程都 拥 有各自的 计 数器、堆 栈和局部 变 量等属性并且能 够访问 共享的内存 变 量。 处 理器在 这 些 线 程上高速切 换 让 使用者感觉到 这 些 线 程在同 时执 行并发。 4.1.2、为什么使用多线程 更多的处理器核心以及超线程技术的广泛运用更快的响应时间 4.1.3、线程优先级 现代操作系统基本采用时分的形式调度运行的线程操作系统会分出一个个时间片线程会分配到若干时间片当线程的时间片用完了就会发生线程调度并等待着下次分配。线程分配到的时间片多少也就决定了线程使用处理器资源的多少而线程优先级就是决定线程需要多或者少分配一些处理器资源的线程属性。Java中可以使用Thread类的setPriority()方法来设置线程的优先级。线程的优先级介于1到10其中10是最高优先级1是最低优先级。默认情况下线程的优先级为5。注线程优先级是提示给操作系统的操作系统可能会根据自己的调度策略来决定线程的实际执行顺序。因此即使设置了线程的优先级也不能保证线程一定会按照优先级顺序执行。 4.1.4、线程状态 NEW初始转态线程被构建但还没有调用start()方法RUNNABLE运行状态BLOCKED阻塞状态表示线程阻塞于锁WAITING等待状态进入该状态表示当前线程需要等待其他线程通知或中断TIME_WAITING超时等待在指定的时间自行返回TERMINATED终止状态表示当前线程已经执行完毕 线程创建之后调用start()方法开始运行。当线程执行wait()方法之后线程进入等待状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态而超时等待状态相当于在等待状态的基础上增加了超时限制也就是超时时间到达时将会返回到运行状态。当线程调用同步方法时在没有获取到锁的情况下线程将会进入到阻塞状态。线程在执行Runnable的run()方法之后将会进入到终止状态。 4.1.5、Daemon线程 Daemon线程是一种支持型线程(守护线程)主要被用作程序中后台调度以及支持性工作。这 意味着当一个Java虚拟机中所有用户线程结束时Java虚拟机将会退出所有Daemon线程都需要立即终止。可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程。 Daemon 属性需要在启 动线 程之前 设 置不能在启 动线 程之后 设 置。 在Java虚拟机退出时Daemon线程中的finally块并不一定会执行 4.2、启动和终止线程 4.2.1、构造线程 在运行线程之前首先要构造一个线程对象线程对象在构造的时候需要提供线程所需要的属性如线程所属的线程组、线程优先级、是否是Daemon线程等信息。 Thread中对线程进行初始化 private Thread(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {if (name null) {throw new NullPointerException(name cannot be null);}//当前线程就是该线程的父线程Thread parent currentThread();this.group g;将daemon、priority属性设置为父线程的对应属性this.daemon parent.isDaemon();this.priority parent.getPriority();this.target target;setPriority(priority);// 将父线程的InheritableThreadLocal复制过来if (inheritThreadLocals parent.inheritableThreadLocals ! null)this.inheritableThreadLocals ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);/* Set thread ID */this.tid nextThreadID();} 在上述 过 程中一个新构造的 线 程 对 象是由其 parent 线 程来 进 行空 间 分配的而 child 线程继 承了 parent 是否 为 Daemon 、 优 先 级 和加 载资 源的 contextClassLoader 以及可 继承的 ThreadLocal 同 时还 会分配一个唯一的 ID 来 标识这 个 child 线 程。至此一个能 够 运行的 线 程对 象就初始化好了在堆内存中等待着运行 4.2.2、启动线程 线 程 在初始化完成之后 调 用 start() 方法就可以启 动该 线 程。s tart() 方法的含义是当前 线 程即 parent 线 程同步告知 Java 虚 拟 机只要 线 程 规 划器空 闲 应 立即启 动调用start() 方法的 线 程。 4.2.3、中断 中断可以理解 为线 程的一个 标识 位属性它表示一个运行中的 线 程是否被其他 线 程 进行了中断操作。中断好比其他 线 程 对该线 程打了个招呼其他 线 程通 过调 用 该线 程的interrupt()方法 对 其 进 行中断操作。 4.2.4、过期的suspend()、resume()、和stop() suspend() 、 resume() 和 stop() 方法完成了 线 程的 暂 停、恢复和终止工作 不建 议 使用的原因主要有 suspend() 方法 在 调 用后 线 程不会 释 放已 经 占有的资源比如 锁 而是占有着 资 源 进 入睡眠状 态 这样 容易引 发 死 锁问题 。同 样 stop() 方法在终结一个 线 程 时 不会保 证线 程的 资 源正常 释 放通常是没有 给 予 线 程完成 资 源 释放工作的机会因此会 导 致程序可能工作在不确定状 态 4.3、线程间通信 4.3.1、volatile和synchronized关键字 volatile可以用来修饰字段成员变量告知程序任何对该变量的访问均需要从共享内存中获取而对它的改变必须同步刷新回共享内存它能保证所有线程对变量访问的可见性。过多地使用volatile是不必要的因为它会降低程序执行的效率。 synchronized 可以修 饰 方法或者以同步 块 的形式来 进 行使用确保多个 线程在同一个 时 刻只能有一个 线 程 处 于方法或者同步 块 中保 证 了 线 程 对变 量 访问 的可 见性和排他性。 对 象、 对 象的 监视 器、同步 队 列和 执 行 线 程之 间 的关系 任意 线 程 对 Object synchronized 保 护 的 访问 首先要 获得Object 的 监视 器。如果 获 取失 败 线 程 进 入同步 队 列 线 程状 态变为 BLOCKED 。当 访问Object的前 驱 获 得了 锁 的 线 程 释 放了 锁 则该释 放操作 唤 醒阻塞在同步 队 列中的 线程使其重新尝试对监视 器的获取。 4.3.2、等待/通知机制 一个 线 程修改了一个 对 象的 值 而另一个 线 程感知到了 变 化然后 进 行相 应的操作整个过 程开始于一个 线 程而最 终执 行又是另一个 线 程。前者是生 产 者后者就是消 费 者 这种模式隔离了 “ 做什么 ” what 和 “ 怎么做 ” How 在功能 层 面上 实现 了解耦体系 结 构上具 备了良好的伸 缩 性。 等待 / 通知的相关方法是任意 Java 对 象都具 备 的因 为这 些方法被定 义 在所有 对象的父类java.lang.Object 上 等待 / 通知机制是指一个 线 程 A 调 用了 对 象 O 的 wait() 方法 进 入等待状 态 而另一个 线 程B调 用了 对 象 O 的 notify() 或者 notifyAll() 方法 线 程 A 收到通知后从 对 象 O 的 wait() 方法返回 进而执 行后 续 操作。上述两个 线 程通 过对 象 O 来完成交互而 对 象上的 wait() 和 notify/notifyAll()的关系就如同开关信号一 样 用来完成等待方和通知方之间的交互工作。 使用 wait() 、 notify() 和 notifyAll() 时 需要先 对调 用 对 象加 锁。 调 用 wait() 方法后 线 程状 态 由 RUNNING 变 WAITING 并将当前 线 程放置到 对象的等待 队列。 notify() 或 notifyAll() 方法 调 用后等待 线 程依旧不会从 wait() 返回需要 调 用 notify()或 notifAll() 的 线 程 释 放 锁 之后等待 线 程才有机会从 wait() 返回。 notify() 方法将等待 队 列中的一个等待 线 程从等待 队 列中移到同步 队 列中而notifyAll()方法 则 是将等待 队 列中所有的 线 程全部移到同步 队 列被移 动 的 线 程状 态 由 WAITING变为 BLOCKED。 从 wait() 方法返回的前提是 获 得了 调 用 对 象的 锁。 从上述 细节 中可以看到等待 / 通知机制依托于同步机制其目的就是确保等待 线程从wait() 方法返回 时 能 够 感知到通知 线 程 对变 量做出的修改。 过程WaitThread首先获取了对象的锁然后调用对象的wait()方法从而放弃了锁并进入了对象的等待队列WaitQueue中进入等待状态。由于WaitThread释放了对象的锁NotifyThread随后获取了对象的锁并调用对象的notify()方法将WaitThread从WaitQueue移到SynchronizedQueue中此时WaitThread的状态变为阻塞状态。NotifyThread释放了锁之后WaitThread再次获取到锁并从wait()方法返回继续执行 4.3.3、等待/通知的今典范式 等待方 获 取 对 象的 锁 如果条件不 满 足那么 调 用 对 象的 wait() 方法被通知后仍要 检查条件 条件 满 足 则执 行 对应 的 逻辑 。 synchronized(对象) {while(条件不满足) {对象.wait();}对应的处理逻辑 } 通知方 获 得 对 象的 锁 改 变 条件  通知所有等待在对象上的线程 synchronized(对象) {改变条件对象.notifyAll(); } 4.3.4、Thread.join() 如果一个线程A执行了thread.join()语句其含义是当前线程A等待thread线程终止之后才从thread.join()返回。线程Thread除了提供join()方法之外还提供了join(long millis)和join(long millis,int nanos)两个具备超时特性的方法。这两个超时方法表示如果线程thread在给定的超时时间里没有终止那么将会从该超时方法中返回 JDK 中 Thread.join() 方法的源 码 public final synchronized void join(final long millis) throws InterruptedException {while (isAlive()) {wait(0);} } 当 线 程 终 止 时 会 调 用 线 程自身的 notifyAll() 方法会通知所有等待在 该线 程 对 象上的 线 程。可以看到 join() 方法的 逻辑结 构与 等待 / 通知 经 典范式一致 4.3.5、ThreadLocal ThreadLocal即线程变量是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个程上的一个值。可以通过set(T)方法来设置一个值在当前线程下再通过get()方法获取到原先设置的值。 五、java中的锁 5.1、Lock接口 锁 是用来控制多个 线 程 访问 共享 资 源的方式一般来 说 一个 锁 能 够 防止多个 线 程同时访问 共享 资 源但是有些 锁 可以允 许 多个 线 程并 发 的 访问 共享 资 源比如 读 写 锁 。在 Lock接口出 现 之前 Java 程序是靠 synchronized 关 键 字 实现锁 功能的而 Java SE 5 之后并 发包中新增了L ock 接口以及相关 实现类 用来 实现锁 功能它提供了与 synchronized 关 键 字 类 似的同步功 能只是在使用 时 需要 显 式地 获 取和 释 放 锁 。 虽 然它缺少了通 过 synchronized 块或者方法所提 供的 隐 式 获 取 释 放 锁 的便捷性但是却 拥 有了 锁获 取与 释 放的可操作性、可中断的 获 取 锁以及超 时获 取 锁 等多种 synchronized 关 键 字所不具 备 的同步特性  使用 Lock lock new ReentrantLock(); lock.lock(); try {} finally {//在finally块中释放锁目的是保证在获取到锁之后最终能够被释放lock.unlock(); } Lock接口提供的synchronized关键字所不具备的特性 Lock是一个接口它定义了锁获取和释放的基本操作 5.2、队列同步器-ASQ 队列同步器AbstractQueuedSynchronizer用来构建锁或者其它同步组件。用一个int成员变量表示同步状态。通过内置的FIFO队列完成资源获取线程的排队工作。 同步器的实现主要是继承同步器需要提供getState()、setState(int newState)和compareAndSetState(int expect,int update)方法来获取同步的状态。 同步器支持独占或者是共享地获取锁。 同步器是 实现锁 同步 组 件的关 键 在 锁 的 实现中聚合同步器利用同步器 实现锁 的 语义 。可以 这样 理解二者之 间 的关系 锁 是面向使用者的它定 义 了使用者与 锁交互的接口比如可以允 许 两个 线 程并行 访问 隐 藏了 实现细节 同步器面向的是 锁 的 实现者 它 简 化了 锁 的 实现 方式屏蔽了同步状 态 管理、 线 程的排 队 、等待与 唤 醒等底 层 操作。 锁和同步器很好地隔离了使用者和 实现 者所需关注的 领 域。 5.2.1、AQS接口 同步器的设计是基于模板方法模式的也就是说使用者需要继承同步器并重写指定的方法随后将同步器组合在自定义同步组件的实现中并调用同步器提供的模板方法而这些模板方法将会调用使用者重写的方法。 实现 自定 义 同步 组 件 时 将会调用同步器提供的模板方法 同步器提供的模板方法基本上分 为 3 类 独占式 获 取与 释 放同步状 态 、共享式 获 取与 释 放 同步状 态 和 查询 同步 队 列中的等待 线程情况 5.2.2、AQS的实现 同步队列同步器依赖内部的同步队列一个FIFO双向队列来完成同步状态的管理当前线程获取同步状态失败时同步器会将当前线程以及等待状态等信息构造成为一个节点Node并将其加入同步队列同时会阻塞当前线程当同步状态释放时会把首节点中的线程唤醒使其再次尝试获取同步状态 独占式同步状态获取与释放 共享式同步状态获取与释放共享式获取与独占式获取最主要的区别在于同一时刻能否有多个线程同时获取到同步状 态。 独占式超时获取同步状态通过调用同步器的doAcquireNanos(int arg,long nanosTimeout)方法可以超时获取同步状态即在指定的时间段内获取同步状态如果获取到同步状态则返回true否则返回false 5.3、重入锁 重入 锁 ReentrantLock 顾 名思 义 就是支持重 进 入的 锁 它表示 该锁 能 够 支持一个 线 程对资 源的重复加 锁 。除此之外 该锁 的 还 支持 获 取 锁时 的公平和非公平性 选择 。 实现重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞 线程再次获取锁锁需要去识别获取锁的线程是否为当前占据锁的线程如果是则再 次成功 获 取。 锁的最终释放线程重复n次获取了锁随后在第n次释放该锁后其他线程能够获取到 该锁 。 锁 的最 终释 放要求 锁对 于 获 取 进 行 计 数自增 计 数表示当前 锁 被重复 获 取的次数而锁被 释 放 时 计 数自减当 计 数等于 0 时 表示 锁 已 经 成功 释 放。 公平与非公平获取锁 公平锁如果一个锁是公平的那么锁的获取顺序就应该符合请求的绝对时间顺序也就是FIFO。非公平锁 5.4、读写锁 读写锁在同一时刻可以允许多个读线程访问但是在写线程访问时所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁一个读锁和一个写锁通过分离读锁和写锁使得并发性相比一般的排他锁有了很大提升 一般情况下 读 写 锁 的性能都会比排它 锁 好因 为 大多数 场 景 读 是多于写的。在 读多于写的情况下 读 写 锁 能 够 提供比排它 锁 更好的并 发 性和吞吐量。 Java 并 发 包提供 读 写 锁的实现是 ReentrantReadWriteLock 写锁的获取与释放写锁是一个支持重进入的排它锁。如果当前线程已经获取了写锁则增加写状态。如果当前线程在获取写锁时读锁已经被获取读状态不为0或者该线程不是已经获取写锁的线程则当前线程进入等待状态。读锁的获取与释放读锁是一个支持重进入的共享锁它能够被多个线程同时获取在没有其他写线程访问或者写状态为0时读锁总会被成功地获取而所做的也只是线程安全的增加读状态。如果当前线程已经获取了读锁则增加读状态。如果当前线程在获取读锁时写锁已被其他线程获取则进入等待状态。锁降级锁降级指的是写锁降级成为读锁。锁降级是指把持住当前拥有的写锁再获取到读锁随后释放先前拥有的写锁的过程 5.5、LockSupport 当需要阻塞或 唤 醒一个 线 程的 时 候可使用 LockSupport 工具 类 来完成相应工作。 LockSupport 定 义 了一 组 的公共静 态 方法 这 些方法提供了最基本的 线 程阻塞和 唤醒功能而 LockSupport 也成 为 构建同步 组 件的基 础工具。LockSupport 定 义 了一 组 以 park 开 头 的方法用来阻塞当前 线 程以及unpark(Thread thread) 方法来 唤 醒一个被阻塞的 线 程。 5.6、Condition 任意一个 Java 对 象都 拥 有一 组监视 器方法定 义 在 java.lang.Object 上主要包括 wait()、wait(long timeout) 、 notify() 以及 notifyAll() 方法 这 些方法与 synchronized 同步关 键字配合可以 实现 等待 / 通知模式。 Condition 接口也提供了 类 似 Object 的 监视 器方法与 Lock 配合可以 实现等待/通知模式 5.6.1Condition接口 Condition 定 义 了等待 / 通知两种 类 型的方法当前 线 程 调 用 这 些方法 时 需要提前 获取到Condition 对 象关 联 的 锁 。 Condition 对 象是由 Lock 对 象 调 用 Lock 对 象的 newCondition() 方法创建出来的 换 句 话说 Condition 是依 赖 Lock 对 象的。 Lock lock new ReentrantLock(); Condition condition lock.newCondition(); public void conditionWait() throws InterruptedException {lock.lock();try {condition.await();} finally {lock.unlock();} }public void conditionSignal() throws InterruptedException {lock.lock();try {condition.signal();} finally {lock.unlock();} } 5.6.2、Condition的实现 ConditionObject 是同步器 AbstractQueuedSynchronizer 的内部 类 因 为 Condition的操作需要获 取相关 联 的 锁 所以作 为 同步器的内部 类 也 较为 合理。每个 Condition 对 象都包含着一个队列以下称 为 等待 队 列 该队 列是 Condition 对 象 实现 等待 / 通知功能的关 键。 下面将分析 Condition 的 实现 主要包括等待 队 列、等待和通知。 等待队列等待队列是一个FIFO的队列在队列中的每个节点都包含了一个线程引用该线程就是在Condition对象上等待的线程如果一个线程调用了Condition.await()方法那么该线程将会释放锁、构造成节点加入等待队列并进入等待状态。一个Condition包含一个等待队列Condition拥有首节点firstWaiter和尾节点lastWaiter。当前线程调用Condition.await()方法将会以当前线程构造节点并将节点从尾部加入等待队列。等待调用Condition的await()方法或者以await开头的方法会使当前线程进入等待队列并释放锁同时线程状态变为等待状态。当从await()方法返回时当前线程一定获取了Condition相关联的锁。如果从队列同步队列和等待队列的角度看await()方法当调用await()方法时相当于同步队列的首节点获取了锁的节点移动到Condition的等待队列中通知调用Condition的signal()方法将会唤醒在等待队列中等待时间最长的节点首节点在唤醒节点之前会将节点移到同步队列中。 六、java并发容器和框架 6.1、ConcurrentHashMap的实现原理与使用 ConcurrentHashMap是线程安全且高效的HashMap 6.1.1、为什么使用ConcurrentHashMap HashMap不安全HashTable效率低synchronizedCurrentHashMap采用锁分段技术可有效提升并发访问率 6.1.2、ConcurrentHashMap的结构 6.1.3、ConcurrentHashMap初始化 6.1.4、定位Segment 6.1.5、ConcurrentHashMap的操作 6.2、ConcurrentLinkedQueue 在并发编程中有时候需要使用线程安全的队列。如果要实现一个线程安全的队列有两种方式一种是使用阻塞算法另一种是使用非阻塞算法。使用阻塞算法的队列可以用一个锁入队和出队用同一把锁或两个锁入队和出队用不同的锁等方式来实现。非阻塞的实现方式则可以使用循环CAS的方式来实现。 ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列它采用先进先出的规则对节点进行排序当我们添加一个元素的时候它会添加到队列的尾部当我们获取一个元素时它会返回队列头部的元素。 6.3、阻塞队列 当队列满时队列会阻塞插入元素的线程直到队列不满在队列为空时获取元素的线程会等待队列变为非空。 阻塞 队 列常用于生 产 者和消 费 者的 场 景生 产 者是向 队 列里添加元素的 线 程消 费者是从 队 列里取元素的 线 程。阻塞 队 列就是生 产 者用来存放元素、消 费 者用来获取元素的容器 ​​​​​​​​​​​​​​​​​​​ArrayBlockingQueue一个由数组结构组成的有界阻塞队列 LinkedBlockingQueue一个由链表结构组成的有界阻塞队列PriorityBlockingQueue一个支持优先级排序的无界阻塞队列DelayQueue一个使用优先级队列实现的无界阻塞队列SynchronousQueue一个不存储元素的阻塞队列每一个put操作必须等待一个take操作 否 则 不能 继续 添加元素 LinkedTransferQueue一个由链表结构组成的无界阻塞队列LinkedBlockingDeque一个由链表结构组成的双向阻塞队列​​​​​​​ 6.3.1、阻塞队列实现原理 使用通知模式 实现 。所 谓 通知模式就是当生 产 者往 满 的 队 列里添加元素 时 会阻塞住生 产 者当消 费 者消 费 了一个 队 列中的元素后会通知生 产 者当前 队 列可用。 ​​​​​​​​​​​​​​ArrayBlockingQueue使用了Condition来实现当往队列里插入一个元素时如果队列不可用那么阻塞生产者主要通过LockSupport.parkthis来实现 6.4、Fork/Join框架​​​​​​​ 把大任 务分割成若干个小任 务 最 终汇总 每个小任 务结 果后得到大任 务结 果的框架 七、java中的原子类 原子更新基本 类 型、原子更新数 组、原子更新引用和原子更新属性Atomic 包里的 类 基本都是使用 Unsafe 实现 的包装 类。采用CAS 原子更新基本类型AtomicBoolean、AtomicInteger、AtomicLong ​​​​​​​​​​​​​Unsafe只提供了3种CAS方法compareAndSwapObject、compareAndSwapInt和compareAndSwapLong再看AtomicBoolean源码发现它是先把Boolean转换成整型再使用compareAndSwapInt进行CAS所以原子更新char、float和double变量也可以用类似的思路来实现原子更新数组AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray原子更新引用AtomicReference原子更新属性 八、java中的并发工具 8.1、CountDownLatch 允 许 一个或多个 线 程等待其他 线 程完成操作 使用CountDownLatch的构造函数接收一个int类型的参数作为计数器如果你想等待N个点完成这里就传入N。 当我们调用CountDownLatch的countDown方法时N就会减1CountDownLatch的await方法 会阻塞当前线程直到N变成零。由于countDown方法可以用在任何地方所以这里说的N个 点可以是N个线程也可以是1个线程里的N个执行步骤。用在多个线程时只需要把这个 CountDownLatch的引用传递到线程里即可如果有线程处理得比较慢我们不可能让主线程一直等待所以可以使用另外一个带指定时间的await方法——awaitlong timeTimeUnit unit这个方法等待特定时间后就会不再阻塞当前线程 数器必 须 大于等于 0 只是等于 0 时 候 计 数器就是零 调 用 await 方法 时不会阻塞当前 线 程。 CountDownLatch 不可能重新初始化或者修改 CountDownLatch 对 象的内部 计数 器的 值 。一个 线 程 调 用 countDown 方法 happen-before 另外一个 线 程 调 用 await 方法。 8.2、CyclicBarrier 让一组线 程到达一个屏障同步点 时 被阻塞直到最后一个 线 程到达屏障 时屏障才会开 门 所有被屏障 拦 截的 线 程才会 继续 运行。 使用CyclicBarrier默认的构造方法是CyclicBarrierint parties其参数表示屏障拦截的线程数量每个线程调用await方法告诉CyclicBarrier我已经到达了屏障然后当前线程被阻塞。场景可以用于多线程计算数据最后合并计算结果的场景CyclicBarrier和CountDownLatch的区别CountDownLatch的计数器只能使用一次而CyclicBarrier的计数器可使用reset()方法重置。所以CyclicBarrier能处理更为复杂的业务场景 8.3、Semaphore 是用来控制同 时访问 特定 资 源的 线 程数量它通 过协调 各个 线程以保 证 合理的使用公共 资 源 应用场景流量控制 8.4、Exchanger 是一个用于 线 程 间协 作的工具 类 。 Exchanger 用于 进 行 线 程 间的数据交换 。它提供一个同步点在 这 个同步点两个 线 程可以交 换 彼此的数据。 这 两个 线 程通过exchange 方法交 换 数据如果第一个 线 程先 执 行 exchange() 方法它会一直等待第二个 线程也执 行 exchange 方法当两个 线 程都到达同步点 时 这 两个 线 程就可以交 换 数据将本 线 程生产出来的数据 传递给对 方 应用场景用于遗传算法 九、java中的线程池 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 第二提高响应速度。当任务到达时任务可以不需要等到线程创建就能立即执行。 第三提高线程的可管理性。线程是稀缺资源如果无限制地创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一分配、调优和监控。 9.1、线程池的实现原理 ThreadPoolExecutor执行 如果当前运行的 线 程少于 corePoolSize 则创 建新 线 程来 执 行任 务 注意 执 行 这 一步 骤 需要 获 取全局 锁。 如果运行的 线 程等于或多于 corePoolSize 则 将任 务 加入 BlockingQueue。 如果无法将任 务 加入 BlockingQueue 队 列已 满 则创 建新的 线 程来 处 理任 务注意执行 这 一步 骤 需要 获 取全局 锁。 如果 创 建新 线 程将使当前运行的 线 程超出 maximumPoolSize 任 务 将被拒 绝 并 调 用 RejectedExecutionHandler.rejectedExecution()方法 ThreadPoolExecutor 采取上述步 骤 的 总 体 设计 思路是 为 了在 执 行 execute() 方法 时尽可能地避免 获 取全局 锁 那将会是一个 严 重的可伸 缩 瓶 颈 。在 ThreadPoolExecutor 完成 预热之后当前运行的 线 程数大于等于 corePoolSize 几乎所有的 execute() 方法 调 用都是 执 行步 骤 2而步 骤 2 不需要 获 取全局 锁 。 9.2、线程池的使用 ThreadPoolExecutor创建线程池 参数 corePoolSize 核心线程数 当提交一个任 务 到 线 程池 时 线 程池会 创 建一个线程来 执 行任 务 即使其他空 闲 的基本 线 程能 够执 行新任 务 也会 创 建 线 程等到需要 执行的任 务 数大于 线 程池基本大小 时 就不再 创 建。如果 调 用了 线 程池的 prestartAllCoreThreads()方法线 程池会提前 创 建并启 动 所有核心 线 程。 BlockingQueue阻塞 队 列用于保存等待 执 行的任 务 的阻塞 队 列。可以 选择以下几个阻塞 队列。 ArrayBlockingQueue 是一个基于数 组结 构的有界阻塞 队 列此 队 列按 FIFO 先 进先出原则对 元素 进行排序。 LinkedBlockingQueue 一个基于 链 表 结 构的阻塞 队 列此 队 列按 FIFO排序元素吞吐量通常要高于 ArrayBlockingQueue 。静 态 工厂方法 Executors.newFixedThreadPool() 使用了 这 个 队列。 SynchronousQueue 一个不存 储 元素的阻塞 队 列。每个插入操作必 须 等到另一个 线 程 调用移除操作否 则 插入操作一直 处 于阻塞状 态 吞吐量通常要高于 Linked-BlockingQueue 静 态工厂方法 Executors.newCachedThreadPool 使用了 这 个 队列。 PriorityBlockingQueue 一个具有 优 先 级 的无限阻塞 队 列。 maximumPoolSize 最大 线 程数 线 程池允 许创 建的最大 线 程数。如果 队 列 满了并且已 创 建的 线 程数小于最大 线 程数 则线 程池会再 创 建新的 线 程 执 行任 务 。 值得注意的是如果使用了无界的任 务队 列 这 个参数就没什么效果。 ThreadFactory用于 设 置 创 建 线 程的工厂可以通 过线 程工厂 给 每个 创 建出来的 线 程设置更有意 义 的名字。 RejectedExecutionHandler 拒绝 策略当 队 列和 线 程池都 满 了 说 明 线 程池 处 于 饱和状 态 那么必 须 采取一种策略 处 理提交的新任务。 ​​​​​​​​​​​​​​​​​​​​AbortPolicy直接抛出异常默认 CallerRunsPolicy让调用者所在线程来运行任务 DiscardOldestPolicy丢弃队列里最近的一个任务 DiscardPolicy不处理丢弃掉 keepAliveTime线程存活时间线程池的工作线程空闲后保持存活的时间。所以如果任务很多并且每个任务执行的时间比较短可以调大时间提高线程的利用率。 TimeUnit 线 程活 动 保持 时间 的 单 位​​​​​​​ 提交任务 execute()用于提交不需要返回值的任务所以无法判断任务是否被线程池执行成功subimit()用于提交需要返回值的任务。线程池会返回一个future类型的对象通过这个future对象可以判断任务是否执行成功并且可以通过future的get()方法来获取返回值get()方法会阻塞当前线程直到任务完成而使用getlong timeoutTimeUnit unit方法则会阻塞当前线 程一段 时间 后立即返回 这时 候有可能任 务 没有 执 行完 关闭线程池 通过调用线程池的shutdown或shutdownNow方法来关闭线程池。它们的原理是遍历线程池中的工作线程然后逐个调用线程的interrupt方法来中断线程所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别shutdownNow首先将线程池的状态设置成STOP然后尝试停止所有的正在执行或暂停任务的线程并返回等待执行任务的列表而shutdown只是将线程池的状态设置成SHUTDOWN状态然后中断所有没有正在执行任务的线程 只要调用了这两个关闭方法中的任意一个isShutdown方法就会返回true。当所有的任务都已关闭后才表示线程池关闭成功这时调用isTerminaed方法会返回true。至于应该调用哪一种方法来关闭线程池应该由提交到线程池的任务特性决定通常调用shutdown方法来关闭线程池如果任务不一定要执行完则可以调用shutdownNow方法 合理配置线程数 任务的性质CPU密集型任务、IO密集型任务和混合型任务任务的优先级高、中和低任务的执行时间长、中和短任务的依赖性是否依赖其他系统资源如数据库连接 质 不同的任 务 可以用不同 规 模的 线 程池分开 处 理。 CPU 密集型任 务应配置尽可能小的线 程如配置 N cpu 1 个 线 程的 线 程池。由于 IO 密集型任 务线 程并不是一直在 执 行任 务 则应配置尽可能多的 线 程如 2*N cpu 。混合型的任 务 依赖数据库连接池的任务因为线程提交SQL后需要等待数据库返回结果等待的时间越 长则CPU空闲时间就越长那么线程数应该设置得越大这样才能更好地利用CPU ​​​​​ 线程池监控 如果在系 统 中大量使用 线 程池 则 有必要 对线 程池 进 行 监 控方便在出 现问题时可以根据 线 程池的使用状况快速定位 问题 。 t askCount 线 程池需要 执 行的任 务 数量。 completedTaskCount 线 程池在运行 过 程中已完成的任 务 数量小于或等于 taskCount 。 largestPoolSize线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小则表示线程池曾经满过。 getPoolSize线程池的线程数量。如果线程池不销毁的话线程池里的线程不会自动销​​​​​​​毁所以这个大小只增不减。 getActiveCount获取活动的线程数 十、Executor框架 在 Java 中使用 线 程来异步 执 行任 务 。 Java 线 程的 创 建与 销 毁 需要一定的开 销 如果我们为 每一个任 务创 建一个新 线 程来 执 行 这 些 线 程的 创 建与 销 毁 将消耗大量的 计 算 资 源。同 时为 每一个任 务创 建一个新 线 程来 执 行 这 种策略可能会使 处 于高 负 荷状 态 的 应 用最 终 崩 溃。 Java 的 线 程既是工作 单 元也是 执 行机制。从 JDK 5 开始把工作 单 元与 执行机制分离开来。工作 单 元包括 Runnable 和 Callable 而 执 行机制由 Executor 框架提供。 10.1、Executor 在 HotSpot VM 的 线 程模型中 Java 线程java.lang.Thread 被一 对 一映射 为 本地操作系统线 程。 Java 线 程启 动时 会 创 建一个本地操作系 统线 程当 该 Java 线 程 终 止 时 这 个操作系 统线程 也会被回收。操作系 统 会 调 度所有 线 程并将它 们 分配 给 可用的 CPU。在上 层 Java 多 线 程程序通常把 应 用分解 为 若干个任 务 然后使用用 户级 的 调度器 Executor 框架将 这 些任 务 映射 为 固定数量的 线 程在底 层 操作系 统 内核将 这 些 线程映射到硬件 处 理器上。 结构 任 务 。包括被 执 行任 务 需要 实现 的接口 Runnable 接口或 Callable接口。 任 务 的 执 行。包括任 务执 行机制的核心接口 Executor 以及 继 承自 Executor的 ExecutorService 接口。 Executor 框架有两个关 键类实现 了 ExecutorService接口 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor。 异步 计 算的 结 果。包括接口 Future 和 实现 Future 接口的 FutureTask 类 。 Executor 框架包含的主要的 类 与接口 Executor 是一个接口它是 Executor 框架的基 础 它将任 务 的提交与任 务 的 执行分离开来。 ThreadPoolExecutor是线程池的核心实现类用来执行被提交的任务。 ScheduledThreadPoolExecutor是一个实现类可以在给定的延迟后运行命令或者定期执 行命令。 ScheduledThreadPoolExecutor 比 Timer 更灵活功能更 强大。 Future 接口和 实现 Future 接口的 FutureTask 类 代表异步 计 算的 结果。 Runnable 接口和 Callable 接口的 实现类 都可以被 ThreadPoolExecutor 或Scheduled和ThreadPoolExecutor 执 行。 Executor框架成员 ThreadPoolExecutorThreadPoolExecutor通常使用工厂类Executors来创建。Executors可以创建3种类型的ThreadPoolExecutor SingleThreadExecutor单个线程corePoolSize和maximumPoolSize被设置为1用无界队列FixedThreadPool固定线程数KeepAliveTime设置为0L用无界队列CachedThreadPool大小无界的线程池corePoolSize被设置为0maximumPoolSize被设置为Integer.MAX_VALUEScheduledThreadPoolExecutor要用来在给定的延迟之后运行任务或者定期执行任务。DelayQueue无界队列 Future接口 Runnable接口无返回结果 ​​​​​​​Callable接口有返回结果 Executors 10.2、FutureTask Future 接口和 实现 Future 接口的 FutureTask 类 代表异步 计 算的 结 果。 面试题​​​​​​​
http://www.zqtcl.cn/news/546270/

相关文章:

  • 美食网站设计的基本思路大网站开发语言
  • 个人网站模板打包下载最近新闻热点国家大事
  • flash做网站步骤中国网评中国网评
  • 网站添加备案号比较好的网站建设公司
  • 旅游电子商务网站建设目的广告设计与制作主修课程
  • 网站标题写什么作用记事本做网站如何添加图片
  • 海口建站模板厂家下载什么网站做吃的
  • 网站建设的指导书动效网站建设
  • 万州做网站的公司wordpress练习
  • 网站域名dnsgoogle推广教程
  • 网站建设报价方案doc网站建设seo视频教程
  • 北京免费建站网络营销怎么做查询网站后台
  • 深圳外贸网站推广用html制作个人博客
  • 建设银行网站最近打不开吗wordpress c
  • 网站icp备案费用浅谈做网站的好处
  • 制作网站需要懂哪些在线设计平台的市场调研
  • 接计设做的网站河南网站建设华企祥云
  • 网站系统维护一般要多久企业网站推广工具
  • 如何诊断网站seo做个网站商场需要多少
  • 腾讯云做视频网站吗创业商机网加工项目
  • 网站建设论文文献郑州seo外包费用
  • 网站优化西安如何免费推广网站
  • 固原市建设局网站外贸网站建设方法
  • 做违规网站主页制作语言缩写
  • 汝南县网站建设怎么注册公司钉钉账号
  • 网站建设酷隆信通网站开发中心
  • 保定网站建设方案报价怎么做网站_
  • 做网站功能的框架结构图做网站用python好吗
  • 襄樊市网站建设模版网站建设企业
  • 网站换服务器php大流量网站开发规范