一. 基本概念
1.并行 多个CPU实例或是多台机器同时执行一段处理逻辑,是真正的同时
2.并发 通过CUP调度算法,让用户看上去同时去执行,实际上从CPU操作层面并不是真正的同时。并发往往需要公共的资源,对公共资源的处理和线程之间的协调是并发的难点
3.进程和线程区别
(1) 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
(2) 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
(3) 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信
号等),某进程内的线程在其他进程不可见;
(4). 调度和切换:线程上下文切换比进程上下文切换要快得多
4.
(1)、新建(New):线程对象被创建时,它只会短暂地处于这种状态。此时它已经分配了必须的系统资源,并执行了初始化。例如,Thread thread = new Thread()。调用start()方法之前
(2)、就绪(Runnable):称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
(3)、运行(Running):线程获取CPU权限进行执行。注意:线程只能从就绪状态进入运行状态。
(4)、阻塞(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态
(5)、死亡(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期
二 .线程的创建
1.继承Thread类
说明 : 实质是也是实现了runnable接口;Thread.java类中的start()方法通知”线程规划器”,此线程准备就绪,等待调用run方法;如果调用了run方法,就是同步执行效果。那么这个线程不会交给”线程规划器”来处理; Start()方法执行了不代表线程就启动了,只是通知线程规划器,等待调用run()方法
2.实现Runnable接口
三 .线程安全
1.线程安全
获得的实例变量的值是经过同步处理的,不会出现脏读的现象;方法中的变量不存在非线程安全问题,因为方法内部的变量是私有的。
2.非线程安全
多个线程对同一个对象的实例变量对象进行并发访问,产生的后果是脏读,取到的数据被修改了
3.sychronized方法解决非线程安全
(1)synchronized方法是用synchronized修饰方法,这是一种粗粒度锁;这个同步方法(非static方法)无需显式指定同步监视器,同步方法的同步监视器是this,也就是调用该方法的对象
(2) 对其他synchronized方法的调用阻塞;同一时间只有一个线程可以执行synchronized同步方法中的代码;
说明 : 能实现方法同步,两个线程共享同一个实例;synchronized取得的锁是对象锁,一个业务对象就只有一个锁
说明 : 上图中无法实现方法同步;两个线程调用的是两个不同的线程两个业务对象就会创建两个锁
4.sychronized代码块解决非线程安全问题
(1) synchronized代码块是用synchronized修饰代码块,这是一种细粒度锁。线程开始执行同步代码块之前,必须先获得对同步监视器的锁定,任何时候只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对同步监视器的锁定
(2) synchronized(this)中的this是指当前对象,即synchronized(this)所在类对应的当前对象。它的作用是获取获取当前对象的同步锁;
5.全局锁和实例锁
实例锁: 锁在某个实例对象上。如果该类是单例,那么该锁也是具有全局锁的概念。实例锁对应的就是synchronized关键字
全局锁:该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)
四 .volatole关键字
1.java内存模型
所有的变量都是存储在主内存中,每个线程都是独立的工作内存,里面保存该线程使用到的变量的副本。线程对共享共享变量的所有操作必须在自己的工作内存,不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值传递需要通过主内存来完成。例如,线程1对共享变量的修改,要想被线程2及时看到,必须经历如下两个过程:
(1)把工作内存1中更新过的变量刷新到主内存中。
(2)将主内存中最新的共享变量的值更新到线程2中。
2.并发编程中的三个概念
(1).可见性:可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
(2).原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行,这个操作是一个不可分割的整体 ;int a=1;这个操作食必客分割的,称为一个原子操作;a++,是可分割的,不是一个原子操作
(3).有序性:即程序执行的顺序按照代码的先后顺序执行
3.volatole作用
(1) 用来确保将变量的更新操作通知其他线程
(2) 一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了:可见性,有序性,非原子性
4.使用场景
(1) synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性
(2) 使用条件:1)对变量的写操作不依赖于当前值 2)该变量没有包含在具有其他变量的不变式中
五 .常用方法
1.常用方法
(1) wait() 让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁; Object方法
(2) notify()/notifyAll() 唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程; Object方法
(3) yield(): 作用是让步,它能让当前线程由“运行状态”进入到“就绪状态”,从而让其他具有相同优先级的等待线程获取执行权;
(4) sleep():让当前线程休眠即当前线程会从“远程状态”进入到“休眠(阻塞状态在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待CPU的调度执行。
(5) join():加入一个线程,一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。如果某个线程在另一个线程t上调用t.join(),此线程将被挂起,直到目标线程t结束才恢复(即t.isAlive()返回为假)
(6) interrupt():中断本线程,无法中断阻塞状态的线程
2.线程优先级
Java中线程的优先级的范围是1~10,默认的优先级为5。10表示最高优先级,1表示最低优先级,5是普通优先级
高优先级的线程比低优先级的线程有更高的几率得到执行
3.守护线程和用户线程
用户线程:一般用户执行用户级任务
守护线程:而守护线程也就是“后台线程”,一般用来执行后台任务
六 线程池
1.线程池好处
(1)重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
(2)能有效控制线程池的最大并发数,避免大量线程之间因互相抢夺系统资源而导致的阻塞现象。
(3)能够对线程进行简单的管理,并提供定时执行以及指向间隔循环执行等功能。
2.线程池的创建
(1) Java SE 5的java.util.concurrent包中的执行器(Executor)管理Thread对象;Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务
(2) ThreadPoolExecutor线程池类
存放一定数量的一个线程集合。线程池允许若个线程同时运行,运行同时运行的线程数量就是线程池的容量。当添加到线程池中的线程超过它的容量时,会有一部分线程阻塞等待,线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理
(3) 线程池的分类
fixedThreadPool: 通过Executor的newFixedThreadPool方法来创建;只有核心线程并且这些核心线程不会被回收,这意味着它能过更加快速的相应外界的请求;并且这些核心线程没有超时机制,另外任务队列也是没有大小限制的;可以一次性预先执行代价高昂的线程分配,因而也就可以限制线程的数量了。这可以节省时间,因为你不用为每个任务都固定的付出创建线程的开销
singleThreadExcutor:通过Executor的newSingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行;SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得这些任务之间不需要处理线程同步的问题
schedulThreadPool:通过Executors的newScheduledPool方法来创建。它的核心线程数量时固定的,而非核心线程数是没有限制的,并且当非核心线程闲置是会被立即回收;ScheduledThreadPool这类线程主要用于执行定时任务和具有固定周期的重复任务
cachedThreadPool:通过Executors的newCachedThreadPool方法来创建。它是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数为Integer.MAX_VALUE;和FixedThreadPool不同的是,CachedThreadPool的任务队列其实相当于一个空集合,这将导致任何任务都会立即被执行
参考文档1:https://blog.csdn.net/wanliguodu/article/details/81005560
参考文档2:https://blog.csdn.net/wanliguodu/article/details/81071562#_803
参考文档3:https://blog.csdn.net/wanliguodu/article/details/81154294