网站建设合同或方案书,手机链接ppt在哪个网站做,北京seo代理计费,在线海报生成并发编程
1.synchronized是什么#xff1f;
synchronized是Java中的一个关键字#xff0c;主要是为了解决多个线程访问共享资源的同步性#xff0c;可以保证被它修饰的代码块或方法在任何时间至多只有一个线程执行。
2.synchronized的进化史?
在早期Java版本中#xf…并发编程
1.synchronized是什么
synchronized是Java中的一个关键字主要是为了解决多个线程访问共享资源的同步性可以保证被它修饰的代码块或方法在任何时间至多只有一个线程执行。
2.synchronized的进化史?
在早期Java版本中synchronizd属于重量级锁性能低下。
synchronized 在 JDK 1.6 之后引入了锁优化可以随着多线程竞争激烈程度的不同而选择合适的锁策略。
当没有线程竞争的时候是无锁的状态当只有一个线程竞争的时候是偏向锁当有多个线程竞争的时候撤销偏向锁成为轻量级锁当轻量级锁 CAS 次数太多的时候就会撤销轻量级锁称为重量级锁
轻量级锁和重量级锁的区别
在轻量级锁下线程是不进行阻塞的线程拿不到锁会不断CAS自旋直到拿到锁为止这样会充分利用CPU资源并在锁释放瞬间确保其他锁可以拿到但是如果线程竞争激烈线程就会不断CAS这也是我们不想看到的。
重量级锁下拿不到锁的线程会被阻塞阻塞线程涉及到用户态到内核态的切换这也是很大的消耗。
3.在高并发场景下并发度肯定是比较高的不建议使用 synchronized 的原因主要有以下几点
由于并发度比较高因此 synchronized 一定会升级到重量级锁但是重量级锁的性能是不太高的因为线程要阻塞再唤醒需要用户态和内核态之间切换synchronized 没有读写锁优化synchronized 不能对线程唤醒也就是你线程如果获取不到锁的话会一直阻塞
4.synchronized怎么用
修饰实例方法给当前对象实例加锁要获得当前对象实例的锁
synchronized void method() {}修饰静态方法
给当前类加锁会作用于类的所有对象实例进入同步代码块前要获得当前 class 的锁
这是因为静态成员不属于任何一个实例对象归整个类所有不依赖于类的特定实例被类的所有实例共享。
synchronized static method() {}修饰代码块锁指定对象/类 synchronized(object) 表示进入同步代码库前要获得 给定对象的锁。synchronized(类.class) 表示进入同步代码前要获得 给定 Class 的锁
synchronized(this) {// 业务代码
}5.synchronized的实现原理
为什么说synchronized是基于对象实现的先掌握Java对象在堆中的存储。
一个Java对象存储在堆上分别是对象头、对象实例数据、对齐填充。
对象头展开是MarkWord、ClassMetadataAddress、数组长度
其中MarkWord记录了两部分信息
第一部分用于存储对象自身运行时数据如哈希码、GC分代年龄另一部分存储了锁信息
进入同步代码块调用monitorEnter方法计数器加一离开同步代码块调用monitorExit方法计数器减一
当计数器为0可以拿到锁同时可以释放锁
6.线程中断与synchronized
//中断线程实例方法
public void Thread.interrupt();//判断线程是否被中断实例方法
public boolean Thread.isInterrupted();//判断是否被中断并清除当前中断状态静态方法
public static boolean Thread.interrupted();7.volatile
在多线程并发编程中synchronized和volatile都扮演着重要的角色volatile是轻量级的 synchronized它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程 修改一个共享变量时另外一个线程能读到这个修改的值。如果volatile变量修饰符使用恰当的话它比synchronized的使用和执行成本更低因为它不会引起线程上下文的切换和调度。本文将深入分析在硬件层面上Intel处理器是如何实现volatile的通过深入分析帮助我们正确地使用volatile变量。
volatile是Java当中的关键字
它的作用是禁止指令重排序和保证变量的可见性
但它不能保证原子性
保证变量的可见性当一个线程去修改被volatile修饰的变量时可以保证修改之后的结果立刻刷新到主存上当一个线程去读取被volatile修饰的变量时必须强制从主存中读取禁用缓存禁止指令重排序防止JVM编译源码生成class时使用重排序
1Lock前缀指令会引起处理器缓存回写到内存。Lock前缀指令导致在执行指令期间声 言处理器的LOCK#信号。在多处理器环境中LOCK#信号确保在声言该信号期间处理器可以 独占任何共享内存[2]。但是在最近的处理器里LOCK信号一般不锁总线而是锁缓存毕 竟锁总线开销的比较大。在8.1.4节有详细说明锁定操作对处理器缓存的影响对于Intel486和 Pentium处理器在锁操作时总是在总线上声言LOCK#信号。但在P6和目前的处理器中如果 访问的内存区域已经缓存在处理器内部则不会声言LOCK#信号。相反它会锁定这块内存区 域的缓存并回写到内存并使用缓存一致性机制来确保修改的原子性此操作被称为“缓存锁 定”缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。
2一个处理器的缓存回写到内存会导致其他处理器的缓存无效。IA-32处理器和Intel 64处 理器使用MESI修改、独占、共享、无效控制协议去维护内部缓存和其他处理器缓存的一致 性。在多核处理器系统中进行操作的时候IA-32和Intel 64处理器能嗅探其他处理器访问系统 内存和它们的内部缓存。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的 缓存的数据在总线上保持一致。例如在Pentium和P6 family处理器中如果通过嗅探一个处理 器来检测其他处理器打算写内存地址而这个地址当前处于共享状态那么正在嗅探的处理 器将使它的缓存行无效在下次访问相同内存地址时强制执行缓存行填充
8.ReentrantLock
ReentrantLock 是基于 AQS 抽象同步队列实现的。
ReentrantLock基于AQS在并发编程中它可以实现公平锁或非公平锁来对共享资源进行同步
同时和synchronized一样ReentrantLock支持可重入除此之外ReentrantLock在调度上更灵活支持更多丰富的功能
ReentrantLock的lock方法
在进入lock方法后发现内部调用了sync.lock()方法。
Sync这个类是一个抽象类它有两个实现类Sync继承了AQS
Sync类abstract static class Sync extends AbstractQueuedSynchronizer {NonFairSyncstatic final class NonfairSync extends Sync {FairSyncstatic final class FairSync extends Sync {使用公平锁参数加true非公平锁直接无参构造就行了:public ReentrantLock(boolean fair) {sync fair ? new FairSync() : new NonfairSync();}公平锁
获取锁时看有没有线程排队有排队就不去竞争线程获取锁的顺序与入阻塞队列的顺序一致不会有插队行为。
非公平锁
不管有没有线程排队先尝试获取锁资源获取不到再去排队但是有插队行为。
分析AQS
Sync继承了AQS我们来分析一下AQS
AQS的数据结构是一个双向链表/队列它里面有个Node类
abstract static class Node {volatile Node prev; // initially attached via casTailvolatile Node next; // visibly nonnull when signallableThread waiter; // visibly nonnull when enqueuedvolatile int status; // written by owner, atomic bit ops by others// methods for atomic operationsfinal boolean casPrev(Node c, Node v) { // for cleanQueuereturn U.weakCompareAndSetReference(this, PREV, c, v);}final boolean casNext(Node c, Node v) { // for cleanQueuereturn U.weakCompareAndSetReference(this, NEXT, c, v);}final int getAndUnsetStatus(int v) { // for signallingreturn U.getAndBitwiseAndInt(this, STATUS, ~v);}final void setPrevRelaxed(Node p) { // for off-queue assignmentU.putReference(this, PREV, p);}final void setStatusRelaxed(int s) { // for off-queue assignmentU.putInt(this, STATUS, s);}final void clearStatus() { // for reducing unneeded signalsU.putIntOpaque(this, STATUS, 0);}private static final long STATUS U.objectFieldOffset(Node.class, status);private static final long NEXT U.objectFieldOffset(Node.class, next);private static final long PREV U.objectFieldOffset(Node.class, prev);}ReentrantLock是如何拿到锁的无论是公平还是非公平
线程通过CAS的方式能够把AQS中的state变量从0变为1就代表拿到锁了
9.让线程进入阻塞有哪些方法
使用 Thread.sleep(long millis)通过调用 Thread.sleep() 方法线程可以暂时挂起一段时间进入阻塞状态。在指定的时间过后线程会重新进入就绪状态。使用 Object.wait()线程可以通过调用 Object.wait() 方法进入等待状态等待其他线程调用相同对象的 notify() 或 notifyAll() 方法来唤醒它。使用 Thread.join()在一个线程中调用另一个线程的 join() 方法可以使调用线程等待被调用线程执行完毕进入阻塞状态直到被调用线程执行完毕。调用 Lock 接口中的 lock 方法通过 Lock 接口获取锁时若锁已经被其他线程持有线程将进入阻塞状态直到获取到锁。使用 BlockingQueueBlockingQueue 是一个用于线程间通信的阻塞队列当队列为空或满时线程在插入或获取元素时会被阻塞。I/O 操作执行阻塞式的 I/O 操作如读取文件、网络通信等会使线程进入阻塞状态直到操作完成。
这些方法允许线程进入阻塞状态并且在满足特定条件或时间后线程可以重新进入就绪状态等待调度器再次分配执行。
10.控制并发线程数的Semaphore
《[中文]Java并发编程的艺术》
Semaphore信号量是用来控制同时访问特定资源的线程数量它通过协调各个线程以保证合理的使用公共资源。
Semaphore可以用于做流量控制特别是公用资源有限的应用场景比如数据库连接。假如有一个需求要读取几万个文件的数据因为都是IO密集型任务我们可以启动几十个线程 并发地读取但是如果读到内存后还需要存储到数据库中而数据库的连接数只有10个这时我们必须控制只有10个线程同时获取数据库连接保存数据否则会报错无法获取数据库连接。这个时候就可以使用Semaphore来做流量控制如代码所示。
public class SemaphoreTest {
private static final int THREAD_COUNT 30;
private static ExecutorServicethreadPool Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s new Semaphore(10);
public static void main(String[] args) {for (inti 0; i THREAD_COUNT; i) {threadPool.execute(new Runnable() {Overridepublic void run() {try {s.acquire();System.out.println(save data);s.release();} catch (InterruptedException e) {}}});}threadPool.shutdown();}
}
在代码中虽然有30个线程在执行但是只允许10个并发执行。Semaphore的构造方法 Semaphoreint permits接受一个整型的数字表示可用的许可证数量。Semaphore10表示允 许10个线程获取许可证也就是最大并发数是10。Semaphore的用法也很简单首先线程使用 Semaphore的acquire()方法获取一个许可证使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。
Semaphore还提供一些其他方法具体如下。
**·intavailablePermits()**返回此信号量中当前可用的许可证数。
**·intgetQueueLength()**返回正在等待获取许可证的线程数。
**·booleanhasQueuedThreads()**是否有线程正在等待获取许可证。
**·void reducePermitsint reduction**减少reduction个许可证是个protected方法。
**·Collection getQueuedThreads()**返回所有等待获取许可证的线程集合是个protected方 法
中当前可用的许可证数。
**·intgetQueueLength()**返回正在等待获取许可证的线程数。
**·booleanhasQueuedThreads()**是否有线程正在等待获取许可证。
**·void reducePermitsint reduction**减少reduction个许可证是个protected方法。
**·Collection getQueuedThreads()**返回所有等待获取许可证的线程集合是个protected方 法