Java并发编程总结 [1] 基础篇(下)

一、把我能想到的写下来:

1.并发与并行的区别

       并发是指在一段时间内,多个线程同”时”执行;一段时间是由许多个单位时间组成的,由于时间片给线程时,其他线程不能拥有时间片而被阻塞,所以同一时刻只能有一个线程在执行。其实并发时线程是交替执行的。
       而并行是指在单位时间内,多个线程同时执行。

(补:
并发是指 在同一个时间段内 多个任务同时都在执行,而且都没有执行结束;
并行是指 在单位时间内多个任务同时在执行。
并发任务强调在一个时间段内同时执行,而一个时间段是由多个单位时间累积而成。)

2.线程安全

       并发时,不同线程对共享资源在进行读写操作,而未进行同步操作,导致的数据脏读等问题。如果只是多线程读取资源,不会出现线程不安全的问题。
 

3.内存可见性

Java并发编程总结 [1] 基础篇(下)
CPU的内存模型:工作内存是各个CPU共享的一个区域,像堆、方法区都在这里,而程序计数器、线程私有的都是在CPU各自的工作内存中的,主内存是处理数据的地方。
Java并发编程总结 [1] 基础篇(下)
Java内存模型:
        当线程对变量进行写入操作时,先会把值写入该线程的一级缓存中,然后再刷新到二级缓存中,而读取值是先到自己的一级缓存中进行读取,如果没有找到该变量,才去二级缓存中。
        内存可见性:线程A对共享变量 r 的修改(更新),对于其他线程都是可见的。 ???
        这两个内存模型有什么关系???
 

4.synchronized

       用于同步块或同步方法。当线程进入 synchronized(对象)修饰的代码块时,如果暂无其他线程拥有该对象的监视器锁,这个线程就会获取到对象的监视器锁。
(监视器锁????:意味着只能有一个线程拥有的权限。
可重入,同一线程可以多次获取该锁,会有计数器 +1,释放锁后计数器 -1。
每个类和对象都会对应一个锁,如果有涉及并发的遍历,该锁就会监视那些变量,否则就什么也不监视。)
       而执行完同步代码块后,锁就会被释放。synchronized 修饰的方法同理,是当前执行这个方法的 对象的 锁。
       synchronized是如何保证”同步“的呢?内存语义:进入同步块之前,会清除变量在一级缓存的值,只能去二级缓存中读取,而写入值时,也是直接写在二级缓存中的。这样也就实现了内存可见性。
        synchronized 既可以实现”原子性“、同步、还可以保证内存可见性。
 

5.ABA问题

        对于共享变量 r=A,如果线程1先将 r 值修改为B,然后切换到线程2 ,线程2将 r 值 修改为A ,线程再切换为 线程1,这时 r 的值是 A,但不是最先的那个”A“了。
  解决 ABA 问题可以用注解 @???
 

6.原子性

        原子性是指语句整体是一起执行的,不能有部分没有被执行。 ???

7.volatile

        volatile 用在共享变量前,表示在读取
        volatile 可以避免指令重排序,(虚拟机有指令优化机制,在不影响执行结果的情况下会进行指令重排 ??? )它的语义:在读取变量之后的命令不能排在读取前面;在写变量之前的命令不能排在写后面。
        能保证内存可见性,但不能保证同步/线程安全。因为内存可见性是对于变量来说,该变量的 操作受影响,而Java并不是每条语句都是是原子性的。比如:
        volatile修饰的变量 i 初值为 0,进行自增操作 i++ ,这一条Java 语句,用javac 编译,通过查看 class 文件??? 它的是有 3 句指令的:
(1)读取 i 的值 。
(2)求 i +1 的值
(3)将 i+1 的值赋给 i。
        比如线程 A 先执行(1),进行 读 的操作,是直接从主内存中取值,这时 i=0,接下来切换到线程 B ,线程 B 先执行 (1),进行 读 的操作,从主内存中取值,i=0,然后执行(2),i+1的值为 1,接下来执行(3),会将1直接刷新到主内存中,不会存在任何缓存中。切换到线程 A 中,这时已经执行过(1)读取操作了,该执行(2)了,I+1的值仍为 1 ,接下来执行(3),会将 1 刷新到主内存中,而经过这俩线程,自增操作的结果 是 ”1“,而不是预期中的 2。
 

8.乐观锁与悲观锁

        悲观锁是指对线程抢占资源持保守态度,认为所有线程都会占据 CPU ,因此在对资源进行写入之前,就要加锁,比如监视器锁机制就是乐观锁。 ???
        乐观锁是任务其他线程并不会抢占 CPU ,所以在写入变量时才加锁。
 

9.自旋锁

        当前线程进入同步块,如果没有持有监视器锁,会被阻塞,后期再就绪、执行,会涉及线程上下文切换,对 CPU 资源造成浪费。而自旋锁是这样的:如果当前没有持有监视器锁,不会被阻塞,而是循环检测,(并不是无限次,是有上限的,比如:10次),直到检测到持有锁,可以执行。
 

10.伪共享

        变量被存储到内存中并不是只有这一个单一变量被存储的,而是一大块的内存行(通常是 2的整数次方幂 位???),因此进行读取时,是以 内存行 为单位进行读取的。这会带来资源的损耗。
       

11.CAS

        compare and swap ,比较并交换,在 CPU 中是个原子操作,有 expect 期望值,update 更新值,将变量与旧值/期望值进行比较,如果值相同,说明没有被其他线程更改过,那么用 update 值进行替换/更新,而如果值不相同,说明被其他线程更改过,维持旧值即可。
        (oldvalue ,offset ,updatevalue )???
        Java 中的 unsafe 类中有很多的 CAS 方法。
       

12.公平锁与非公平锁

        公平锁是线程的优先级是以被阻塞的时间长短为依据的,指越早被阻塞,即阻塞时间越长的线程,越早获得CPU时间片,而非公平锁与被阻塞时间无关。
        可以通过 ???(true)进行设置。
        ReetrantLock实现了公平锁机制。
 

二、补充