一个程序同时执行多个任务,通常每个任务称为一个线程。
进程和线程区别
进程拥有自己的一整套变量体系,而线程则是共享。线程是轻量的,创建撤销的开销小。
方式一:
//自定义Runnable
class MyRunnable implements Runnable{
@Override
public void run () {
//需要处理的任务
}
}
//创建Runnable对象
Runnable r = new MyRunnable();
//创建Thread对象
Thread t = new Thread(r);
//启动线程
t.start();
方式二
//复写Thread类的run方法
Thread thread= new Thread(){
@Override
public void run () {
//需要做的任务
}
};
thread.start();
推荐方式一,因为方式二每一个任务启动一个线程不好,开销大,多任务并行处理可以通过线程池来实现。
注意直接调用thread和Runnable 的run方法没有用,不会启动新线程。
线程终止只有两种方式:
1、run方法正常退出,自然死亡
2、因为有没有捕获的异常而终止run方法,意外死亡
stop 方法可以终止线程,但是被弃用了
当线程调用interrupt方法时,线程中断状态被置位;每个线程都有一个boolean标志,以判断线程是否被中断。
中断不等于终止,没有任务和语言上的要求,中断的线程应该终止。中断一个线程只不过是引起他的注意,被中断的线程可以决定如何响应中断。线程可以简单的将中断作为一个终止的请求。
//发送中断请求
void interrupt()
//测试当前线程(即正在执行这一命令的线程)是否被中断,副作用是会把当前线程中断状态变成false
static boolean interrupted()
//测试线程是否被中断,实例方法,不会重置中断状态
boolean isInterrupted()
将中断请求作为终止终止请求的方式
public void run(){
try{
while(!Thread.current().isInterrupted()&&more work to do){
do more work
}
}catch(InterruptedException e){
//线程在sleep,wait的时候被调用interrupted方法会产生中断异常
}finally{
cleanup,if required
}
}
线程被阻塞时中断状态无法被检测,会抛出中断异常InterruptedException;通常是sleep,wait,IO阻塞等的情况下出现。
void join()//等待终止指定线程,让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作
void join(long millis) //等待指定的线程死亡或者经过指定的毫秒数,如果超过这个时间,则停止等待,变为可运行状态
void stop()//停止该线程。这一方法已过时
void suspend() //暂停这一线程的执行 已过时
void resume() //恢复线程,和suspend一起使用,也过时
void sleep(long millis)// 等待休眠millis 毫秒,执行这个方法,线程进入计时等待
void yield()//暂停当前正在执行的线程对象,并执行其他线程。理论上,yield意味着放手,放弃,投降。
//一个调用yield()方法的线程告诉虚拟机它乐意让其他线程占用自己的位置。这表明该线程没有在做一些紧急的事情。
//注意,这仅是一个暗示,并不能保证不会产生任何影响。
线程有很多属性,常常关心只有几种,线程优先级,守护线程,处理未捕获异常的处理器。
线程优先级,注意一点就行,程序功能的正确性不能依赖于优先级
守护线程:守护线程唯一的用途就是为其他线程服务,当只剩下守护线程的时候虚拟机就会退出,所以守护线程永远不要去访问资源,如文件,数据库,因为他可能在任何时候中断。函数void setDaemon(boolean isDaemon)
未捕获异常处理器:这是因为run方法不能抛出任何被检测的异常,但是被检测的异常被抛出就会终止线程;有几种方法处理,一是自己setUncaughtExceptionHandler;第二种是try catch
线程同步方法主要有两种,一种是使用ReentrantLock类,和synchronized关键字
ReentrantLock类进行代码保护的方式
private Lock lock = new ReentrantLock();
...
lock.lock()
try{
要保护的代码
}finally{
lock.unlock();
}
条件锁适用的场景,使用条件来管理哪些获得锁,但是没有能力做有用做的线程。形式如下:
private Lock lock = new ReentrantLock();
private Condition condition= lock.newCondition();
...
lock.lock()
try{
while(!(条件 Ok ))
condition.await();
要保护的代码
condition.signalAll//只有调用这个函数才能重新激活这一条件等待的所有线程。
}finally{
lock.unlock();
}
1、锁是可重入的,比如A调用B,执行A需要锁s,执行B也需要锁s,那么获得锁s,执行A同时也可以执行B。
2、公平锁偏爱等待时间长的线程,但是效率不好,ReentrantLock是公平锁。
3、锁可以拥有多个条件状态
4、锁使得代码只能被一个线程执行
其实synchronized 关键字使用的是对象的内部锁,java的每一个对象都有一个内部锁。
public synchronized void method(){
}
等价于
public void method(){
this.intrinsicLock.lock()
try{
要保护的代码
}finally{
this.intrinsicLock.unlock();
}
}
内部锁只有一个相关条件,wait 方法添加到线程等待集中,notifyAll()/notify() 唤醒。
等价于await() 和signalAll()
静态方法也是可以使用synchronized关键字的,他持有的是Class对象的内部锁。
同步阻塞形式
private Object obj= new Object();
synchronized(obj){
}
总结:最好不要用锁解决问题,能用阻塞队列完成就用。
volatile关键字作用:保证了实例域在不同线程中的一致性。
适用于:只会进行赋值操作的共享变量就适用于volatile修饰,是一种免锁机制。
ThreadLocal类为各个线程提供了自己的实例。
适用于 随机数,SimpleDateFormat类等的变量,因为如果这种类型是共享变量同时进行dateformat的操作,由于不是同步方法,就可以能破坏原来的内部数据结构,而发生错误。
形式:
ThreadLocal<SimpleDateFormat>l dateformat = new ThreadLocal{
protected SimpleDateFormat initialValue(){
return new SimpleDateFormat("yyyy-MM-dd");
}
}
stop 是一种破坏式的终止,不管什么时候,直接终止run方法,这样就有可能破坏原来锁保护的代码块,从而使程序出错。
suspend容易造成死锁,因为他不会释放锁,只能等resume唤醒。
定义:当向队列添加元素而队列已满,或者当从队列移出元素而队列为空的时候,阻塞队列导致线程阻塞。
阻塞队列重要方法
方法 | 正常动作 | 特殊情况下的动作 |
---|---|---|
add | 添加一个元素 | 如果队列满,则抛出IllegalStateException |
element | 返回队列的头元素 | 如果队列空,则抛出NoSuchElementException |
offer | 添加一个元素并返回true | 如果队列满,返回false |
peek | 返回队列的头元素 | 如果队列空,则返回Null |
poll | 移出并返回队列的头元素 | 如果队列空,则返回Null |
put | 添加一个元素 | 如果队列满,则阻塞 |
remove | 移出并返回头元素 | 如果队列空,则抛出NoSuchElementException |
take | 移出并返回队列的头元素 | 如果队列空,则阻塞 |
常见的阻塞队列:
类名 | 特点 |
---|---|
LinkedBlockingQueue | 容量没有限制,也可以指定大小 |
ArrayBlockingQueue | 构造时需要指定容量,并且可以选择是否要公平性 |
PriorityBlockingQueue | 优先级队列,并不是先进先出,而是根据其优先级顺序 |
Callable和Runnable类似,都是接口;不同是Runnable是一个没有参数和返回值的异步方法封装类,只包含run这个方法,可以被Thread直接执行;但是Callable不能直接被Thread执行,需要被包装一下,有返回值,可以抛出异常;
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Future也是接口:
package java.util.concurrent;
/**
* A {@code Future} represents the result of an asynchronous
* computation. Methods are provided to check if the computation is
* complete, to wait for its completion, and to retrieve the result of
* the computation. The result can only be retrieved using method
* {@code get} when the computation has completed, blocking if
* necessary until it is ready. Cancellation is performed by the
* {@code cancel} method. Additional methods are provided to
* determine if the task completed normally or was cancelled. Once a
* computation has completed, the computation cannot be cancelled.
* If you would like to use a {@code Future} for the sake
* of cancellability but not provide a usable result, you can
* declare types of the form {@code Future>} and
* return {@code null} as a result of the underlying task.
*
*
* Sample Usage (Note that the following classes are all
* made-up.)
*
{@code
* interface ArchiveSearcher { String search(String target); }
* class App {
* ExecutorService executor = ...
* ArchiveSearcher searcher = ...
* void showSearch(final String target)
* throws InterruptedException {
* Future future
* = executor.submit(new Callable() {
* public String call() {
* return searcher.search(target);
* }});
* displayOtherThings(); // do other things while searching
* try {
* displayText(future.get()); // use future
* } catch (ExecutionException ex) { cleanup(); return; }
* }
* }}
*
* The {@link FutureTask} class is an implementation of {@code Future} that
* implements {@code Runnable}, and so may be executed by an {@code Executor}.
* For example, the above construction with {@code submit} could be replaced by:
* {@code
* FutureTask future =
* new FutureTask(new Callable() {
* public String call() {
* return searcher.search(target);
* }});
* executor.execute(future);}
*
* Memory consistency effects: Actions taken by the asynchronous computation
* happen-before
* actions following the corresponding {@code Future.get()} in another thread.
*
* @see FutureTask
* @see Executor
* @since 1.5
* @author Doug Lea
* @param The result type returned by this Future's {@code get} method
*/
public interface Future<V> {
/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when {@code cancel} is called,
* this task should never run. If the task has already started,
* then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
* After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not be cancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* Returns {@code true} if this task was cancelled before it completed
* normally.
*
* @return {@code true} if this task was cancelled before it completed
*/
boolean isCancelled();
/**
* Returns {@code true} if this task completed.
*
* Completion may be due to normal termination, an exception, or
* cancellation -- in all of these cases, this method will return
* {@code true}.
*
* @return {@code true} if this task completed
*/
boolean isDone();
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;
/**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
get方法的调用被阻塞,直到计算完成。
FutureTask是一个包装器,可以将Callable转化成Future 和Runnable
举个例子:
public static void main (String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call () throws Exception {
System.out.println("1");
Thread.sleep(10);//调整这个时间可以看出在没有返回值之前,task.get()会阻塞调用他的线程
return 1;
}
};
FutureTask task = new FutureTask<Integer>(callable);
Thread thread = new Thread(task);
thread.start();
System.out.println("执行get前时间:" + System.currentTimeMillis());
try {
System.out.println("当前值:" + task.get());
System.out.println("执行后时间:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
执行器Executor类有很多静态工厂方法来构建线程池。为什么要是有线程池(Thread Pool)? 1、创建新的线程有一定的代价;2、run方法运行结束不会结束线程;3、创建大量线程可能会使得Java虚拟机崩溃。
Executor 静态方法:
方法 | 描述 |
---|---|
newCachedThreadPool | 必要时创建新线程;空闲线程会被保留60秒 |
newFixedThreadPool | 该线程池包含固定数量的线程,空闲线程会一直被保留 |
newSingleThreadPool | 只有一个线程的线程池,执行顺序是提交顺序 |
newScheduledThreadPool | 用于预定执行而构建的固定线程池,替代java.util.Timer |
newSingleThreadScheduledExecutor | 用于预定执行而构建的单线程 |
newCachedThreadPool、newFixedThreadPool、newSingleThreadPool这3个方法都会返回实现了ExecutorService接口的ThreadPoolExecutor类对象。
三种submit函数,都是将要执行的Runnable或者Callable提交给ExecutorService;
方法 | 描述 |
---|---|
Future> submit(Runnable task) | 可以调用 isDone,cancel,isCanceled方法,但是调用get方法的时候返回的是null |
Future< T > submit(Runnable task,T result) | get方法返回时返回指定的 result |
Future< T > submit(Callable task) | 调用get的时候需要等call方法执行返回的结果 |
newScheduledThreadPool和newSingleThreadScheduledExecutor返回ScheduledExecutorService对象,可以控制Callable或者Runnable在初始延迟之后执行一次也可以周期性的执行
ThreadPoolExecutor 有四种构造方法
/*
corePoolSize 核心线程数,默认情况下核心线程会一直存活
maximumPoolSize 最大线程数,非核心线程在等待超时后终止
keepAliveTime 非核心线程闲置超时时间
unit 超时时间单位
workQueue 线程池任务队列
handler 达到线程池容量边界或者任务队列满了,会被调用
threadFactory 用于给线程池创建新线程
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)