• (七) 线程并发工具类之Semaphore,Callable、Future和FutureTask


    Semaphore

    基本概念:
    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
    使用场景:
    Semaphore可以用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接。
    假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发地读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有10个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,就可以使用Semaphore来做流量控制。
    Semaphore的构造方法Semaphore(int permits)接收一个整型的数字,表示可用的许可证数量。Semaphore的用法也很简单,首先线程使用Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。
    Semaphore还提供一些其他方法,具体如下:

    1. intavailablePermits():返回此信号量中当前可用的许可证数。
    2. intgetQueueLength():返回正在等待获取许可证的线程数。
    3. booleanhasQueuedThreads():是否有线程正在等待获取许可证。
    4. void reducePermits(int reduction):减少reduction个许可证,是个protected方法。
    5. Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方法。

    图解:

    数据库连接配置:

    
    import java.sql.*;
    import java.util.Map;
    import java.util.Properties;
    import java.util.concurrent.Executor;
    
    /**
     * @author monco
     * @date 2020/5/20
     * @description: 实现数据库连接接口
     */
    public class SqlConnectImpl implements Connection {
    
        /*拿一个数据库连接*/
        public static final Connection fetchConnection() {
            return new SqlConnectImpl();
        }
    
    
        @Override
        public Statement createStatement() throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return null;
        }
    
        @Override
        public CallableStatement prepareCall(String sql) throws SQLException {
            return null;
        }
    
        @Override
        public String nativeSQL(String sql) throws SQLException {
            return null;
        }
    
        @Override
        public void setAutoCommit(boolean autoCommit) throws SQLException {
    
        }
    
        @Override
        public boolean getAutoCommit() throws SQLException {
            return false;
        }
    
        @Override
        public void commit() throws SQLException {
    
        }
    
        @Override
        public void rollback() throws SQLException {
    
        }
    
        @Override
        public void close() throws SQLException {
    
        }
    
        @Override
        public boolean isClosed() throws SQLException {
            return false;
        }
    
        @Override
        public DatabaseMetaData getMetaData() throws SQLException {
            return null;
        }
    
        @Override
        public void setReadOnly(boolean readOnly) throws SQLException {
    
        }
    
        @Override
        public boolean isReadOnly() throws SQLException {
            return false;
        }
    
        @Override
        public void setCatalog(String catalog) throws SQLException {
    
        }
    
        @Override
        public String getCatalog() throws SQLException {
            return null;
        }
    
        @Override
        public void setTransactionIsolation(int level) throws SQLException {
    
        }
    
        @Override
        public int getTransactionIsolation() throws SQLException {
            return 0;
        }
    
        @Override
        public SQLWarning getWarnings() throws SQLException {
            return null;
        }
    
        @Override
        public void clearWarnings() throws SQLException {
    
        }
    
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
            return null;
        }
    
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
            return null;
        }
    
        @Override
        public Map<String, Class<?>> getTypeMap() throws SQLException {
            return null;
        }
    
        @Override
        public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
    
        }
    
        @Override
        public void setHoldability(int holdability) throws SQLException {
    
        }
    
        @Override
        public int getHoldability() throws SQLException {
            return 0;
        }
    
        @Override
        public Savepoint setSavepoint() throws SQLException {
            return null;
        }
    
        @Override
        public Savepoint setSavepoint(String name) throws SQLException {
            return null;
        }
    
        @Override
        public void rollback(Savepoint savepoint) throws SQLException {
    
        }
    
        @Override
        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
    
        }
    
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
            return null;
        }
    
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
            return null;
        }
    
        @Override
        public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
            return null;
        }
    
        @Override
        public Clob createClob() throws SQLException {
            return null;
        }
    
        @Override
        public Blob createBlob() throws SQLException {
            return null;
        }
    
        @Override
        public NClob createNClob() throws SQLException {
            return null;
        }
    
        @Override
        public SQLXML createSQLXML() throws SQLException {
            return null;
        }
    
        @Override
        public boolean isValid(int timeout) throws SQLException {
            return false;
        }
    
        @Override
        public void setClientInfo(String name, String value) throws SQLClientInfoException {
    
        }
    
        @Override
        public void setClientInfo(Properties properties) throws SQLClientInfoException {
    
        }
    
        @Override
        public String getClientInfo(String name) throws SQLException {
            return null;
        }
    
        @Override
        public Properties getClientInfo() throws SQLException {
            return null;
        }
    
        @Override
        public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
            return null;
        }
    
        @Override
        public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
            return null;
        }
    
        @Override
        public void setSchema(String schema) throws SQLException {
    
        }
    
        @Override
        public String getSchema() throws SQLException {
            return null;
        }
    
        @Override
        public void abort(Executor executor) throws SQLException {
    
        }
    
        @Override
        public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
    
        }
    
        @Override
        public int getNetworkTimeout() throws SQLException {
            return 0;
        }
    
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    }
    
    

    ** Semaphore 进行限流使用:**

    
    import java.sql.Connection;
    import java.util.LinkedList;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author : monco
     * @date : 2019/10/11 1:46
     * className: DBPoolSemaphore
     * description: Semaphore 进行限流使用
     */
    public class DBPoolSemaphore {
    
        private final static int POOL_SIZE = 10;
    
        /**
         * 两个指示器,分别表示池子还有可用连接和已用连接
         */
        private final Semaphore useful, useless;
    
        /**
         * 创建存储连接的容器
         */
        private static LinkedList<Connection> pool = new LinkedList<Connection>();
    
        /**
         * 初始化连接池
         */
        static {
            for (int i = 0; i < POOL_SIZE; i++) {
                pool.addLast(SqlConnectImpl.fetchConnection());
            }
        }
    
        public DBPoolSemaphore() {
            this.useful = new Semaphore(10);
            this.useless = new Semaphore(0);
        }
    
        /**
         * 归还连接
         *
         * @param connection
         * @throws InterruptedException
         */
        public void returnConnect(Connection connection) throws InterruptedException {
            if (connection != null) {
                System.out.println("当前有" + useful.getQueueLength() + "个线程等待数据库连接!!"
                        + "可用连接数:" + useful.availablePermits());
                useless.acquire();
                synchronized (pool) {
                    pool.addLast(connection);
                }
                useful.release();
            }
        }
    
        /**
         * 获取连接
         *
         * @return
         * @throws InterruptedException
         */
        public Connection takeConnect() throws InterruptedException {
            useful.acquire();
            Connection connection;
            synchronized (pool) {
                connection = pool.removeFirst();
            }
            useless.release();
            return connection;
        }
    }
    
    
    

    ** 测试类:**

    
    import java.sql.Connection;
    import java.util.Random;
    
    /**
     * 类说明:测试数据库连接池
     */
    public class AppTest {
    
        private static DBPoolSemaphore dbPool = new DBPoolSemaphore();
    
        private static class BusiThread extends Thread {
            @Override
            public void run() {
                //让每个线程持有连接的时间不一样
                Random r = new Random();
                long start = System.currentTimeMillis();
                try {
                    Connection connect = dbPool.takeConnect();
                    System.out.println("Thread_" + Thread.currentThread().getId()
                            + "_获取数据库连接共耗时【" + (System.currentTimeMillis() - start) + "】ms.");
                    //模拟业务操作,线程持有连接查询数据
                    SleepTools.ms(100 + r.nextInt(100));
                    System.out.println("查询数据完成,归还连接!");
                    dbPool.returnConnect(connect);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 50; i++) {
                Thread thread = new BusiThread();
                thread.start();
            }
        }
    
    }
    
    

    ** 运行结果:**

    Callable、Future和FutureTask

    我们在创建的线程的时候,在实现Runnable的时候,因为Runnable是一个接口,在它里面只声明了一个run()方法,由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。
    Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call(),这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。
    Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
    image
    因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask

    FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

    因此我们通过一个线程运行Callable,但是Thread不支持构造方法中传递Callable的实例,所以我们需要通过FutureTask把一个Callable包装成Runnable,然后再通过这个FutureTask拿到Callable运行后的返回值。

    要new一个FutureTask的实例,有两种方法

    
    import java.util.Random;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * @author : monco
     * @date : 2019/10/11 1:46
     * className: UseFuture
     * description: 实现future
     */
    public class UseFuture {
    
        /**
         * 实现 Callable接口,允许有返回值
         */
        private static class UseCallable implements Callable<Integer> {
            private int sum;
    
            @Override
            public Integer call() throws Exception {
                System.out.println("Callable子线程开始计算!");
                Thread.sleep(1000);
                for (int i = 0; i < 5000; i++) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("Callable子线程计算任务中断!");
                        return null;
                    }
                    sum = sum + i;
                    System.out.println("sum=" + sum);
                }
                System.out.println("Callable子线程计算结束!结果为: " + sum);
                return sum;
            }
        }
    
        public static void main(String[] args)
                throws InterruptedException, ExecutionException {
    
            UseCallable useCallable = new UseCallable();
            //包装
            FutureTask<Integer> futureTask = new FutureTask<>(useCallable);
            Random r = new Random();
            new Thread(futureTask).start();
    
            Thread.sleep(1);
            if (r.nextInt(100) > 50) {
                System.out.println("Get UseCallable result = " + futureTask.get());
            } else {
                System.out.println("Cancel................. ");
                futureTask.cancel(true);
            }
    
        }
    }
    
    
  • 相关阅读:
    ASP.NET Core 中的管道机制
    常见的 HttpModule
    IIS+Asp.Net Mvc必须知道的事(解决启动/重启/自动回收站点后第一次访问慢问题)
    ASP.NET三剑客 HttpApplication HttpModule HttpHandler 解析
    Js国际化
    MethodImplOptions
    Java守护线程普通线程的例子
    Java启动新线程的几种方式(Runnable、Callable、CompletableFuture)
    Tomcat源码分析(3)-容器Container整体架构
    Tomcat源码分析(2)-连接器Connector整体架构
  • 原文地址:https://www.cnblogs.com/monco-sxy/p/13234949.html
Copyright © 2020-2023  润新知