关注我,能够获取最新知识、经典面试题以及技术分享
多线程和并发是求职大小厂面试中必问的知识点,其涉及到点不少,难度很大。有些人面对这些问题有点迷茫,为了解决这状况,总结了一下java多线程并发的基础知识点。并且要想深刻研究java多线程并发也必须先掌握基础知识,可为后续各个模块深刻研究作好作好准备。如今废话很少说,各位看官请查看基础知识点,后续还有源码解析(synchronize
底层原理,线程池原理,Lock
,AQS
,同步、并发容器等源码解析)。java
程序: 是计算机指令的集合,它以文件的形式存储在磁盘上,即程序是静态的代码面试
进程:数据库
线程:设计模式
别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这
使得线程间的通讯远较进程简单。数组
三者之间的关系:缓存
内存机制可查看文章《推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题》安全
组成部分:虚拟CPU、执行的代码以及处理的数据。性能优化
进程: 指系统中正在运行中的应用程序,它拥有本身独立的内存空间;多线程
线程: 是指进程中一个执行流程,一个进程中容许同时启动多个线程,他们分别执行不一样的任务,多个线程共享内存,从而极大地提升了程序的运行效率;并发
主要区别:
据,所以线程间的通讯比较简单,消耗的系统开销也相对较小
使用多线程好处:
Java程序启动时,一个线程马上运行,它执行main方法,这个线程称为程序的主线程,任何Java程序都至少有一个线程,即主线程。
主线程的特殊之处在于:
单核计算机只有一个CPU,各个线程轮流得到CPU的使用权,才能执行任务:
Thread
类有以下3个静态常量来表示优先级:
线程状态(State
枚举值表明线程状态):
Thread thread = new Thread()
。start
方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取 cpu 的使用权。run()
方法),此时除非此线程自动放弃 CPU 资源或者有优先级更高的线程进入,线程将一直运行到结束sleep
,suspend
,wait
等方法均可以致使线程阻塞WAITING
,它能够在指定的时间后自行返回。run
方法执行结束或者调用stop
方法后,该线程就会死亡。对于已经死亡的线程,没法再使用start
方法令其进入就绪。 线程在Running的过程当中可能会遇到阻塞(Blocked)状况:
join()
和sleep()
方法,sleep()
时间结束或被打断,join()
中断,IO完成都会回到Runnable
状态,等待JVM的调度。wait()
,使该线程处于等待池(wait blocked pool),直到notify()
/notifyAll()
,线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable) 线程建立方式:
run()
,无返回值run()
1.实现Runnable接口,重载run()
,无返回值,Runnable接口的存在主要是为了解决Java中不容许多继承的问题。
public class ThreadRunnable implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } public class ThreadMain { public static void main(String[] args) throws Exception { ThreadRunnable threadRunnable1 = new ThreadRunnable(); ThreadRunnable threadRunnable2 = new ThreadRunnable(); ThreadRunnable threadRunnable3 = new ThreadRunnable(); Thread thread1 = new Thread(threadRunnable1); Thread thread2 = new Thread(threadRunnable2); Thread thread3 = new Thread(threadRunnable3); thread1.start(); thread2.start(); thread3.start(); } }
2.继承Thread类,重写run()
,经过调用Thread的start()
会调用建立线程的run()
,不一样线程的run方法里面的代码交替执行。但因为Java不支持多继承.所以继承Thread类就表明这个子类不能继承其余类.
public class ThreadCustom extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread() + ":" + i); } } } public class ThreadTest { public static void main(String[] args) { ThreadCustom thread = new ThreadCustom(); thread.start(); } }
3.实现Callable接口,经过FutureTask/Future来建立有返回值的Thread线程,经过Executor执行,该方式有返回值,能够得到异步。
public class ThreadCallableCustom { public static void main(String[] args) throws Exception { FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() { public Integer call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } return 1; } }); Executor executor = Executors.newFixedThreadPool(1); ((ExecutorService) executor).submit(futureTask); //得到线程执行状态 System.out.println(Thread.currentThread().getName() + ":" + futureTask.get()); } }
4.使用Executors建立ExecutorService,入参Callable或Future,适用于线程池和并发
public class ThreadExecutors { private final String threadName; public ThreadExecutors(String threadName) { this.threadName = threadName; } private ThreadFactory createThread() { ThreadFactory tf = new ThreadFactory() { public Thread newThread(Runnable r) { Thread thread = new Thread(); thread.setName(threadName); thread.setDaemon(true); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return thread; } }; return tf; } public Object runCallable(Callable callable) { return Executors.newSingleThreadExecutor(createThread()).submit(callable); } public Object runFunture(Runnable runnable) { return Executors.newSingleThreadExecutor(createThread()).submit(runnable); } } public class ThreadTest { public static void main(String[] args) throws Exception { ThreadExecutors threadExecutors = new ThreadExecutors("callableThread"); threadExecutors.runCallable(new Callable() { public String call() throws Exception { return "success"; } }); threadExecutors.runFunture(new Runnable() { public void run() { System.out.println("execute runnable thread."); } }); } }
1)两个接口须要实现的方法名不同,Runnable须要实现的方法为run()
,Callable须要实现的方法为call()
。
2)实现的方法返回值不同,Runnable任务执行后无返回值,Callable任务执行后能够获得异步计算的结果。
3)抛出异常不同,Runnable不能够抛出异常,Callable能够抛出异常。
线程安全定义
当多个线程访问某个一类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的(即在多线程环境中被调用时,可以正确地处理多个线程之间的共享变量,使程序功能正确完成)。
线程安全示例
饿汉式单例模式-线程安全
public class EagerSingleton(){ private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton(){}; public static EagerSingleton getInstance(){ return instance; } }
如何解决线程安全问题?
能够经过加锁的方式:
lock()
以及释放同步锁unlock()
死锁,是指两个或两个以上的进程(或线程)在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。
活锁,任务或者执行者没有被阻塞,因为某些条件没有知足,致使一直重复尝试,失败,尝试,失败。
产生死锁的必要条件:
死锁的解决方法:
从另一些进程那里强行剥夺足够数量的资源分配给死锁进程,以解除死锁状态。
1)悲观锁
悲观锁,老是假设最坏的状况,每次去拿数据的时候都认为别人会修改,因此每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
2)乐观锁
乐观锁,顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,因此不会上锁,可是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可使用版本号等机制。乐观锁适用于多读的应用类型,这样能够提升吞吐量。
多个线程间锁的并发控制,对象锁多个线程、每一个线程持有该方法所属对象的锁以及类锁。synchronized, wait, notify 是任何对象都具备的同步工具
对象锁的同步和异步
同步的目的就是为线程安全,其实对于线程安全来讲,须要知足两个特性:原子性(同步)、可见性。
Volatile做用,实现变量在多个线程间可见,保证内存可见性和禁止指令重排
多线程的内存模型:main memory(主存)、working
memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操做后再save回去(volatile关键词的做用:每次针对该变量的操做都激发一次load and save)。
线程局部变量,以空间换时间的手段,为每一个线程提供变量的独立副本,以无锁的状况下保障线程安全。主要解决的就是让每一个线程执行完成以后再结束,这个时候就要用到join()方法。
适用场景:
1). 线程同步,是指线程之间所具备的一种制约关系,一个线程的执行依赖另外一个线程的消息,当它没有获得另外一个线程的消息时应等待,直到消息到达时才被唤醒。
线程间的同步方法,大致可分为两类:用户模式和内核模式。顾名思义:
内核模式,就是指利用系统内核对象的单一性来进行同步,使用时须要切换内核态与用户态。内核模式下的方法有:
用户模式,就是不须要切换到内核态,只在用户态完成操做。用户模式下的方法有:
2). 线程互斥,是指对于共享的进程系统资源,在各单个线程访问时的排它性。
当有若干个线程都要使用某一共享资源时,任什么时候刻最多只容许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。
线程互斥能够当作是一种特殊的线程同步。
线程是操做系统中独立的个体,但这些个体之间若是不通过特殊的协做就不能成为一个总体,线程间的通讯就成为总体的必用方式之一。
线程间通讯的几种方式?
线程之间的通讯方式:
共享内存:在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间经过写-读内存中的公共状态来隐式进行通讯。典型的共享内存通讯方式,就是经过共享对象进行通讯。
消息传递:在消息传递的并发模型里,线程之间没有公共状态,线程之间必须经过明确的发送消息来显式进行通讯。在 Java 中典型的消息传递方式,就是 wait()
和 notify()
,或者 BlockingQueue 。
java.util.concurrent.locks.Lock 接口,比 synchronized 提供更具拓展行的锁操做。它容许更灵活的结构,能够具备彻底不一样的性质,而且能够支持多个相关类的条件对象。它的优点有:
AQS ,AbstractQueuedSynchronizer ,即队列同步器。它是构建锁或者其余同步组件的基础框架(如 ReentrantLock、ReentrantReadWriteLock、Semaphore 等),J.U.C 并发包的做者(Doug Lea)指望它可以成为实现大部分同步需求的基础。它是 J.U.C 并发包中的核心基础组件。
优点:
AQS 解决了在实现同步器时涉及当的大量细节问题,例如获取同步状态、FIFO 同步队列。基于 AQS 来构建同步器能够带来不少好处。它不只可以极大地减小实现工做,并且也没必要处理在多个位置上发生的竞争问题。
在基于 AQS 构建的同步器中,只能在一个时刻发生阻塞,从而下降上下文切换的开销,提升了吞吐量。同时在设计 AQS 时充分考虑了可伸缩性,所以 J.U.C 中,全部基于 AQS 构建的同步器都可以得到这个优点。
何为同步容器?能够简单地理解为经过synchronized来实现同步的容器,若是有多个线程调用同步容器的方法,它们将会串行执行。
特色:
常见同步类容器:
jdk5.0之后提供了多种并发类容器来替代同步类容器从而改善性能。
同步类容器局限性:
经常使用的并发类容器:
ConcurrentHashMap原理
Copy-On-Write容器
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。
JDK里的COW容器有两种:
并发Queue:
ConcurrentLinkedQueue
经常使用方法:
BlockingQueue接口实现:
CountDownLatch: 用于监听某些初始化操做,等初始化执行完毕后,通知主线程继续工做。
CycilcBarrier: 全部线程都准备好后,才一块儿出发,只要有一我的没有准备好,你们都等待。
Concurrent.util经常使用类
定义:实现异步回调,jdk针对该场景提供了一个实现的封装,简化了调用
适合场景:处理耗时的业务逻辑时,可有效的减小系统的响应时间,提升系统的吞吐量。
Concurrent.util经常使用类
Semaphore:信号量,适合高并发访问, 用于进行访问流量的控制
ReentrantLock(重入锁)
重入锁,在须要进行同步的代码部分加上锁定,但不要忘记最后必定要释放锁定,否则会形成锁永远没法释放,其余线程永远也进不来的结果。
锁与等待/通知
多Condition
ReentrantReadWriteLock(读写锁)
使用 Executor 框架的缘由:
线程池的建立方式:
普通任务线程池
newFixedThreadPool(int nThreads) 方法,建立一个固定长度的线程池。
newCachedThreadPool() 方法,建立一个可缓存的线程池。
newSingleThreadExecutor() 方法,建立一个单线程的线程池。
定时任务线程池
线程池的关闭方式
ThreadPoolExecutor 提供了两个方法,用于线程池的关闭,分别是:
因为java多线程并发涉及到的知识点太多了,这边不可能一一列全,不过我会在后续的更新中一一去补充完善,并且会涉及原理以及源码层次的解析。谢谢观看,有错误欢迎指出更改!!
最后麻烦各位看官点个赞,谢谢支持!!!