Java多线程

本文详细探讨了线程的六种基本状态,同步与异步问题,BIO/NIO/AIO区别,并发与串行概念,sleep/wait/join/yield等方法,以及线程池、线程优先级、守护线程、ThreadLocal、线程安全与死锁。涵盖了创建线程方式、并发三要素和进程与线程的区别。

一、线程的基本概念

 1.线程的六种状态

        1)初始状态:new一个线程对象出来,但还没有调用start()

        2)运行状态:包括就绪和运行中

        3)阻塞状态:线程阻塞于锁修饰的方法或代码块

        4)等待状态:进入该状态的线程需要等待其他线程做出一些动作(通知或中断)

        5)超时等待状态:该状态不同于waiting,它可以在指定的时间后自行返回

        6)终止状态:表示线程已执行完毕

 2.同步和异步的问题

        1)同步锁:一个虚拟机上多个线程操作一个变量时出现线程安全的问题需要同步锁

        2)异步锁:多个虚拟机或服务器操作一个变量时出现线程安全问题需要异步锁

        3)同步和异步:强调的是消息通信机制。所谓同步,发起一个调用后,被调用者未处理完请求之前,调用不返回。异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调函数等机制来通知调用者其返回结果。

        4)阻塞和非阻塞:强调的是程序在等待调用结果(消息、返回值)时的状态。阻塞指调用结果返回前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞指不能立刻得到结果时,该调用不会阻塞当前线程。

3.BIO、NIO、AIO

        (1)BIO(Bloking I/O)同步阻塞IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时,服务器端就要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的开销。也可通过线程池改善。

                BIO适用于连接数少且固定的架构,对服务器资源要求较高,并发局限于应用中。 

        (2)NIO:同步非阻塞IO,服务器实现模式为一个请求一个线程,即客户端的连接请求都会注册到多路复用器上,多路复用器轮询到连接上有I/O请求时,才启动一个线程进行处理。

                 NIO适用于连接数多且连接较短的架构,如聊天服务器。

        (3)AIO:异步非阻塞IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成再通知服务器去启动线程进行处理。

                 AIO适用于连接数多且连接比较长的架构,如相册服务器。

4、并发、并行、串行

        1)并发指一个线程开始后,其他线程也可以运行。即多个线程在(一个)CPU上轮流切换执行的情况。

        2)并行指多个线程可以在多个CPU上同时运行;并发与并行并不是互斥的

        3)串行指在任意时间点上,只能有一个线程在运行

5、sleep()和wait()的区别

        (1)sleep()是Thread类的静态本地方法,wait()是Object()类的成员本地方法

        (2)sleep()可以在任何地方使用,wait()只能在同步方法或同步代码块中使用

        (3)sleep(long time)会休眠当前线程指定时间,释放CPU资源,不释放锁,休眠结束后自动苏醒继续执行;wait()会释放对象锁,进入等待队列,当该对象被调动notify()或notifyAll()之后才能竞争获取对象锁,进入运行状态

        (4)均需捕获interruputedException异常

6、notify()和notifyAll()的区别

        等待池(存放的是调用了wait()的线程):假设一个线程调用了wait()方法,就会释放该对象的锁,该线程进入对象的等待池。

        锁池(存放的是未获取锁的线程):只有获得了对象的锁,拥有锁的线程才能执行对象的synchronized代码,对象的锁只有一个线程能获得,其他线程只能在锁池中等待。

        notify()是从等待池中随机唤醒一个线程进入锁池;

        notifyAll()是唤醒对象的等待池中的所有线程,进入锁池。

7、join()和join(long time)

        join()是把指定线程加入到当前线程。如在对象方法中加入a.join(),会让当前线程b进入等待,直到a的生命周期结束,此时b处于阻塞状态。

        join(long time)是在指定时间内如果加入的线程未结束,会唤醒被join()阻塞的线程,让两个线程处于并行状态。

8、yield()

        是将当前线程从运行状态切换为待运行状态,让所有待运行状态的线程竞争进入运行状态。不释放锁。获取对象锁的线程使用yield()会失效。

        根据源码,yield()主要用于底层测试debug。

9、interrupt()、interrupted()和isInterrupted()

        thread.interrupt()是给调用对象的线程设置中断标志,想要中断需重写run()

        thread.interrupted()是判断当前线程(在主函数中main线程)是否中断,会清除中断状态,想要判断调用对象的线程,需改为Thread.currentThread().interrupted()

        thread.isInterrupted()是判断调用对象的线程thread的中断状态。

10、并发的三要素

        (1)原子性:指一个或多个操作,要么全部执行且在执行的过程中不被其他操作打断,要么全部不执行。

        (2)可见性:指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改结果

        (3)有序性:即程序的执行顺序按照代码的先后顺序来执行

