1. 什么是线程?
- 线程的底层机制是切分CPU时间.
2. 线程与进程的区别?
3. 如何定义任务?
有三种方式:
- 实现
Runnable
接口并重写run()
方法. - 继承
Thread
类并重写run()
方法.
3.1 Runnable接口
Runnable
接口的实现类本身不具备任何内在的线程能力, 要实现线程行为, 必须将其附着到线程上, 即作为构造参数传给Thread
类.
3.2 Thread
Thread
类具有线程能力, 调用其start()
方法便可以启动一个线程.
3.2.1 start()
启动线程
3.2.2 static void sleep(long millis)
线程会等待指定时间, 此时线程不会释放其已获取的锁.
可通过TimeUnit.XXXX.sleep(long time)
方法代替.
3.2.3 setPriority(int priority)
设置线程的优先级, 优先级高的线程具有较高的执行优先权.但这是不确定的. JDK分为1-10共10级. 默认为5.
3.2.4 static void yield();
让出cpu时间给其他线程, 同时该线程也参与争夺cpu时间.
3.2.5 void setDaemon(boolean on)
设置守护线程, 当所有非守护线程运行完成后, 程序才会终止, 守护线程也就运行结束.在线程启动之前设置. 守护线程产生的子线程也是守护线程.
注: 守护线程中的finally块中的语句不会被执行
3.2.6 void join()
等待一个线程完成之后再执行.如调用了t.join(), 则等待t线程执行完成后再执行. 可以指定等待时间.
3.3 Executor
这是一个接口, 用于管理Thread对象, 它在客户端和任务执行之间提供了一个中间层, 将由它来执行任务. 其子接口ExecutorService
提供了更多的管理线程的定义. 而Executors
类为创建这些Executor
提供了一系列便捷的工厂方法.
常用形式如下:
ExecutorService es = Executors.newxxxxThreadPool();
es.execute(runnable);
3.3.1 Executors方法
-
static ExecutorService newFixThreadPool(int nThreads)
创建一个可重用固定线程数的线程池 -
static ExecutorService newFixThreadPool(int nThreads, ThreadFactory threadFactory)
使用提供的ThreadFactory
创建一个可重用固定线程数的线程池 -
static ExecutorService newSingleThreadExecutor()
创建只有一个worker线程的执行器, 如果提交了多个任务, 这些任务将排队, 每个任务都将在上个任务结束之后开始, 可保证顺序的执行各个任务.即任何时间内都只有一个线程在执行. -
static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
意义同上. -
static ExecutorService newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,如果之前构造的线程可用时将重用它们。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。 -
static ExecutorService newCachedThreadPool(ThreadFactory tf)
意义同上
3.3.2 ExecutorService方法
-
void shutdown()
启动有序关闭, 执行之前提交的任务, 但不再接收新的任务. -
List<Runnable> shutdownNow()
尝试停止所有正在执行的任务,停止处理等待的任务,并返回等待执行的任务列表。 -
boolean isShutdown()
如果这个执行器已被被shutdown了则返回true
. -
boolean isTerminated()
如果shutdown
后所有任务都已完成则返回true
, 如果没有调用shutdown
或shutdownNow
方法, 则永远都不会返回true
. -
boolean awaitTermination(long timeout, TimeUnit unit)
执行器收到shutdown
请求后会阻塞, 直到所有任务已完成或发生超时或当前线程已中断, 如果执行器结束则返回true
, 如果超时(在正常结束前发生)则返回false
. -
Future<?> submit(Runnable task)
向执行器提交一个任务, 并返回这个任务的Future
对象, 任务执行成功后这个对象的get方法将返回null
. -
Future<T> submit(Runnable task, T result)
意义同上, 但任务执行成功后这个对象的get方法将返回给定的result
. -
Future<T> submit(Callable<T> task)
意义同上.
3.4 Callable
如果要在一个任务完成时能够返回一个值, 则用Callable
接口. 且要用ExecutorService
的submit()
方法调用它.
3.4.1 Future接口
-
boolean isDone()
任务是否完成 -
boolean isCanceled()
任务是否取消 -
boolean cancel(boolean b)
尝试取消任务, 如果任务已经完成、已经被取消或由于其他原因无法取消,则此尝试将失败。如果成功,对于尚未启动的任务,则永远不应该运行。 -
V get()
获取执行结果, 如果任务没有完成将会等待. -
V get(long timeout, TimeUnit unit)
获取执行结果, 但只等待给定的时间, 如果超时则抛出异常.
4. 捕获异常
由于线程的本质特性, 使得你无法捕获从线程中逃逸的异常, 它会向外传播到控制台. 如你在run
方法中抛出一个RuntimeException
, 在执行线程时你无法在try-catch块中处理它.
4.1 Thread.UncaughtExceptionHandler
这是一个接口. 为每个线程对象提供一个处理未捕获异常的处理方法.
用法:
- 先是实现这个接口.
- 然后调用线程对象的
setUncaughtExceptionHandler()
方法将前面的实现类对象传给线程即可.
注: 也可以在ThreadFactory
实现类中统一设置而不必每个线程都这样设置.
5. synchronized
对于并发的处理是通过某种方式来禁止多个任务同时访问同一个资源. 即在资源上加锁, 只有获得这个锁才能访问它, 而每次又只能有一个线程可以持有这个锁.
将共享资源包装到一个对象中, 然后把所有要访问这个资源的方法(普通方法或静态方法)都标记为synchronized
. 每次线程调用这些方法时,都将检查锁是否可用, 然后获取锁, 执行代码, 释放锁.
所有的对象都自动地含有单一的锁(也称为监视器).在对象上调用任何synchronized
方法都会将此对象加锁. 其他所有要调用类中的synchronized
方法的线程都会被阻塞.
5.1 同步规则
5.2 Lock
Lock
对象必须被显式地创建, 锁定和释放. 相对于synchronized
, 它更加灵活.
Lock lock = new ReentrantLock();
lock.lock;
try {
.....
} finally {
lock.unlock;
}
注意: return 语句要放在try块中.
Lock类方法:
boolean tryLock()
void lock()
void unlock()
5.3 volatile
volatile
修饰的变量, 如果对其进行了写操作, 那么所有的读操作都将能看到这个修改. 但是如果变量的值依赖于其之前的值(如递增), 那么用volatile
修饰后将无法工作了.
- 对于读取和写入除
long
和double
这外的基本类型变量的操作都是原子性操作. 由于long
和double
是64位, 在读取和写入时是被当作两个分离的32位操作来执行的, 在这个过程中会发生线程不安全的操作. 但是如果使用了volatile
关键字, 则会获取原子性. - 如果一个变量会被多个任务同时访问, 且这些任务中至少有一个是写入任务, 那么你就应该将这个域设置为
volatile
的.
5.4 同步代码块
有时不希望锁住整个方法而只希望锁住部分代码, 则可用如下格式:
// 常用synchronized(this)
synchronized(syncObject) {
...
}
这会大大提高方法的执行效率, 因为对象被锁住的时间更短.
6. 结束任务
6.1 线程状态
- new(新建)
线程被创建后所处的状态, 线程已得到了必需的系统资源 - Runnable(就绪)
调用start()
后所处的状态. 此状态下线程可运行也可不运行. - Blocked(阻塞)
此时线程能运行, 但是有个条件阻止它运行, 此时系统不会给它分配CPU时间. - Dead(死亡)
线程终止.即运行完成(从run方法中返回)或被中断.
6.2 进入阻塞状态
- 调用了
sleep
方法. 在指定时间内线程不会运行. - 调用了
wait()
方法, 直到线程得到了notify()
或notifyAll()
方法, 线程才会再进行就绪状态. - 等待输入或输出完成.
- 等待获取对象锁.
6.3 中断
-
interrupt()
直接操作线程对象.
对于ExecutorService
对象, 通过调用它的shutdownNow()
方法, 它会发送一个interrupt()
调用给它启动的所有线程.如果只想中断它启动的某一个线程, 可要用submit
方法, 该方法返回一个Future
对象, 通过调用Futrue
对象的cancel()
方法, 它将会在这个线程上调用interrupt()
方法. -
lockInterruptibly
Lock锁中中断锁的获取.
网友评论