1. Semaphore

限制可以访问某些资源(物理或逻辑的) 线程数目,

  • 实际上是就是普通共享锁,内部静态类 Sync 继承了 AQS,实现了共享锁的 tryAcquireShared、tryReleaseShared。
  • 逻辑就是正常共享锁逻辑,同时还可以实现公平和非公平
  • 可以说是 ReentrantLock 的共享版,所以注意要在 finally 里面调用 semaphore.release() 释放锁

1.1 主要方法摘要:

void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。

  • void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
  • void release():释放一个许可,将其返回给信号量。
  • int availablePermits():返回此信号量中当前可用的许可数。
  • boolean hasQueuedThreads():查询是否有线程正在等待获取。

1.2 举例

public class DasApplicationTests {

    public static void main(String[] args) {
        /**
         * 1. 初始化线程池 创建一个可缓存线程池 如果线程池长度超过处理需要,
         */
        ExecutorService service = Executors.newCachedThreadPool();

        // 2. 创建Semaphore 信号量 初始化许可大小为 3
        final Semaphore sp = new Semaphore(3);
        for (int i = 0; i < 10; i++) {


            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Runnable runnable = new Runnable() {
                @Override
                public void run() {

                    // 请求获取许可,如果有可获取的许可 则继续往下执行,许可数减1 否则进入阻塞状态
                    try {
                        sp.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程:" + Thread.currentThread().getName() + "进入,当前有" + (3 - sp.availablePermits()) + "个并发");

                    try {
                        Thread.sleep((long) (Math.random() * 10000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程" + Thread.currentThread().getName() +
                            "即将离开");
                    sp.release();//释放许可,许可数加1
                    //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
                    System.out.println("线程" + Thread.currentThread().getName() +
                            "已离开,当前已有" + (3 - sp.availablePermits()) + "个并发");
                }
            };

            /**
             * execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
             */
            service.execute(runnable);
            /**
             *submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,
             * 通过这个 Future 对象可以判断任务是否执行成功 ,并且可以通过 Future 的 get()方法来获取返回值,
             * get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)
             * 方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
             */
//            Future<?> submit = service.submit(runnable);
        }

        /**
         * shutdown() 关闭线程池 线程池状态变为SHUTDOWN 。线程池不在接收新任务 但是队列里的任务得执行完毕
         *
         */
        service.shutdown();

        /**
         *  shutdownnow() 关闭线程池 线程状态变为 STOP 线程池会终止当前正在执行的任务,并停止处理排队的任务,并返回正在等待执行的list
         */
//        List<Runnable> runnables = service.shutdownNow();


        while (true){
            // isShutDown 当调用shutdown() 方法后返回 true
            // isTerminated 当调用 shutdown() 方法后,并且所有提交的任务后 返回true
            if(service.isTerminated()){
                System.out.println("线程池任务执行结束!!!");
                break;
            }
        }
        System.out.println(" 主线程执行结束");
    }

1.3 线程池

线程池底层维护了一个用于存放待执行任务的队列,和已被实例化的几个线程的线程池。调用线程的execute() 方法,则会通过线程池中的线程执行任务。如果任务过多,没有可用的线程,则任务会被放入线程池,等待之前被执行的任务执行结束。有空闲的线程可用,接着执行后续提交的任务。

1.3.1 Executors

主要用于提供线程池相关的操作,Executors类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。

  • public static ExecutorService newFiexedThreadPool(int Threads) 创建固定数目线程的线程池。
  • public static ExecutorService newCachedThreadPool():创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。缓存型池子通常用于执行一些生存期很短的异步型任务。
  • public static ExecutorService newSingleThreadExecutor():创建一个单线程化的Executor。任意时间,池子里只有一个线程。
  • public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

1.4 几个常见的对比

1.4.1 Runnable 和 Callable

  • Runnable 接口不会返回结果或者抛出检查异常,
  • Callable 接口可以返回结果和抛出异常,

工具类 Executors 可以实现 Runnable 对象和 Callable 对象之间的转换。

  • Executors.callable(Runnable task)
  • Executors.callable(Runnable task,Object resule)

1.4.2 execute() 和 submit()

  • execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
  • submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功 ,并且可以通过 Future 的 get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

1.4.3 shutdown() 和 shutdownNow()

  • shutdown() :关闭线程池,线程池的状态变为 SHUTDOWN。线程池不再接受新任务了,但是队列里的任务得执行完毕。
  • shutdownNow() :关闭线程池,线程的状态变为 STOP。线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。

1.4.4 isTerminated() VS isShutdown()

  • isShutDown 当调用 shutdown() 方法后返回为 true。
  • isTerminated 当调用 shutdown() 方法后,并且所有提交的任务完成后返回为 true