目录
多线程编程的三个核心概念
原子性:很经典的银行转账问题,要么全成功要么全失败
可见性:一个线程对共享变量修改,其他线程能够立刻看到(关于cpu存储的方式,虽然写进了cpu中,但不会立刻写进主内存,所以导致不在同一cpu上执行的线程,访问该变量读取到的是旧数据,不是第一个线程更新后的数据)
顺序性:程序执行的顺序按照代码的先后顺序执行
Java如何解决多线程并发问题
如何保证原子性
锁和同步:
使用锁可以保证在同一时间只有一个线程能拿到锁,也就是说同一时间只有一个线程可以执行拿到锁和释放锁之间的代码。
使用同步方法或者同步代码块,使用非静态同步方法锁住的是当前实例对象,使用静态同步方法锁住的是class对象,使用静态代码块锁住的是synchronzied后面括号里锁的对象。
使用锁和使用synchronized是一种牺牲性能的方法。
CAS操作
Java中提供了对应的原子操作类来实现该操作,并保证原子性,其本质是利用了CPU级别的CAS指令。
如何保证可见性
使用volatile关键字,被volatile修饰的变量会保证对该变量修改的值会存储到主存中,其他缓存中的该变量的值失效,所以必须去主存中才能取到这个变量的值。
如何保证顺序性
编译器和处理器对指令进行重新排序时,会保证重新排序后的执行结果和代码顺序执行的结果一致,所以重新排序过程并不会影响单线程程序的执行,却可能影响多线程程序并发执行的正确性。
Java中可通过volatile在一定程序上保证顺序性,另外还可以通过synchronized和锁来保证顺序性。
synchronized和锁保证顺序性的原理和保证原子性一样,都是通过保证同一时间只会有一个线程执行目标代码段来实现的。
除了从应用层面保证目标代码段执行的顺序性外,JVM还通过被称为happens-before原则隐式地保证顺序性。两个操作的执行顺序只要可以通过happens-before推导出来,则JVM会保证其顺序性,反之JVM对其顺序性不作任何保证,可对其进行任意必要的重新排序以获取高效率。
一些问题
1.多线程创建方式:继承thread类、实现runnable接口/通过实现Callable接口
2.wait和sleep方法的区别:
wait会释放锁,用于线程交互。sleep不会释放锁,用于暂停执行。
3.synchronized和volatile
volatile的作用:一个共享变量被volatile修饰后 1)保证了可见性 2)禁止指令重排
1)被volatile修饰后值要从主存中取,synchronized锁定当前变量只有该线程可以访问,其他线程被阻塞
2)volatile仅能使用在变量级别 ,synchronized可以使用在方法 变量 类上
3)volatile仅能实现变量的可见性 不能保证原子性,synchronized都可以
4)volatile不会造成线程阻塞,synchronize可能会造成线程阻塞
5)volatile标记的变量不会被编译优化(指的是jvm指令重排序),synchronized可以被编译器优化
4.什么是线程池,如何使用?
线程池是用来管理多个线程的,
四种线程池 newCachedThreadPool(); newFixedThreadPool();newScheduledThreadPool();newSingleThreadExecutor();
之后使用execute方法就可以使用线程池了。
5.线程池的好处
6.启动策略
7.如何避免死锁
加锁的顺序,加锁的时限