湛江有网站的公司名称,关于网站建设费用,wordpress和phpwind,做我的狗哪个网站可以看1、Object 的 wait()和notify() 方法下图为线程状态的图#xff1a;Object 对象中的 wait()和notify()是用来实现实现等待 / 通知模式。其中等待状态和阻塞状态是不同的。等待状态的线程可以通过notify() 方法唤醒并继续执行#xff0c;而阻塞状态的线程则是等待获取新的锁。… 1、Object 的 wait()和notify() 方法下图为线程状态的图Object 对象中的 wait()和notify()是用来实现实现等待 / 通知模式。其中等待状态和阻塞状态是不同的。等待状态的线程可以通过notify() 方法唤醒并继续执行而阻塞状态的线程则是等待获取新的锁。调用 wait()方法后当前线程会进入等待状态直到其他线程调用notify()或notifyAll() 来唤醒。调用 notify() 方法后可以唤醒正在等待的单一线程。2、并发特性 - 原子性、有序性、可见性原子性即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断要么就都不执行。可见性指当多个线程访问同一个变量时一个线程修改了这个变量的值其他线程能够立即看得到修改的值。有序性即程序执行的顺序按照代码的先后顺序执行不进行指令重排列。3、synchronized 实现原理synchronized 可以保证方法或者代码块在运行时同一时刻只有一个进程可以访问同时它还可以保证共享变量的内存可见性。Java 中每一个对象都可以作为锁这是 synchronized 实现同步的基础普通同步方法锁是当前实例对象静态同步方法锁是当前类的 class 对象同步方法块锁是括号里面的对象同步代码块monitorenter 指令插入到同步代码块的开始位置monitorexit指令插入到同步代码块的结束位置JVM 需要保证每一个monitorenter都有一个monitorexit与之相对应。任何对象都有一个 Monitor 与之相关联当且一个 Monitor 被持有之后他将处于锁定状态。线程执行到monitorenter 指令时将会尝试获取对象所对应的 Monitor 所有权即尝试获取对象的锁。同步方法synchronized 方法则会被翻译成普通的方法调用和返回指令如invokevirtual、areturn指令在 VM 字节码层面并没有任何特别的指令来实现被synchronized修饰的方法而是在 Class 文件的方法表中将该方法的access_flags字段中的synchronized 标志位置设置为 1表示该方法是同步方法并使用调用该方法的对象或该方法所属的 Class 在 JVM 的内部对象表示 Klass作为锁对象。synchronized 是重量级锁在 JDK1.6 中进行优化如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。4、volatile 的实现原理volatile 是轻量级的锁它不会引起线程上下文的切换和调度。volatile可见性对一个volatile 的读总可以看到对这个变量最终的写。volatile 原子性volatile对单个读 / 写具有原子性32 位 Long、Double但是复合操作除外例如i 。JVM 底层采用“内存屏障”来实现 volatile 语义防止指令重排序。volatile 经常用于两个两个场景状态标记变量、Double Check 。5、Java 内存模型JMMJMM 规定了线程的工作内存和主内存的交互关系以及线程之间的可见性和程序的执行顺序。一方面要为程序员提供足够强的内存可见性保证。另一方面对编译器和处理器的限制要尽可能地放松。JMM 对程序员屏蔽了 CPU 以及 OS 内存的使用问题能够使程序在不同的 CPU 和 OS 内存上都能够达到预期的效果。Java 采用内存共享的模式来实现线程之间的通信。编译器和处理器可以对程序进行重排序优化处理但是需要遵守一些规则不能随意重排序。在并发编程模式中势必会遇到上面三个概念原子性一个操作或者多个操作要么全部执行要么全部不执行。可见性当多个线程同时访问一个共享变量时如果其中某个线程更改了该共享变量其他线程应该可以立刻看到这个改变。有序性程序的执行要按照代码的先后顺序执行。通过 volatile、synchronized、final、concurrent 包等 实现。6、有关队列 AQS 队列同步器AQS 是构建锁或者其他同步组件的基础框架如 ReentrantLock、ReentrantReadWriteLock、Semaphore 等, 包含了实现同步器的细节获取同步状态、FIFO 同步队列。AQS 的主要使用方式是继承子类通过继承同步器并实现它的抽象方法来管理同步状态。①维护一个同步状态 state。当 state 0时表示已经获取了锁当state 0 时表示释放了锁。②AQS 通过内置的 FIFO 同步队列来完成资源获取线程的排队工作如果当前线程获取同步状态失败锁时AQS 则会将当前线程以及等待状态等信息构造成一个节点Node并将其加入同步队列同时会阻塞当前线程当同步状态释放时则会把节点中的线程唤醒使其再次尝试获取同步状态。AQS 内部维护的是** CLH 双向同步队列**7、锁的特性可重入锁指的是在一个线程中可以多次获取同一把锁。 ReentrantLock 和 synchronized 都是可重入锁。可中断锁顾名思义就是可以相应中断的锁。synchronized 就不是可中断锁而 Lock 是可中断锁。公平锁即尽量以请求锁的顺序来获取锁。synchronized 是非公平锁ReentrantLock 和 ReentrantReadWriteLock它默认情况下是非公平锁但是可以设置为公平锁。8、ReentrantLock 锁ReentrantLock可重入锁是一种递归无阻塞的同步机制。它可以等同于 synchronized的使用但是 ReentrantLock 提供了比synchronized 更强大、灵活的锁机制可以减少死锁发生的概率。ReentrantLock 实现 Lock 接口基于内部的 Sync 实现。Sync 实现 AQS 提供了 FairSync 和 NonFairSync 两种实现。ConditionCondition 和 Lock 一起使用以实现等待/通知模式通过 await()和singnal() 来阻塞和唤醒线程。Condition 是一种广义上的条件队列。他为线程提供了一种更为灵活的等待 / 通知模式线程在调用 await 方法后执行挂起操作直到线程等待的某个条件为真时才会被唤醒。Condition 必须要配合 Lock 一起使用因为对共享状态变量的访问发生在多线程环境下。一个 Condition 的实例必须与一个 Lock 绑定因此 Condition 一般都是作为 Lock 的内部实现。9、ReentrantReadWriteLock读写锁维护着一对锁一个读锁和一个写锁。通过分离读锁和写锁使得并发性比一般的排他锁有了较大的提升在同一时间可以允许多个读线程同时访问。但是在写线程访问时所有读线程和写线程都会被阻塞。读写锁的主要特性公平性支持公平性和非公平性。重入性支持重入。读写锁最多支持 65535 个递归写入锁和 65535 个递归读取锁。锁降级遵循获取写锁再获取读锁最后释放写锁的次序如此写锁能够降级成为读锁。ReentrantReadWriteLock 实现 ReadWriteLock 接口可重入的读写锁实现类。在同步状态上为了表示两把锁将一个 32 位整型分为高 16 位和低 16 位分别表示读和写的状态10、Synchronized 和 Lock 的区别Lock 是一个接口而 synchronized 是 Java 中的关键字synchronized 是内置的语言实现synchronized 在发生异常时会自动释放线程占有的锁因此不会导致死锁现象发生而 Lock 在发生异常时如果没有主动通过 unLock() 去释放锁则很可能造成死锁现象因此使用 Lock 时需要在 finally 块中释放锁Lock 可以让等待锁的线程响应中断而 synchronized 却不行使用 synchronized 时- 等待的线程会一直等待下去不能够响应中断通过 Lock 可以知道有没有成功获取锁而 synchronized 却无法办到。Lock 可以提高多个线程进行读操作的效率。更深的与 synchronized 相比ReentrantLock 提供了更多更加全面的功能具备更强的扩展性。例如时间锁等候可中断锁等候锁投票。ReentrantLock 还提供了条件 Condition 对线程的等待、唤醒操作更加详细和灵活所以在多个条件变量和高度竞争锁的地方ReentrantLock 更加适合以后会阐述 Condition。ReentrantLock 提供了可轮询的锁请求。它会尝试着去获取锁如果成功则继续否则可以等到下次运行时处理而 synchronized则一旦进入锁请求要么成功要么阻塞所以相比synchronized 而言ReentrantLock 会不容易产生死锁些。ReentrantLock 支持更加灵活的同步代码块但是使用 synchronized时只能在同一个synchronized块结构中获取和释放。注意ReentrantLock 的锁释放一定要在finally 中处理否则可能会产生严重的后果。ReentrantLock 支持中断处理且性能较 synchronized 会好些。11、Java 中线程同步的方式sychronized 同步方法或代码块volatileLockThreadLocal阻塞队列LinkedBlockingQueue使用原子变量java.util.concurrent.atomic变量的不可变性12、CAS 是一种什么样的同步机制多线程下为什么不使用 int 而使用 AtomicIntegerCompare And Swap比较交换。可以看到 synchronized 可以保证代码块原子性很多时候会引起性能问题volatile也是个不错的选择但是volatile 不能保证原子性只能在某些场合下使用。所以可以通过 CAS 来进行同步保证原子性。我们在读 Concurrent 包下的类的源码时发现无论是 ReentrantLock 内部的 AQS还是各种 Atomic 开头的原子类内部都应用到了 CAS。在 CAS 中有三个参数内存值 V、旧的预期值 A、要更新的值 B 当且仅当内存值 V 的值等于旧的预期值 A 时才会将内存值 V 的值修改为 B否则什么都不干。其伪代码如下if (this.value A) { this.value B return true;} else { return false;}CAS 可以保证一次的读-改-写操作是原子操作。在多线程环境下int 类型的自增操作不是原子的线程不安全可以使用 AtomicInteger 代替。// AtomicInteger.javaprivate static final Unsafe unsafe Unsafe.getUnsafe();private static final long valueOffset;static { try { valueOffset unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField(value)); } catch (Exception ex) { throw new Error(ex); }}private volatile int value;Unsafe 是 CAS 的核心类Java 无法直接访问底层操作系统而是通过本地 native 方法来访问。不过尽管如此JVM 还是开了一个后门Unsafe 它提供了硬件级别的原子操作。valueOffset 为变量值在内存中的偏移地址Unsafe 就是通过偏移地址来得到数据的原值的。value当前值使用volatile 修饰保证多线程环境下看见的是同一个。// AtomicInteger.javapublic final int addAndGet(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta) delta;}// Unsafe.java// compareAndSwapIntvar1, var2, var5, var5 var4其实换成 compareAndSwapIntobj, offset, expect, update比较清楚意思就是如果 obj 内的 value 和 expect 相等就证明没有其他线程改变过这个变量那么就更新它为 update如果这一步的 CAS 没有成功那就采用自旋的方式继续进行 CAS 操作取出乍一看这也是两个步骤了啊其实在 JNI 里是借助于一个 CPU 指令完成的。所以还是原子操作。public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 var4)); return var5;}// 该方法为本地方法有四个参数分别代表对象、对象的地址、预期值、修改值public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);13、HashMap 是不是线程安全如何体现如何变得安全由于添加元素到 map 中去时数据量大产生扩容操作多线程会导致 HashMap 的 node 链表形成环状的数据结构产生死循环。所以 HashMap 是线程不安全的。如何变得安全Hashtable通过 synchronized 来保证线程安全的独占锁悲观策略。吞吐量较低性能较为低下SynchronizedHashMap 通过 Collections.synchronizedMap() 方法对 HashMap 进行包装返回一个 SynchronizedHashMap 对象在源码中 SynchronizedHashMap 也是用过 synchronized 来保证线程安全的。但是实现方式和 Hashtable 略有不同前者是 synchronized 方法后者是通过 synchronized 对互斥变量加锁实现ConcurrentHashMapJUC 中的线程安全容器高效并发。ConcurrentHashMap 的 key、value 都不允许为 null。14、ConcurrentHashMap 的实现方式ConcurrentHashMap 的实现方式和 Hashtable 不同不采用独占锁的形式更高效其中在 jdk1.7 和 jdk1.8 中实现的方式也略有不同。Jdk1.7 中采用分段锁和 HashEntry 使锁更加细化。ConcurrentHashMap 采用了分段锁技术其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量的线程并发。Jdk1.8 利用 CASSynchronized 来保证并发更新的安全当然底层采用数组链表红黑树的存储结构。table 中存放 Node 节点数据默认 Node 数据大小为 16扩容大小总是 2^N。为了保证可见性Node 节点中的 val 和 next 节点都用 volatile 修饰。当链表长度大于 8 时会转换成红黑树节点会被包装成 TreeNode放在TreeBin 中。put()1. 计算键所对应的 hash 值2. 如果哈希表还未初始化调用 initTable() 初始化否则在 table 中找到 index 位置并通过 CAS 添加节点。如果链表节点数目超过 8则将链表转换为红黑树。如果节点总数超过则进行扩容操作。get()无需加锁直接根据 key 的 hash 值遍历 node。15、CountDownLatch 和 CyclicBarrier 的区别 并发工具类CyclicBarrier 它允许一组线程互相等待直到到达某个公共屏障点 (Common Barrier Point)。在涉及一组固定大小的线程的程序中这些线程必须不时地互相等待此时 CyclicBarrier 很有用。因为该 Barrier 在释放等待线程后可以重用所以称它为循环 ( Cyclic ) 的 屏障 ( Barrier ) 。每个线程调用 #await() 方法告诉 CyclicBarrier 我已经到达了屏障然后当前线程被阻塞。当所有线程都到达了屏障结束阻塞所有线程可继续执行后续逻辑。CountDownLatch 能够使一个线程在等待另外一些线程完成各自工作之后再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后计数器的值就会减一。当计数器的值为 0 时表示所有的线程都已经完成了任务然后在 CountDownLatch 上等待的线程就可以恢复执行任务。两者区别CountDownLatch 的作用是允许 1 或 N 个线程等待其他线程完成执行而 CyclicBarrier 则是允许 N 个线程相互等待。CountDownLatch 的计数器无法被重置CyclicBarrier 的计数器可以被重置后使用因此它被称为是循环的 barrier 。Semaphore 是一个控制访问多个共享资源的计数器和 CountDownLatch 一样其本质上是一个“共享锁”。一个计数信号量。从概念上讲信号量维护了一个许可集。如有必要在许可可用前会阻塞每一个 acquire然后再获取该许可。每个 release 添加一个许可从而可能释放一个正在阻塞的获取者。16、怎么控制线程尽可能减少上下文切换减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。无锁并发编程。多线程竞争锁时会引起上下文切换所以多线程处理数据时可以使用一些方法来避免使用锁。如将数据的ID按照Hash算法取模分段不同的线程处理不同段的数据。CAS算法。Java的Atomic包使用CAS算法来更新数据而不需要加锁。使用最少线程。避免创建不需要的线程比如任务很少但是创建了很多线程来处理这样会造成大量线程都处于等待状态。协程。在单线程里实现多任务的调度并在单线程里维持多个任务间的切换。17、什么是乐观锁和悲观锁像 synchronized这种独占锁属于悲观锁它是在假设一定会发生冲突的那么加锁恰好有用除此之外还有乐观锁乐观锁的含义就是假设没有发生冲突那么我正好可以进行某项操作如果要是发生冲突呢那我就重试直到成功乐观锁最常见的就是CAS。18、阻塞队列阻塞队列实现了 BlockingQueue 接口并且有多组处理方法。抛出异常add(e) 、remove()、element()返回特殊值offer(e) 、pool()、peek()阻塞put(e) 、take()JDK 8 中提供了七个阻塞队列可供使用ArrayBlockingQueue 一个由数组结构组成的有界阻塞队列。LinkedBlockingQueue 一个由链表结构组成的无界阻塞队列。PriorityBlockingQueue 一个支持优先级排序的无界阻塞队列。DelayQueue一个使用优先级队列实现的无界阻塞队列。SynchronousQueue一个不存储元素的阻塞队列。LinkedTransferQueue一个由链表结构组成的无界阻塞队列。LinkedBlockingDeque一个由链表结构组成的双向阻塞队列。ArrayBlockingQueue一个由数组实现的有界阻塞队列。该队列采用 FIFO 的原则对元素进行排序添加的。内部使用可重入锁 ReentrantLock Condition 来完成多线程环境的并发操作。19、线程池线程池有五种状态RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED。RUNNING接收并处理任务。SHUTDOWN不接收但处理现有任务。STOP不接收也不处理任务同时终端当前处理的任务。TIDYING所有任务终止线程池会变为 TIDYING 状态。当线程池变为 TIDYING 状态时会执行钩子函数 terminated()。TERMINATED线程池彻底终止的状态。内部变量** ctl **定义为 AtomicInteger 记录了“线程池中的任务数量”和“线程池的状态”两个信息。共 32 位其中高 3 位表示”线程池状态”低 29 位表示”线程池中的任务数量”。线程池创建参数corePoolSize线程池中核心线程的数量。当提交一个任务时线程池会新建一个线程来执行任务直到当前线程数等于 corePoolSize。如果调用了线程池的 prestartAllCoreThreads() 方法线程池会提前创建并启动所有基本线程。maximumPoolSize线程池中允许的最大线程数。线程池的阻塞队列满了之后如果还有任务提交如果当前的线程数小于 maximumPoolSize则会新建线程来执行任务。注意如果使用的是无界队列该参数也就没有什么效果了。keepAliveTime线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁而是继续存活一段时间keepAliveTime。默认情况下该参数只有在线程数大于 corePoolSize 时才会生效。unitkeepAliveTime 的单位。TimeUnitworkQueue用来保存等待执行的任务的阻塞队列等待的任务必须实现 Runnable 接口。我们可以选择如下几种ArrayBlockingQueue基于数组结构的有界阻塞队列FIFO。LinkedBlockingQueue基于链表结构的有界阻塞队列FIFO。SynchronousQueue不存储元素的阻塞队列每个插入操作都必须等待一个移出操作反之亦然。PriorityBlockingQueue具有优先界别的阻塞队列。threadFactory用于设置创建线程的工厂。该对象可以通过 Executors.defaultThreadFactory()。他是通过 newThread() 方法提供创建线程的功能newThread() 方法创建的线程都是“非守护线程”而且“线程优先级都是 Thread.NORM_PRIORITY”。handlerRejectedExecutionHandler线程池的拒绝策略。所谓拒绝策略是指将任务添加到线程池中时线程池拒绝该任务所采取的相应策略。当向线程池中提交任务时如果此时线程池中的线程已经饱和了而且阻塞队列也已经满了则线程池会选择一种拒绝策略来处理该任务。线程池提供了四种拒绝策略AbortPolicy直接抛出异常默认策略CallerRunsPolicy用调用者所在的线程来执行任务DiscardOldestPolicy丢弃阻塞队列中靠最前的任务并执行当前任务DiscardPolicy直接丢弃任务当然我们也可以实现自己的拒绝策略例如记录日志等等实现 RejectedExecutionHandler 接口即可。当添加新的任务到线程池时线程数量未达到 corePoolSize则新建一个线程核心线程执行任务线程数量达到了 corePoolSize则将任务移入队列等待队列已满新建线程非核心线程执行任务队列已满总线程数又达到了 maximumPoolSize就会由 handler 的拒绝策略来处理线程池可通过 Executor 框架来进行创建FixedThreadPoolpublic static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueueRunnable());}corePoolSize 和 maximumPoolSize 都设置为创建 FixedThreadPool 时指定的参数 nThreads意味着当线程池满时且阻塞队列也已经满时如果继续提交任务则会直接走拒绝策略该线程池不会再新建线程来执行任务而是直接走拒绝策略。FixedThreadPool 使用的是默认的拒绝策略即 AbortPolicy则直接抛出异常。但是 workQueue 使用了无界的 LinkedBlockingQueue, 那么当任务数量超过 corePoolSize 后全都会添加到队列中而不执行拒绝策略。SingleThreadExecutorpublic static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueueRunnable()));}作为单一 worker 线程的线程池SingleThreadExecutor 把 corePool 和 maximumPoolSize 均被设置为 1和 FixedThreadPool 一样使用的是无界队列 LinkedBlockingQueue, 所以带来的影响和 FixedThreadPool 一样。CachedThreadPoolCachedThreadPool是一个会根据需要创建新线程的线程池 他定义如下public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueueRunnable());}这个线程池当任务提交是就会创建线程去执行,执行完成后线程会空闲60s,之后就会销毁。但是如果主线程提交任务的速度远远大于 CachedThreadPool 的处理速度则 CachedThreadPool 会不断地创建新线程来执行任务这样有可能会导致系统耗尽 CPU 和内存资源所以在使用该线程池是一定要注意控制并发的任务数否则创建大量的线程可能导致严重的性能问题。20、为什么要使用线程池创建/销毁线程伴随着系统开销过于频繁的创建/销毁线程会很大程度上影响处理效率。线程池缓存线程可用已有的闲置线程来执行新任务(keepAliveTime)线程并发数量过多抢占系统资源从而导致阻塞。运用线程池能有效的控制线程最大并发数避免以上的问题。对线程进行一些简单的管理(延时执行、定时循环执行的策略等)21、生产者消费者问题实例代码用 Object 的 wait()和notify() 实现也可用 ReentrantLock 和 Condition 来完成。或者直接使用阻塞队列。public class ProducerConsumer { public static void main(String[] args) { ProducerConsumer main new ProducerConsumer(); QueueInteger buffer new LinkedList(); int maxSize 5; new Thread(main.new Producer(buffer, maxSize), Producer1).start(); new Thread(main.new Consumer(buffer, maxSize), Comsumer1).start(); new Thread(main.new Consumer(buffer, maxSize), Comsumer2).start(); } class Producer implements Runnable { private QueueInteger queue; private int maxSize; Producer(QueueInteger queue, int maxSize) { this.queue queue; this.maxSize maxSize; } Override public void run() { while (true) { synchronized (queue) { while (queue.size() maxSize) { try { System.out.println(Queue is full); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Random random new Random(); int i random.nextInt(); System.out.println(Thread.currentThread().getName() Producing value : i); queue.add(i); queue.notifyAll(); } } } } class Consumer implements Runnable { private QueueInteger queue; private int maxSize; public Consumer(QueueInteger queue, int maxSize) { super(); this.queue queue; this.maxSize maxSize; } Override public void run() { while (true) { synchronized (queue) { while (queue.isEmpty()) { try { System.out.println(Queue is empty); queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() Consuming value : queue.remove()); queue.notifyAll(); } } } }}作者Fururur来源www.cnblogs.com/Sinte-Beuve