Java线程
线程的状态可以分为5种:新建new、可运行runnable、正在运行running、阻塞blocked和死亡dead。
现线程主要有3种方式:使用内核线程实现、使用用户线程实现和使用用户线程加轻量级进程实现。
线程使用的三种方式
- 继承Thread类,重写run方法。
- 实现Runnable接口,重写run方法(和第一种方式相比,这种方式更符合面向对象的设计)。
- 实现Callable接口通过FutureTask来创建Thread线程。具体为:new Thread(new FutureTask(new OneCallable())); 其中OneCallable是实现Callable接口并重写了call方法的类。
使用start和run方法的区别
直接调用线程的run方法是在原来现在调用的,跟调用普通方法没什么区别;调用start方法是将新建线程状态变为runnable,线程run方法的调用是在获取到CPU时间片才执行,是在另一个线程中运行的。
Sleep、wait(notify/notifyAll)、yield、join
- Sleep是线程类的静态方法,是让该线程睡眠一段时间,睡眠的线程进入阻塞状态,放弃争夺CPU资源,但是不释放锁,也就是当拥有synchronized的代码块的程序sleep之后虽然暂停执行,但是其他线程还是不能访问。
- Wait一般和notify/notifyAll一起使用,这三个方法都是Object的方法,一般用于多个线程对共享数据的获取,并且只能在synchrnoized中使用,因为wait和notify方法使用的前提是必须先获取一个锁。Wait的作用是使当前线程进入阻塞状态并释放持有的对象锁,线程会进入该对象的等待池中,但不会主动去竞争该对象的锁;notify是随机唤醒一个等待当前对象的锁的线程,notifyAll是唤醒所有等待当前对象的锁的线程。
- Join是让当前的线程进入阻塞状态,等待调用join的线程执行完毕之后再进入runnable状态。
- Yield方法是让running的线程重新进入runnable,不会释放锁。
线程同步的7种方式
- synchronized同步方法。如果是普通方法会锁住这个对象,如果是静态方法锁住的是整个类。
- synchronized同步代码块。
- volatile修饰变量。
- 重入锁ReenreantLock。实现了Lock接口,可重入,但效率低。
- ThreadLocal线程级别的变量共享。
- 阻塞队列LinkedBlockingQueue。主要通过synchronized的put、take实现。
- 原子变量。
Executor框架
Executor框架是一个异步执行框架,执行任务的线程相当于消费者,提交任务的线程相当于生产者。主要API包括以下:
- Executor:执行器接口,只有一个execute(Runnable)的方法,可以执行一个Runnable的任务,但是该方法没有返回值,使用较少。
- ExecutorService:是Executor的子接口,也是ThreadPoolExecutor的父接口,增加了一些方法包括关闭等,最重要的是增加了submit(Callable)的方法,返回值是Future
或FutureTask 。 - ScheduledExecutorService:是ExecutorService的子接口,主要增加了一些定时任务的方法。
- ThreadPoolExecutor:JDK的线程池类。
- Executors:是一个工厂类,提供了5个静态方法去创建线程池。
Synchronized、Lock、ReentrantLock、ReadWriteLock 、ReentrantReadWriteLock
- Synchronized是JVM内置的关键字。可以使用在代码块、方法上;非公平锁;可重入锁;不能响应中断;在执行完了锁住的代码块或发生中断会自动释放锁;无法得到是否持有锁、是否有等待线程等信息。
- Lock是一个接口,ReentrantLock是Lock的实现类,提供了lock、trylock等获取锁的方法和unlock释放锁的方法;默认是非公平锁。也可以设置为公平锁;可重入锁;能响应中断;需要主动释放锁,否则很有可能发生死锁现象;能得到锁的一些信息。
- ReadWriteLock是另外一个接口,ReentrantReadWriteLock是ReadWriteLock的实现类,主要特点是有readLock和writeLock两种锁,也需要主动释放锁。如果一个线程占了读锁,不影响其他线程获取读锁,但若其他线程要获取写锁,需要等待读锁完成;如果一个线程占了写锁,那么其他线程获取读写锁都需要等待该写锁释放。
一些锁的定义
- 可重入锁:可重入锁是指当持有锁的线程再次获取这个锁时能立即成功,锁的计数+1,当执行完代码块时计数-1,当计数为0时释放锁;不可重入锁是持有锁的线程再次获取时会造成死锁的情况。
- 可中断锁:可中断锁就是线程在等待获取锁的时候可以中断,去处理别的任务。Synchronized不是可中断锁,lock就是可中断锁。
- (非)公平锁:公平锁是指获取锁是有顺序的,例如ReentrantLock可以通过构造方法设置成公平锁,等待时间长的锁优先获取锁;非公平锁就是获取锁跟顺序无关,随机选择一个线程分配锁,例如synchronized就是典型的非公平锁,这种非公平锁有可能导致某个线程一直获取不到锁。
- 独享锁\共享锁(互斥锁\读写锁):独享锁是指这个锁同一时间只能被一个线程持有,例如synchronized、ReentrantLock;共享锁是这个锁可以被多个线程共同持有,例如ReadWriteLock和其子类ReentrantReadWriteLock,其读锁是共享锁,写锁是独享锁。而互斥锁和读写锁就分别是独享锁和共享锁的具体表现。
- 乐观锁\悲观锁:乐观锁是认为读多写少,所以在读的时候不会加锁,在写的时候会先照常执行,当发现执行结果不对时会舍弃本次操作再重试,例如CAS算法;悲观锁是认为读少写多,所以在每次读写都会进行加锁,例如独占锁。
线程池
threadPoolExecutor:
corePoolSize:线程池的基本大小。线程池刚创建的时候线程数为0,当有任务提交线程数为corePoolSize.
maximumPoolSize:线程池最大大小。线程池可同时活动的线程数。
WorkQueue:
(有界队列)ArrayBlockingQueue:基于数组实现的有界阻塞队列。
(无界队列)LinkedBlockingQueue:基于链表实现的无界阻塞队列。
(直接提交)SynchronousQueue:(无界)不是真正的队列因为它不会维护队列空间大小,而是维护一组线程,put必然伴随着take。提交的任务直接交给corethread处理,如果corethread满了,直接新建一个线程自行处理,一般要求maxthread为无穷。一般用在线程有先后依赖关系。
PriorityBlockingQueue:具有优先级的队列。
newFixedThreadPool: corePoolSize和maximumPoolSize设定为固定值,并且不会超时。采用LinkedBlockingQueue。
newCachedThreadPool: corePoolSize=0,maximumPoolSize最大值,超时1分钟。采用SynchronousQueue.
newSingleThreadExecutor: corePoolSize和maximumPoolSize设定为1,并且不会超时。采用LinkedBlockingQueue。
饱和策略:当有界队列填满之后,饱和策略发挥作用,无界队列没有饱和策略。分为四种。
- Abort。默认策略,队列满了之后,新的任务提交会抛出异常,可以捕获异常进行处理。
- CallerRun。调节策略,队列满了之后,新的任务不会丢弃也不会抛出异常,而是让调用executer的线程去执行。
- Discard。丢弃策略。队列满了之后,新的任务会被丢弃。
- DiscardOldest。丢弃最老的任务,也就是队列头的任务。
————————————————
原文链接:https://blog.csdn.net/weixin_39537927/article/details/87793180
浙公网安备 33010602011771号