多线程
线程基础
继承是操作系统结构的基础,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的基本单位。进程可以被看做程序的实体,同样,它也是线程的容器。
线程是操作系统调度的最小单元,也叫做轻量级进程。
使用多线程的原因:使用多线程可以减少程序的响应时间;与进程相比,线程的创建和切换开销更小,同时在数据共享方面效率更高;多cpu或者多核计算机本身就支持多线程,多进程可以提高CPU的使用率;多线程可以简化程序的结构,使程序便于理解和维护。
线程的状态:New(新创建状态,刚创建为调用start方法);Runnable(可运行状态,调用start方法);Blocked(阻塞状态,线程被锁阻塞,暂时不活动);Waiting(等待状态);Timed waiting(超时等待状态,可以在指定的时间自行返回);Terminated(终止状态,执行完毕退出或者捕获异常退出)
创建线程的方法:继承Thread类,重写run方法;实现Runnable接口,并实现run方法;实现Callable接口,重写call方法。call方法可以返回一个返回值,也可以抛出异常,同时Callable可以拿到一个Future,来监视目标线程的call方法,但是调用Future的get方法获取结果时,线程会堵塞,知道call方法返回结果。
1 | ExecutorService s=ExecutorService.newSingleThreadPool(); |
三种方法中,推荐使用Runnable,因为没有加强和修改的情况下没有必要使用继承。
使用Lock
的lock方法可以对方法中的代码块进行加锁,在通过unlock进行解锁。除此以外,可以通过newCondition
方法获取条件对象,调用await
方法阻塞当前线程,并放弃锁,等待其他线程调用Condition
的signalAll()
方法唤醒。
volatile修饰变量之后,当一个线程修改了变量的值时,该变量对其他线程会立刻可见。
volatile无法保证对变量的操作是原子性的,但是它保证是有序性的,即在volatile变量之前的语句不会到之后去执行。
使用volatile的条件:对变量的操作不依赖当前值(如自增,因为其不保证原子性);没有包含在其他不等式中;
volatile的使用场景:状态标志;双重检查模式
阻塞队列
阻塞队列场景:
当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。
当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到有空的位置。
核心方法:
offer(onject)
:表示如果可以的话,将object对象添加到BlockingQueue中,成功返回true,失败返回false,该方法不阻塞队列。
offer(E 0,long timeout,TimeUnit unit)
:设定等待时间添加对象,如果时间到了还没有添加,则返回失败。
put(object)
:添加对象,如果没有空间,则线程被阻塞。
poll(time)
:取走排在首位的对象,没有则等待相应的时间,取不到则返回null
poll(long time,TimeUnit unit)
:取走排在首位的对象,没有则等待相应的时间,取不到则返回false
take()
:取走排在首位的对象,没有则阻塞
drainTo()
:一次性取走所有可用的数据对象
阻塞队列种类:
1.ArrayBlockingQueue
:
使用数组实现有限的阻塞队列,并按照先进先出原则对元素进行排列。默认不保证线程公平(即先阻塞的先用)。保证公平原则的可以如下使用:
1 | ArrayBlockingQueue queue=new ArrayBlockingQueue(200,true); |
2.LinkedBlockingQueue
:
使用链表的阻塞队列,按照先进先出原则对元素排列,内部有一个数据缓冲队列。当生产者往里面添加一个数据时会立即返回,数据会添加到队列中,直到队列满时才会阻塞。内部生产者和消费者采用独立的锁来控制数据同步。如果构造它时不指定其容量的大小,那么会产生一个近似于无限大的队列,会无限往里面添加时会消耗掉内存。
3.PriorityBlockingQueue
:
支持优先级的无界队列,默认采取自然升序排列。可以自定义实现compareTo方法指定元素排序规则,也可以构造时传入Comparator来对元素进行排列。
4.DelayQueue
:
支持延时获取元素的无界阻塞队列,里面使用PriorityBlockingQueue实现。队列中的元素必须实现Delayed接口来指定元素到期的时间,到期后才能被取走。
5.SynchronousQueue
:
不存储元素的阻塞队列。即每一插入的操作必须要等待另一个取走操作,反过来也一样。
6.LinkedTransferQueue
:
由链表结构组成的TransferQueue队列,其中实现了一个重要的接口TransferQueue,里面有3个重要的方法:
(1)transfer(E e):若当前存在一个正在等待获取的消费者线程,则立即将元素给消费者,否则会将元素插入到队列尾部,并且等待进入阻塞状态,直到被取走。
(2)tryTransfer(E e):若当前存在一个正在等待获取的消费者线程,则立即将元素给消费者,否则返回false,这个不是一个阻塞操作。
(3)tryTransfer(E e,long timeout,TimeUnit unit):若当前存在一个正在等待获取的消费者线程,则立即将元素给消费者,否则会将元素插入到队列尾部,并且等待进入阻塞状态,直到被取走。若超时则返回false,不超时返回true。
7.LinkedBlockingQueue:
由链表组成的双向阻塞队列,可以从两端插入移出元素,所以多了addFirst、addLast、offerFirst、offerLast等方法。
线程池 ThreadPoolExecutor
核心参数:
corePoolSize:核心线程数。
maximumPoolSize:最大线程数。
keepAliveTime:非核心线程闲置超时时间。
TimeUnit:参数时间单位。
workQueue:任务队列。
ThreadFactory:线程工厂,一般无需设置。
RejectExecutionHandler:饱和策略。(CallerRunsPolicy:用调用者所在线程来处理任务;DiscardPolicy:不能执行任务,并将任务删除;DiscardOldestPolicy:丢弃队列最近任务,并执行当前任务)