本文共 3156 字,大约阅读时间需要 10 分钟。
目录: 1、多线程介绍 2、线程运行流程 3、线程安全 4、synchronized、volatile、锁 5、CAS 介绍 一、多线程相关介绍 1、什么是多线程,多线程的优劣? 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。 可以提高 CPU 的利用率。在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待, 这样就大大提高了程序的效率。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。 2、什么是线程,什么是进程? 1) 进程,一个在内存运行中应用程序,每个进程有自己独立内存空间,一个进程可以有多个线程。比如,qq.exe。 2) 线程,进程中的一个执行任务,负责当前进程中程序执行,一个进程至少一个线程或多个,多个线程可以共享内存空间。3、多线程时如何指定线程数量
1)cup密集型,即程序主要以计算运行等为主,设定线程数量等于cpu核数+1。 2) io密集型,即程序以一些io操作为主,这个就要考虑io空闲比例是多少,比如是90%,那么就设置为cup核数/(1-90%)。 3) 兼顾,综合上面二个计算方式。 二、线程运行状态 1、线程的运行状态 1)创建: new Thread(),创建一个线程。 2) 可运行(runnable):线程对象创建后,当调用线程对象的 start()方法,等待cup调度,获取cpu的使用权。 3) 运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。 4) 消亡(dead):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。 5) 阻塞状态: 分为同步阻塞(锁池等待锁),等待阻塞(等待池等待-释放锁),其他阻塞(不释放锁)。 同步阻塞:线程在获取锁时失败,锁被其他线程占用,线程就进入锁池,线程会进入同步阻塞状态。 等待阻塞:运行状态中的线程执行 wait()方法,该线程放入等待队列中,此时就进入到等待阻塞状态。 其他阻塞:leep(),join, io停顿 线程会进入到阻塞状态,当上列三个操作完成,线程又转入就绪状态。2、sleep() 和 wait() 有什么区别? 两者都可以暂停线程的执行,sleep() 不释放锁;wait() 释放锁,sleep是Thread静态方法,wait是Object方法。 Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。 3、notify() 和 notifyAll() 有什么区别? notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则 留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。
三、线程安全
1、什么是线程安全: 当【多个线程】访问一个类时,这个类【不需要额外协调】,这个类始终都能够表现正确的行为,那么这 个类就是线程安全的。显然只有资源竞争时才会导致线程不安全,因此【无状态对象永远是线程安全】的。 2、并发编程三要素是什么?在 Java 程序中怎么保证多线程的运行安全? 原子性:原子,即一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。(synchronized) 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。(synchronized,volatile) 有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)(synchronized,volatile)synchronized保证了原子性,那么如何保证可见性?在开始执行时会加锁,执行完成后会进行解锁,但在一个变量解锁之前,必须先把此变量
同步回主存中,这样解锁后,后续其它线程就可以访问到被修改后的值,从而保证可见性。 volatile:关键字就是保证可见性,以及指令重排序,即有序性。 四、synchronized、volatile、锁 (1)synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。 (2)volatile 提供多线程共享变量可见性和禁止指令重排序优化。 (3)CAS 是基于冲突检测的乐观锁(非阻塞) 1、volatile关键字 概念:volatile来保证可见性和禁止指令重排。 volatile 提供happens-before 的保证,确保一个线程的修改能对其他线程 是可见的。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时, 它会去内存中读取新值。 使用规则: 1)运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。 2)变量不需要与其他状态变量共同参与不变约束 范例:一般用作状态标记使用。(单例懒汉的null判断等) 2、synchronized关键字 jdk中用来加锁的关键字,锁住一个方法或者代码块,锁对象如果是实例方法就是实例对象,是静态方法就是class,代码块为代码块中指定的 对象。jdk1.6之后,该关键字被进行了很多的优化,锁可以从偏向锁、轻量级锁(自旋锁、自旋自适应锁)、重量级锁 1) 偏向锁,即锁会偏向于第一个获得它的线程,如果一直没有其他线程来竞争,那么持有偏向锁的线程将永远不需要进行同步操作。 2) 轻量级锁,偏向锁有其他线程来竞争锁就升级成轻量级锁-自旋。所谓自旋,就是指当有另外一个线程来竞争锁时,这个线程会在原地循环等待, 而不是把该线程给阻塞,直到那个获得锁的线程释放锁之后,这个线程就可以马上获得锁的。 3) 重量级锁,轻量级锁膨胀之后,就升级为重量级锁了。 3、锁按功能特点可以分为--可重入锁、公平锁/非公平锁、独占锁/共享锁 1) 可重入锁,synchronized的锁是可重入的,即已经获得锁去执行锁保护的另外一个方法还是可以执行的,原理利用 锁对象维护着一个记录着被锁次数的计数器,当同一个线程再次获得该对象的锁的时候,计数器自增。这个再次操作释放 时,计数器自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁。 2) 公平锁/非公平锁 公平锁: 是指多个线程按照申请锁的顺序来获取锁,类似于排队,FIFO规则 非公平锁: 是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象。 3) 独占锁/共享锁 独占锁: 指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronize而言都是独占锁。 共享锁: 指该锁可被多个线程所持有。 五、cas CAS 是 compare and swap 的缩写,即我们所说的比较交换。 cas 是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将 资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一 种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加 version 来获取数据, 性能较悲观锁有很大的提高。 CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面 的值和 A 的值是一样的,那么就将内存里面的值更新成 B。CAS是通过无限循环来获取数据的 ,若果在第一轮循环中,a 线程获取地址里面的值被b 线程修改了,那么 a 线程需要自旋,到 下次循环才有可能机会执行。转载地址:http://ueuni.baihongyu.com/