Java并发知识点总结


1.Thread类的sleep方法和Object类wait方法对比.
sleep让当前线程暂停指定时间,不释放锁,结束后回到就绪状态.
wait方法导致当前线程放弃对象的锁,线程暂停,进入等待池,调用notify时,唤醒并进入等锁池.

2.sleep()方法和yield()方法区别
4个
    sleep将方法让个其他线程不考虑线程的优先级,yield会优先个优先级高的线程.
    yield执行直接进入就绪状态.
    sleep抛出异常(interrupted exception),yield没有异常
    sleep移植性好.


3.编写多线程的三个方法
  implements runnable            需要重写run()
  extends Thread                     需要重写run()
  implements callable              与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。

4.线程ExectorService有四种
线程池: 为了避免系统频繁的创建和销毁线程,我们可以将创建的线程进行复用。数据库中的数据库连接池也是此意。
  • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。定时执行
  • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

5.线程池构造方法

参数含义


**尽量避免用Executors创建线程池


6.线程池shutdown和shutdownnow的区别
shutdown会让正在执行的线程执行完之后在进行停止,shutdownnow会立即停止.


7.submit() ,execute() Future比较
使用 submit(Runnable task)  的时候, 错误的堆栈信息跑出来的时候会被内部捕获到,所以打印不出来具体的信息让我们查看 ,解决的方法有如下两种:
1、使用execute()代替submit();2.Future


8.线程状态图



9. volatile关键字在Java中有什么作用?

用来保证可见性和有序性。不具有原子性。
被关键了volatile修饰的变量,可以保证修改后的数据立即更新到主存,其他线程直接到主存中去取。
volatile通过静止了指令重排来在一定程度上保证有序性。
应用场景:
volatile在某些情况下优于synchronized
1.对变量的写操作不依赖于当前值。(简单来说不存在原子性操作)
2.该变量没有包含在其他变量的不变式中
应用:
1.状态标记量
2.双重校验

10. 什么是ThreadLocal?

ThreadLocal用于 创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。
每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。在 ThreadLoc al例子这篇文章中你可以看到一个关于ThreadLocal的小程序。
ThreadLocal类用于创建一个线程本地变量
在Thread中有一个成员变量ThreadLocals,该变量的类型是ThreadLocalMap,也就是一个Map,它的键是threadLocal,值为就是变量的副本。通过ThreadLocal的get()方法可以获取该线程变量的本地副本,在get方法之前要先set,否则就要重写initialValue()方法。
ThreadLocal的使用场景:
        数据库连接:在多线程中,如果使用懒汉式的单例模式创建Connection对象,由于该对象是共享的,那么必须要使用同步方法保证线程安全,这样当一个线程在连接数据库时,那么另外一个线程只能等待。这样就造成性能降低。如果改为哪里要连接数据库就来进行连接,那么就会频繁的对数据库进行连接,性能还是不高。这时使用ThreadLocal就可以既可以保证线程安全又可以让性能不会太低。但是ThreadLocal的缺点时占用了较多的空间。


11. 什么是Java Timer类?如何创建一个有特定时间间隔的任务?

java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可以用安排一次性任务或者周期任务。

12.线程和进程
进程:是程序的一次执行,动态且占用资源, 是系统进行资源分配和调度的基本单位.
线程:是一个微量实体, 是程序执行流的最小单元。 是系统独立调度和分派CPU的基本单位.

13.sychronized可重入锁,为什么要引入重入锁
当一个线程得到一个对象的锁后,在该锁里执行代码的时候可以再次请求该对象的锁时可以再次得到该对象的锁。 自己可以获取自己的内部锁

最大的作用是 避免死锁。 假如有一个场景:用户名和密码保存在本地txt文件中,则登录验证方法和更新密码方法都应该被加synchronized,那么当更新密码的时候需要验证密码的合法性,所以需要调用验证方法,此时是可以调用的
可重入锁的其他特性:父子可继承性

14.sychronized其他特性
(1)出现异常时,锁自动释放
(2)将任意对象作为监视器(对象锁)
(3)单例模式-双重校验锁的


15.线程同步的方法
1.sychronized同步方法
2.sychronized同步块
3.ReentrantLock 重入锁

