1.并发编程三要素
1 .原子性:指并发编程中的一个操作或者多个操作应当看作一个原子,而原子是一个不可再分割的对象,即一个或多个的操作要么全部成功,要么全部失败;
2 .可见性:指当前线程对于共享对象的操作,应当保证对象改变之后,其他线程能够立即看到;
3 .有序性:程序的执行应当按照先后顺序进行执行
2.如何在 Java 程序中保证多线程的运行安全
首先出现线程安全的原因大致如下:
1 .线程切换带来原子性问题
2 .缓存导致可见性问题
3 .编译优化带来有序性问题
相应的解决方案如下:
1 .JDK Atomic开头的原子类,synchronized,LOCK可以解决原子性问题
2 .synchronized,volatile,lock可以解决可见性问题
3 .Happens-Before规则可以解决有序性问题
3.什么是多线程,多线程的优劣?
1 .多线程指同一个程序中有多个执行流,即同一个程序中可以同时运行多个执行流来执行不同的任务
2 .多线程的好处是提高CPU的利用率,即在多线程的程序中,如果有一个线程必须要等待的时候,CPU可以运行其他线程,而不是等待,提高了程序的执行效率
3 .多线程的劣处就是就是当线程过多时会大量占用内存,且对线程会出现资源竞用问题,产生线程安全问题,所以需要在编程过程中注意。
4.什么是线程和进程?
进程指的是在内存中运行的应用程序,每个进程中都有自己独立的内存空间;而线程指的是进程中的一个任务单元。即一个进程中至少有一个线程,且一个进程可以运行多个线程,多个线程可以共享资源。
5.进程和线程的根本区别
进程是操作系统分配的基本单元,线程是处理器任务执行和调度的基本单元
6.什么是上下文切换?
多线程编程中,在任意时刻cpu只能被一个线程使用,所有为了让所有的线程都得到有效的执行,CPU采取为每个线程分配时间片并轮换执行的方式。
所以当前线程的时间片消耗完毕后会保存当前任务的执行状态,以便下次能够再次切回当前任务继续加载。所以把一个任务从保存到再加载的过程称为任务一次上下文切换。
7.线程死锁
7.1什么是线程死锁
线程死锁指的就是两个或两个以上的线程同时竞争资源,或彼此通信造成的一种阻塞现象。
7.2 线程死锁发生的四个必要条件
1 .互斥条件:线程对于争抢的资源具与排它性,即一个资源只能同时被一个线程使用。
2 .请求与保持条件:一个线程因被请求而占用资源而发生阻塞,对已获得的资源不释放。
3 .不剥夺条件:即一个资源在一个线程使用期间不能被其他线程强行剥夺,只能自己释放。
4 .循环等待条件:当发生死锁时,所有的线程必定会形成一个闭环,造成永久死锁。
7.3 如何避免造成线程死锁
破坏产生死锁的四个条件之一就行:
1 .破坏互斥条件,因为用锁本就是让他们互斥,所以当前条件无法破坏
2 .破坏请求与保持条件:一次性申请所有资源
3 .破坏不剥夺条件:占用一部分资源的线程在请求新的资源时,如果请求不到,就让他释放一部分自身的持有的资源。
4 .破坏循环等待条件:按序申请资源
8.线程的创建
1 .继承Thread类
定义一个Thread类的子类,重写run方法,实现相关逻辑。
创建自定义的线程子类对象
调用线程对象的star( ) 方法来启动线程
2 .实现Runnable接口
定义Runnable接口的实现类,重写run方法
实现类创建Thread对象
调用线程对象的star( ) 方法来启动线程
3 .实现Callable接口
创建Callable接口的实现类
以实现类为参数创建FtureTask对象
将FtureTask对象作为参数创建Thread对象
调用线程对象的star( ) 方法来启动线程
4 .使用Executors工具类创建线程池
Executors提供了一系列的工厂方法来创建线程池,返回的线程池都实现了ExecutorService接口
主要有newFixedThreadPool,newCachedThreadPool,newScheduledThreadPool,newSinglethreadPool这四种线程池类型
(1)newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
(2)newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。如果希望在服务器上使用线程池,建议使用 newFixedThreadPool方法来创建线程池,这样能获得更好的性能。
(3) newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。
(4)newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
9.runnable和Callable的异同
相同点:
都是接口
都可以实现多线程编程
本质都是采用Thread.star( ) 启动线程
不同点:
Runnable接口无返回值,Callable接口有返回值,Callable可以利用FutreTask和Thread来获取异步执行的结果
Runnable接口的run方法只能抛出异常,无法处理,Callable接口的run方法可捕获异常。
10.线程中的run()和star()方法
run( ) 和star( ) 的区别
run( ) 是线程中的线程体,用来实现线程需要运行的代码,star( ) 用来启动线程
run( ) 方法可以重复调用,star( ) 只能执行一次
run( ) 只是本线程里的一个方法并不是多线程,只要通过star( ) 调用之后,线程才能真正的执行
为什么执行线程时都是执行star( ) 方法去调用run( ) 方法,而不是我们直接调用run( ) 方法?
调用star( ) 方法可以启动线程并进入就绪状态,而调用run方法只是Thread中的一个普通方法
11.线程生命周期和五种基本状态
新建(new):新创建了一个线程对象
可运行(runnable):线程创建后,通过调用star( ) 方法让线程进入就绪状态,等待被线程调用
运行( running) :线程获得时间片
阻塞( block) :运行中由于某种原因,放弃cpu的使用权
死亡( dead) :所有方法执行完毕,或因异常退出run( ) 方法,死亡的线程不可恢复
12 .线程调度算法
计算机通常只有一个 CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU 的使用权才能执行指令。所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得 CPU 的使用权,分别执行各自的任务。在运行池中,会有多个处于就绪状态的线程在等待 CPU,JAVA 虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配 CPU 的使用权。
有两种调度模型:分时调度模型和抢占式调度模型。
分时调度模型是指让所有的线程轮流获得 cpu 的使用权,并且平均分配每个线程占用的 CPU 的时间片这个也比较好理解。
Java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃 CPU。
13.sleep() 和 wait() 有什么区别?
类的不同:sleep( ) 是 Thread线程类的静态方法,wait( ) 是 Object类的方法
是否释放锁:sleep( ) 不释放锁;wait( ) 释放锁
用途不同:Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。
用法不同:wait( ) 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify( ) 或者 notifyAll( ) 方法。sleep( ) 方法执行完成后,线程会自动苏醒。或者可以使用wait( long timeout ) 超时后线程会自动苏醒。
14.synchronized 的作用?
在 Java 中,synchronized 关键字是用来控制线程同步的,就是在多线程的环境下,控制 synchronized 代码段不被多个线程同时执行。synchronized 可以修饰类、方法、变量。