同步操作将从 dearHaoGeGe/Ebooks 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
AQS核心思想是如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用。
SynchronousQueue队列是无锁队列,其内部是使用CAS自旋操作保证线程安全。
volatile关键字能够保证数据的可见性,但不能保证数据的原子性。
synchronized关键字既能够保证数据的可见性,又能够保证数据的原子性,即保证资源的同步。
volatile关键字只能用于修饰变量,而synchronized关键字可以修饰方法以及代码块。
ConcurrentHashMap和Hashtable都可以用于多线程的环境,但当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。
HashTable的任何操作都会把整个表锁住,是阻塞的。好处是:总能获取最实时的更新,比如说线程A调用putAll()写入大量数据,期间线程B调用get(),线程B就会被阻塞,直到线程A完成putAll(),因此线程B肯定能获取到线程A写入的完整数据。坏处是所有调用都需要排队,效率较低。
ConcurrentHashMap是设计为非阻塞的。在更新时会局部锁住某部分数据,但不会把整个表都锁住。同步读取操作则是完全非阻塞的。好处是在保证合理的同步前提下,效率很高。坏处是:严格来说,读取操作不能保证反映最近的更新。例如线程A调用putAll()写入大量数据,期间线程B调用get(),则只能get()到目前为止已经顺利插入的部分数据。
JDK8的版本,与JDK6的版本有很大差异。实现线程安全的思想也已经完全变了,它摒弃了Segment(分段锁)的概念,而是启用了一种全新的方式实现,利用CAS算法。它沿用了与它同时期的HashMap版本的思想,底层依然由数组+链表+红黑树的方式思想,但是为了做到并发,又增加了很多复制类,例如TreeBin、Traverser等对象内部类。CAS算法实现无锁化的修改至操作,他可以大大降低锁代理的性能消耗。这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的一个变量值是否相等,如果相等,则接受你指定的修改的值,否则拒绝你的操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。
newCachedThreadPool
创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,如果没有可回收线程,则新建线程。
newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor
创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定执行。
在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,但是不能随意重排序,不是不是想怎么排序就怎么排序,需要满足以下两个条件:
1)在单线程环境下不能改变程序运行的结果; 2)存在数据依赖关系的不允许重排序
需要注意的是:重排序不会影响单线程环境的执行结果,但是会破坏多线程的执行语义。
协程(Coroutine):是单线程下的并发,又称微线程,纤程。简单理解就是线程中的线程。
优点
轻量,创建成本小,降低了内存消耗
用户态调度,减少了 CPU 上下文切换的开销,提高了 CPU 缓存命中率
减少同步加锁,提高了性能
可以用同步思维写异步代码
缺点
在协程执行中不能有阻塞操作,否则整个线程被阻塞
不擅长处理 CPU 密集型
适用场景
高性能要求,考虑牺牲公平性换取吞吐量
IO 密集型任务
Generator 式的流式计算
1)如果是方法内定义的局部变量,因为每个方法栈是线程私有的,所以一定是线程安全的。
2)如果是类的成员变量,++i就是非线程安全的,这是因为++i相当于i=i+1。
实现线程安全可以使用synchronize关键字修饰提供同步或使用AtomicInteger原子操作类,因++i同步体比较小,可以使用自旋CAS的AtomicInteger类实现线程安全。
注意:因为volatile只能保证可见性,不能保证原子性,所以volatile不能解决这个线程安全存在的问题。
AtomicInteger保证线程安全
在JDK1.5版本之后,Java程序才可以使用CAS操作,该操作由sun.misc.Unsafe类中compareAndSwapInt()和compareAndSwapLong()等几个方法包装提供,虚拟机编译出来的结果就是一条平台相关的处理器CAS指令。
Unsafe类中getUnsafe()方法中限制了只有启动类加载器Bootstrap ClassLoader加载的Class才能访问它,因此Unsafe类不提供给用户程序调用,如果不使用反射机制的话只能通过其他的Java API来使用它,比如JUC包中AtomicInteger类,其中incrementAndGet()等方法都使用了Unsafe类的CAS操作。
JDK1.8源码如下:
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
通过源码可以看出AtomicInteger类是通过自旋CAS实现了线程安全的数量变化。
不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。
不可变对象的类即为不可变类(Immutable Class)。Java平台类库中包含许多不可变类,如String、基本类型的包装类、BigInteger和BigDecimal等。
不可变对象天生是线程安全的。它们的常量(域)是在构造函数中创建的。既然它们的状态无法修改,这些常量永远不会变。
不可变对象永远是线程安全的。
只有满足如下状态,一个对象才是不可变的。
1)它的状态不能在创建后再被修改; 2)所有域都是final类型; 3)被正确创建(创建期间没有发生this引用的逸出)。
举例来说明锁的可重入性
public class UnReentrant{
Lock lock = new Lock();
public void outer(){
lock.lock();
inner();
lock.unlock();
}
public void inner(){
lock.lock();
//do something
lock.unlock();
}
}
outer中调用了inner,outer先锁住了lock,这样inner就不能再获取lock。其实调用outer的线程已经获取了lock锁,但是不能在inner中重复利用已经获取的锁资源,这种锁即称之为 不可重入可重入就意味着:线程可以进入任何一个它已经拥有的锁所同步着的代码块。
synchronized、ReentrantLock都是可重入的锁,可重入锁相对来说简化了并发编程的开发。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。