16.ReentrapLock
private Lock lock;
public int func(int value) {
   try {
       lock.lock();
       // ...
   } finally {
      lock.unlock();
   }
}
ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁,相比于 synchronized,它多了一些高级功能:
1.等待可中断  :( 当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,执行其他任务)
2.可实现公平锁
     公平锁是指多个线程在等待同一个锁时,必须 按照申请锁的时间顺序来依次获得锁 ;而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁。synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。
3.锁绑定多个条件 
*Lock和Sychronized的区别:
1. synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
2. 在新版本的 JDK 中对 synchronized 进行了很多优化,例如自旋锁等。性能基本持平,优先考虑Sychronized

17.BlockingQueue
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
  • FIFO 队列 :LinkedBlockingQueue、ArrayListBlockingQueue(固定长度)
  • 优先级队列 :PriorityBlockingQueuejava.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
提供take()和put()方法 
*是线程安全的
用阻塞队列写生产消费模型
// 生产者 public class Producer implements Runnable {
private BlockingQueue< String > queue;

public Producer ( BlockingQueue< String > queue ) {
this . queue = queue;
}

@Override
public void run () {
System . out . println( Thread . currentThread() . getName() + " is making product. " );
String product = " Made By " + Thread . currentThread() . getName();
try {
queue . put(product);
} catch ( InterruptedException e) {
e . printStackTrace();
}
}
}
// 消费者 public class Consumer implements Runnable {
private BlockingQueue< String > queue;

public Consumer ( BlockingQueue< String > queue ) {
this . queue = queue;
}

@Override
public void run () {
try {
String product = queue . take();
System . out . println( Thread . currentThread() . getName() + " is consuming product. " + " ( " + product + " ) " );
} catch ( InterruptedException e) {
e . printStackTrace();
}
}
}
// 客户端 public class Client {
public static void main ( String [] args ) {
BlockingQueue< String > queue = new LinkedBlockingQueue<> ( 5 );
for ( int i = 0 ; i < 2 ; i ++ ) {
new Thread ( new Consumer (queue), " Consumer- " + i) . start();
}
for ( int i = 0 ; i < 5 ; i ++ ) {
// 只有两个 Product,因此只能消费两个,其它三个消费者被阻塞
new Thread ( new Producer (queue), " Producer- " + i) . start();
}
for ( int i = 2 ; i < 5 ; i ++ ) {
new Thread ( new Consumer (queue), " Consumer- " + i) . start();
}
}
}
// 运行结果
Producer-0 is making product.
Consumer-0 is consuming product.( Made By Producer-0 )
Producer-1 is making product.
Consumer-1 is consuming product.( Made By Producer-1 )
Producer-2 is making product.
Producer-3 is making product.
Producer-4 is making product.
Consumer-2 is consuming product.( Made By Producer-2 )
Consumer-3 is consuming product.( Made By Producer-3 )
Consumer-4 is consuming product.( Made By Producer-4 )
  
18.关于J.U.C, Java.util.concurrent 
CAS(比较交换策略)
1.CountDownLatch :用来控制一个线程等地多个线程(cnt递减)
2.CyclicBarrier: 用来控制多个线程互相等待,只有多个线程都到达时,线程才会继续执行.(cnt递增)
3.Semaphore:  就是操作系统中的信号量,可以控制互斥资源的访问数.
4.FutureTask:  接口,继承自Runnable.Futrure,可以封装任务,而且有返回值.(futuretask.get() )
5.BlockingQueue<> : 阻塞队列,两种实现
    ① FIFO  : LinkedBlockingQueue  ArrayListBlockingQueue
    ② 优先级队列:PriorityBlockingQueue
6.ForkJoin :主要用于并行计算,把大任务化成小任务进行计算

* Java中CyclicBarrier 和 CountDownLatch有什么不同?

CyclicBarrier 和 CountDownLatch 都可以用来让一组线程等待其它线程。与 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。 点此查看更多信息和示例代码

19.如何定时完成一项任务
1.用Thread类里的sleep方法
2.Timer类
3. java.util.concurrent里的 ScheduledExecutorService

