Java多线程

80 阅读1分钟

1.线程的创建方式

1.继承Thread

class MyThread extends Thread{
    @Overridepublic void run() {    
        super.run();
    }
}
new MyThread().start()

2.实现Runnable接口

class MyRunnable implements Runnable{    @Override
    public void run() {

    }
}
new Thread(new MyRunnable()).start()

3.实现Callable接口(可异步等待返回值的线程)

class MyCallable implements Callable<String>{
    @Overridepublic String call() throws Exception {
        return "abc";
    }
}
FutureTask<UnionLoginBean> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
System.out.print(futureTask.get(Timeout));

2.线程的生命周期

  • 创建

  • new MyThread()

  • 就绪

  • Thread.start() 还没真正执行,等待CUP调度

  • 运行

  • Thread.start() 并且获取到CPU执行权的时候

  • 阻塞

  • 1、**等待阻塞:**运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

  • 2、**同步阻塞:**运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

  • 3、**其他阻塞:**运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

    **线程等待:**Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。唤醒线程后,就转为就绪(Runnable)状态。
    **线程让步:**Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
    **线程加入:**join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
    **线程I/O:**线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
    **线程唤醒:**Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意性的,并在对实现做出决定时发生。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。

  • **线程睡眠:**Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。

  • 死亡

  • Thread.stop()

  • 线程执行完毕

  • 线程抛出异常

3.线程池ThreadPoolExecutor

在并发频繁的程序中,使用线程池来统一管理线程的好处:

1.复用线程池中的线程,避免线程的频繁创建销毁带来的开销

2.任务执行时可以从线程池捞出线程复用,减去了线程的创建时间,提高了任务的执行速度

3.提高线程的可管理性

线程池的底层原理:

七大构造参数:

  1.  corePoolSize 线程池中常驻的核心线程
  2. maximumPoolSize 线程池中允许同时执行的最大线程数
  3. keepAliveTime 非核心线程执行完任务后允许存活的时间,在该时间内没被复用则会被回收
  4. TimeUnit   keepAliveTime的时间单位
  5. workQueue  超过核心线程后暂放的任务队列
  6. threadFactory  生成工作线程的线程工厂,一般默认即可
  7. handler    当任务队列满并且线程数等于maximumPoolSize后采用的拒绝策略
  • AbortPolicy:默认拒绝策略,直接抛RejectedExecutionException

  • CallerRunsPolicy:直接拒绝,交给调用者线程自己执行

  • DiscardOldestPolicy:丢弃等待队列中最早加入还未执行的任务,并把新任务添加进队列

  • DiscardPolicy:直接拒绝新任务

其他重要参数:

mainLock:一个ReentrantLock,保证正在运行线程的增删安全

workers:Hashset集合类,存放正在运行的线程

线程池的运行机制:

新任务添加的时候

  1. 当正在运行的线程数<corePoolSize时直接创建核心线程执行任务

  2. 当corePoolSize<=正在运行的线程数,把新任务放入任务队列

  3. 当任务已满 && 正在运行的线程数 < maximumPoolSize,创建非核心线程执行任务

  4. 当任务已满 && 正在运行的线程数 >= maximumPoolSize,执行handler拒绝策略

java线程池种类:

1.Executors.newCachedThreadPool

public static ExecutorService newCachedThreadPool(ThreadFactory var0) { 
   return new ThreadPoolExecutor(0, 
                                 Integer.MAX_VALUE,
                                 60L,
                                 TimeUnit.SECONDS,
                                 new SynchronousQueue());
}

核心线程数为0,有非核心线程可复用则复用没有可复用则直接创建非核心线程执行任务,非核心线程数无限,空闲时间60秒。

2.Executors.newSingleThreadPool

public static ExecutorService newSingleThreadExecutor() {
    return new Executors.FinalizableDelegatedExecutorService(
                 new ThreadPoolExecutor(1, 1, 0L,
                 TimeUnit.MILLISECONDS, new LinkedBlockingQueue())
               );
}

只有一个线程的线程池,所有任务循序执行。

FinalizableDelegatedExecutorService是对ExecutorService的封装,隐藏ExecutorService中的方法,保证线程的唯一性,避免线程的重新配置。并且重弄写finalize保证线程的关闭

3.Executors.newFixedThreadPool

public static ExecutorService newFixedThreadPool(int threads) {
    return new ThreadPoolExecutor(threads,                                  threads,                                  0L,
                                  TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue());
}

定长的线程池,并发数量超过指定线程数阻塞在等待执行

4.Executors.newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);    //内部调用  ThreadPoolExecutor(corePoolSize,                                  Integer.MAX_VALUE,                                 
                                  0L,
                                  TimeUnit.NANOSECONDS,                                               new ScheduledThreadPoolExecutor.DelayedWorkQueue());
}

固定周期重复执行的线程池,相当与Timer的多线程版本

4.线程池的workQueue

  1. SynchronousQueue:不存储数据的队列,添加任务的时候直接交给工作线程执行
  2. LinkedBlockingQueue:默认无界队列,指定队列长度则为有界队列
  3. ArrayBlockingQueue:有界队列,必须指定队列长度,遵循FIFO原则
  4. PriorityBlockingQueue:有优先级的有界队列,默认长度11,可执行长度