java线程基础知识总结

什么是线程

一个程序同时执行多个任务,通常每个任务称为一个线程。
进程和线程区别
进程拥有自己的一整套变量体系,而线程则是共享。线程是轻量的,创建撤销的开销小。

线程的简单使用

方式一:

//自定义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阻塞等的情况下出现。

线程状态

线程就是下图的6中状态
在这里插入图片描述
注意几个函数:

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类

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关键字

其实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关键字作用:保证了实例域在不同线程中的一致性。
适用于:只会进行赋值操作的共享变量就适用于volatile修饰,是一种免锁机制。

线程局部变量

ThreadLocal类为各个线程提供了自己的实例。
适用于 随机数,SimpleDateFormat类等的变量,因为如果这种类型是共享变量同时进行dateformat的操作,由于不是同步方法,就可以能破坏原来的内部数据结构,而发生错误。
形式:

ThreadLocal<SimpleDateFormat>l dateformat = new ThreadLocal{
    protected SimpleDateFormat initialValue(){
        return new SimpleDateFormat("yyyy-MM-dd");
    }
}

stop 和suspend方法被弃用原因

stop 是一种破坏式的终止,不管什么时候,直接终止run方法,这样就有可能破坏原来锁保护的代码块,从而使程序出错。
suspend容易造成死锁,因为他不会释放锁,只能等resume唤醒。

阻塞队列

定义:当向队列添加元素而队列已满,或者当从队列移出元素而队列为空的时候,阻塞队列导致线程阻塞。
阻塞队列重要方法

方法正常动作特殊情况下的动作
add添加一个元素如果队列满,则抛出IllegalStateException
element返回队列的头元素如果队列空,则抛出NoSuchElementException
offer添加一个元素并返回true如果队列满,返回false
peek返回队列的头元素如果队列空,则返回Null
poll移出并返回队列的头元素如果队列空,则返回Null
put添加一个元素如果队列满,则阻塞
remove移出并返回头元素如果队列空,则抛出NoSuchElementException
take移出并返回队列的头元素如果队列空,则阻塞

常见的阻塞队列:

类名特点
LinkedBlockingQueue容量没有限制,也可以指定大小
ArrayBlockingQueue构造时需要指定容量,并且可以选择是否要公平性
PriorityBlockingQueue优先级队列,并不是先进先出,而是根据其优先级顺序

Callable、Future和FutureTask

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.
 *
 * <p>
 * <b>Sample Usage</b> (Note that the following classes are all
 * made-up.)
 * <pre> {@code
 * interface ArchiveSearcher { String search(String target); }
 * class App {
 *   ExecutorService executor = ...
 *   ArchiveSearcher searcher = ...
 *   void showSearch(final String target)
 *       throws InterruptedException {
 *     Future<String> future
 *       = executor.submit(new Callable<String>() {
 *         public String call() {
 *             return searcher.search(target);
 *         }});
 *     displayOtherThings(); // do other things while searching
 *     try {
 *       displayText(future.get()); // use future
 *     } catch (ExecutionException ex) { cleanup(); return; }
 *   }
 * }}</pre>
 *
 * 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:
 *  <pre> {@code
 * FutureTask<String> future =
 *   new FutureTask<String>(new Callable<String>() {
 *     public String call() {
 *       return searcher.search(target);
 *   }});
 * executor.execute(future);}</pre>
 *
 * <p>Memory consistency effects: Actions taken by the asynchronous computation
 * <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a>
 * actions following the corresponding {@code Future.get()} in another thread.
 *
 * @see FutureTask
 * @see Executor
 * @since 1.5
 * @author Doug Lea
 * @param <V> 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.
     *
     * <p>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

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)
  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值