11、进程和线程的区别

        (1)进程:进程可以看作程序执行的一个实例,进程是系统资源分配的独立实体,每个进程都拥有独立的地址空间。不同进程之间相互访问,要使用进程间通信,如管道,文件,套接字等。

        (2)线程:是操作系统能够进行运算调度的最小单位。一个进程中可以并发执行多个线程,每条线程并行执行不同的任务

12、创建线程有哪些方式?

        (1)继承Thread类

        (2)实现Runnable接口(重写方法是run(),不需抛异常,无返回值)

        (3)通过Callable和Future(重写方法是call(),可以抛异常,有返回值)

        (4)通过线程池创建

13、线程的优先级

        1)优先级较高的线程获取的CPU资源较多,会被优先执行,但不能保证优先级高的一定先执行

        2)优先级具有继承性,a线程继承b线程,a和b优先级相同

14、什么是守护线程

        Java线程有两种,一种是用户线程,一种是守护线程。

        守护线程主要用于程序中后台调度以及支持性工作。当虚拟机中不存在非守护线程时,守护线程才随JVM一起结束工作。常见的GC(垃圾回收器)

        设置方法:Thread.setDaemon(true)

15、什么是ThreadLocal

        Synchronized用于线程间的数据共享,ThreadLocal用于线程间的数据隔离。

        同步机制中,通过对象锁保证同一时间只有一个线程访问变量。此时该变量是多个线程共享的。需要考虑什么时候读写,什么时候加锁,什么时候释放锁。

        而ThreadLocal为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据访问的冲突。

        ThreadLocal存储的是ThreadLocalMap,存储的元素结构类似于hashmap的entry。threadLocal为key,由我们自己设定,需要隔离的数据为value。方法有get()、set()、remove()。

        PS:用完ThreadLocal()后,执行remove()操作,避免内存溢出

16、ThreadLocal的用处

        (1)在进行对象跨层传递时,使用ThreadLocal可以避免多次床底,打破层次间的约束。

        (2)线程间的数据隔离

        (3)进行事务操作,用于存储线程事务信息

        (4)数据库连接,Session会话管理

17、线程池的创建

        线程池是提前创建若干个线程,如果有任务要处理,线程池里的线程就会处理任务,处理完成之后并不会销毁,而是等待下一个任务。

        创建方式:

                1)new CachedThreadPool 创建一个可缓存的线程池

                2)new FixedThreadPool 创建一个定长线程池,可控制线程最大并发数

                3)new ScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行

                4)new SingleThreadExcutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务

18、线程池的优点

        (1)提高线程的重用性,减少对象创建销毁的开销 

        (2)可有效控制最大并发线程数,提高系统资源利用率,同时避免过多资源竞争,避免阻塞

        (3)提供定时执行、定期执行、单线程、并发数控制等功能

19、Java线程过多的后果

        (1)线程的生命后期开销非常高

        (2)消耗过多的CPU资源

        (3)降低稳定性

20、单例模式的线程安全问题

        (1)饿汉式:线程安全

        (2)懒汉式:非线程安全

        (3)双检索单例模式:线程安全

21、什么是CAS

        CAS:compare and swap 比较交换

        CAS是一种基于锁的操作,属于乐观锁。悲观锁,是将资源锁住,等一个之前获取锁的线程释放锁之后,下一个线程才会访问。而乐观锁,通过某种不加锁的方式处理资源,如给记录加version来获取数据,性能较悲观锁有很大提高。

        CAS包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存地址的值等于A,那么就更新内存,A->B。CAS是通过无限循环来获取数据,如果第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能执行。

22、CAS的问题

        (1)容易造成ABA的问题

                描述:A->B->A

                方法:可以加版本号或更新时间字段来检查是否发生

        (2)不能保证代码块的原子性

                CAS只能保证一个变量的原子性,如需保证3个变量共同原子性更新,必须用Synchronized。

        (3)造成CPU利用率增加

23、死锁的原因和避免

        (1)原因:多个线程在申请锁时发生了交叉闭环申请。(线程1获得锁A且未释放下去申请锁B,同时线程2已获得锁B且未释放,又要申请锁A,发生闭环)

        (2)方法:

                a.加锁顺序(线程按一定顺序加锁)

                b.加锁时限(线程尝试获取锁时加上一定时限,超时放弃请求,且释放现有锁)

                c.死锁检测

24、什么是AQS

        AQS是一个Java提出的底层同步工具类,用一个int类型的变量表示同步状态,并提供了一系列CAS操作来管理这个同步状态。

        AQS是一个构建锁和同步器的框架,能简单高效构造出应用广泛的大量同步器,如ReentranLock(独占式)、Semaphore(共享式)、ReentranReadWriteLock(组合式)等。

25、synchronized、volatile、CAS比较

        1)synchronized是悲观锁,属于抢占式,会引起其他线程阻塞

        2)volatile提供多线程共享变量可见性和禁止指令重排序优化

        3)CAS是基于冲突检测的乐观锁(非阻塞)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值