20.JVM中Synchronized的底层实现
Sychronized底层是由JVM实现.本质是一个对象监视器(monitor)
任何对象都有一个 monitor 与之关联,当且一个monitor 被持有后,它将处于锁定状态。线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权,即尝试获得对象的锁。

synchronized用的锁是存放在对象头中的
 对象头:(2word)
Mark Word: 存储对象的hashCode或锁信息等。
Klass Pointer: 存储到对象类型数据的指针

1.对于普通方法,锁是当前实例对象。
2.对于静态同步方法,锁是class对象。
3.同步方法块,锁是synchronized后面括号里的对象。


21.Synchronized的锁优化
锁优化包括: 如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等
锁主要存在四中状态,依次是: 无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态  
可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。

自旋锁
目的: 频繁的阻塞和唤醒对CPU来说是一件负担很重的,有些场景,对象锁的状态持续时间短
实现过程: 自旋锁让线程等待一会,去执行一个无意义的循环.不会被立即挂起.避免状态切换带来的资源开销.
缺点: 对处理器核数有要求,占用处理器时间,遇到线程长时间不释放锁,对性能浪费.
优化: 自旋 默认次数为10次,可以通过参数-XX:PreBlockSpin来调整;(不好)

适应性自旋锁:
自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。

消除锁:
当JVM检测到不共享数据竞争,这时会对同步锁进行锁消除。锁消除的依据是逃逸分析的数据支持

锁粗化:
就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。 

轻量级锁:
在多没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁,则会尝试获取轻量级锁,

偏向锁:
偏向锁主要解决无竞争下的锁性能问题, 一旦线程第一次获得了监视对象,之后让监视对象“偏向”这个线程

重量级锁:
重量级锁通过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态到内核态的切换,切换成本非常高。


22.系统内存模型相关概念
高速缓存cache: cpu在寄存器上执行指令时候,会涉及到数据的读取和写入,由于数据是存放在主存里,而且 由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多. 因此在CPU里面就有了高速缓存。 当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。

通过cache缓存在多线程会出现缓存不一致问题.

为解决缓存不一致问题,硬件层面提出的解决方案:
1.在总线加Lock#锁
(只能有一个cpu来操作变量,效率低)
2.缓存一致性协议MESL
( 保证了 每个缓存中使用的共享变量的副本是一致的。)

23.JAVA内存模型
JMM : 来屏蔽各个硬件平台和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。
JAVA内存模型 也会出现缓存不一致和指令重排问题.

Java内存模型分为 : 主内存和工作内存 (类似主存和高速缓存关系)
 
如何保证并发特性?
  • 原子性: 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作即这些操作是不可被中断的,要么执行,要么不执行。
      
        
* i++不满足原子性操作,应为i++需要先从主存读取i值,再在工作内存+1,再将新的值写入主存。
* 内存模型只有简单的读取,赋值才是原子性操作,实现更大范围的操作,需要用Synchronized或Lock或AtomicInteger来实现。 
*在32位操作系统中,对64位数据进行读写赋值操作也不是原子性的

  • 可见性:一个线程修改了共享变量的值,其他线程可以立刻得知这个修改。普通共享变量不能实现可见性。因为不知道什么时候将修改的值写入主存。
*Java提供volatile来保证可见性(被v修饰的变量会立刻更新到主存,其他线程直接去主存里取)
*Synchronized和Lock也可以保证。(会在释放锁之前将数据更新到主存)
        
  • 有序性: 在Java内存模型中,允许编译器和处理器进行指令重排序,但是重排序过程不会影响到单线程程序的执行(重排是会考虑指令之间数据依赖性),却会影响到多线程并发执行的正确性。
*java内存模型具有先天有序性。(通过happens-before原则)
*通过volatile来保证有序性。( 通过添加内存屏障的方式来禁止指令重排)
*通过Synchronized和Lock也能保证

24.先行发生原则(happens-before)
1.程序次序原则
2.管城锁定原则
3.volitle变量原则
4.传递原则
5.线程启动原则
6.线程终止原则
7.线程中断原则
8.对象终结原则

总结待补充ing...

猜你喜欢

转载自blog.csdn.net/github_39336148/article/details/80329326