java大数据学习笔记(二) 线程池与应用

自己理解了一下,多处理系统下,过少的线程会浪费处理器资源,多的线程产生不必要的上下文切换反而降低效率。线程池是为了在处理大量的并发的小任务时创建合适数量的线程,减少了创建和销毁线程的开销,同时也能避免造成高额的上下文切换代价,通常应用于web服务器处理大量请求。

虽然java提供了4种常用的线程池实现,不过学习应当知其然更要知其所以然,所以还是先研究一番线程池的实现原理。

new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit,runnableTaskQueue, handler);

从构造方法可以看出不少端倪

  • corePoolSize 线程池基本大小,一开始也不太懂基本大小什么鬼,查了一下是当线程池小于这个大小的时候即使有空闲线程,也创建新的线程来执行新加入的任务。其实相当于一个初始化,把线程池控制在一个合适的大小以优化资源的使用。如果调用prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
  • maximumPoolSize 设定最多线程数量。
  • keepAliveTime,TimeUnit 线程存活时间,后者是单位。
  • runnableTaskQueue 这个可以看出java实现的线程池维护了一个任务队列,有多种阻塞队列可以选择。
  • handler 当线程池任务满了的时候对新添加的任务的处理策略。

之后用网上找到的一张图可以清楚地看到线程池的工作机制,调用execute(runnable)提交任务的时候,线程池按照下图流程工作
这里写图片描述

最后按照自己的理解总结一下,其实线程池本质是对象池,是对 对象的复用。这里的对象是指线程。线程池管理这些线程对象,并复用它们按照一定的策略来不断完成被添加到队列里的任务。联想到连接池,其实就是复用的对象变成了数据库的connection而已。

java实现的4中常用线程池,封装在Excutors中

Executors.newCachedThreadPool();

创建一个不限制大小的线程池,当无线程空闲时会创建新的线程处理任务

public class ThreadPoolTest {

    public static void main(String[] args) {

        ExecutorService myThreadPool = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++) {
            myThreadPool.execute(new Runnable() {

                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }

            });
        }

    }

}
Executors.newFixedThreadPool(int n);

创建最大为n的线程池。当没有线程空闲时任务会等待。

ExecutorService myThreadPool = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 100; i++) {
            myThreadPool.execute(new Runnable() {

                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }

            });
newScheduledThreadPool

这个线程池可以周期性的执行一批任务,类似批处理,每隔多长时间跑一次,不过是多线程的。

ExecutorService myThreadPool = Executors.newScheduledThreadPool(5);//设定线程池是5个线程的,每个周期执行执行5个任务,如果任务数量多于5个,那么需要等待。

        for (int i = 0; i < 5; i++) {
            ((ScheduledExecutorService) myThreadPool).scheduleAtFixedRate(new Runnable() {//调用这个方法执行周期的任务。

                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);//为了体现等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }, 0, 5, TimeUnit.SECONDS);//0s开始,每隔5s跑一次。for循环生成5个任务。所以每隔5s输出5个线程的名字一次,如果这里生成6个任务,第六个需要等待前面的完成。
        }




newSingleThreadExecutor

单线程的线程池,保证任务队列fifo。

ExecutorService myThreadPool = Executors.newSingleThreadExecutor();

for (int i = 0; i < 5; i++) {
myThreadPool.execute(new Runnable() {

@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

});

最后实践一下利用线程池来访问数据库做一些操作,简单写了个demo,因为3.21哪个服务器访问不了,所以自己找了一些数据,统计一个数据库下所有表的age字段的总值。 对每个表建立一个统计查询的任务,交到线程池处理。最终输出总的结果。 由于数据比较少,运行时间没什么参考意义,等明天服务器可以访问了再测试下。

package test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SqlThreadPoll {
    static ConnectionFactory cf = new ConnectionFactory();
    static ExecutorService pool = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        Connection conn = cf.getConnection();
        String sql = "select table_name from information_schema.tables where table_schema='test'  "; //选取所有表
        try {
            Statement st = conn.createStatement();
            ResultSet rs = st.executeQuery(sql);
            while (rs.next()) {
                pool.execute(new Work(rs.getString("table_name"), cf)); //每个表建立一个任务提交给线程池
            }
            st.close();
            pool.shutdown();
            while (true) {
                if (pool.isTerminated()) {//等待线程池任务全部结束
                    System.out.println(Source.result);
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

class Source {
    static long result;

    public static void add(long i) {
        synchronized (Source.class) {
            Source.result += i;
        }
    }
}

class Work implements Runnable {
    private final ConnectionFactory cf;
    private String sql = "select  sum(age)  from "; //对每个表计算年龄总数值

    Work(String table_name, ConnectionFactory cf) {
        this.cf = cf;
        sql = sql + table_name;
    }

    @Override
    public void run() {
        try {
            Connection conn = cf.getConnection();
            Statement st = conn.createStatement();
            ResultSet rs = st.executeQuery(sql);
            int i = 0;
            if (rs.next())
                i = rs.getInt(1);
            st.close();
            System.out.println(i);
            Source.add(i);
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

}

class ConnectionFactory { //单例模式返回一个数据库连接, 或者用连接池会快些?
    private final static String url = "jdbc:mysql://localhost/test";
    private final static String driver = "com.mysql.jdbc.Driver";
    private final static String user = "root";
    private final static String passwd = "root";

    private static Connection conn;
    static {
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, passwd);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }

    public Connection getConnection() {
            return conn;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值