并发编程8-

使用线程池并发的时候有可能出现问题

线程饥饿死锁

如果第一个任务依赖于第二个任务的某个状态改变,而第二个任务要等待第一个任务执行完才能执行,就会造成饥饿死锁。

耗时阻塞

如果任务的耗时时间不同,最后任务可能会被一些耗时大的操作所占用,造成阻塞。


上面两个在足够大的线程池中都不会有问题,因此如何界定一个线程池的大小是很重要的。


通常决定一个池应该是多大的条件是:

稀缺资源,比如JDBC连接,如果JDBC连接为10个,那么同时的线程池就应该不超过10。

CPU个数,通常我们保证Ncpu + 1个线程的池来保证最优的利用率。

如果包含了阻塞或者等待的操作,那么可能不是所有的线程都处于计算中。比如,等待时间/计算时间=2,这个时候原来的1个CPU就可以同时给3个线程来使用,这个时候的基数应该为(1 + 等待时间/计算时间)

考虑上面多重因素后,保证CPU尽可能的出于运算状态,并且同时不会造成阻塞或者内存溢出等问题,达到最大的利用率就是池的最适合大小。


ThreadPoolExecutor中有核心池的大小,最大池的大小,和存活时间来共同管理着线程的创建和销毁。

核心池意味着大于他的超过存活时间的会被销毁掉, 最大池意味着再提交进来的会进入阻塞队列。不会创建新的线程。

比如newCachedThreadPool最大池这时为了Integer.MAX_VALUE,核心池设置为0,意味着可以无限增长,一旦不用一分钟后就会销毁。


管理队列

为了应付激增的请求,以及控制对CPU和内存的使用,可以使用队列。

排队的方式有三种:无限队列、有限队列和同步移交。

newFixedThreadPool使用的是一个无限LinkedBlockingQueue。 

有限队列可以避免资源耗尽,但是会带来潜在的吞吐量约束。

对于庞大或者无限的池,可以使用SynchronousQueue做同步移交, 绕开队列,放在其中的任务必须有消费者,如果没有就会创建,如果饱和会执行饱和策略,比如被拒绝掉。


饱和策略

当有限队列充满后,饱和策略开始起作用。 ThreadPoolExecutor的饱和策略铜鼓setRejectedExecutionHandler来修改。

通常有这么集中方式:  终止(抛出异常)、遗弃(通常是遗弃最久的), 返回给调用者Executor.CallerRunsPolicy(),让调用者自己执行。


线程工厂

在需要的时候给线程池创建一个线程,比如处理UncaughtExceptionHandler的时候,这个时候我们可能需要使用日志记录一下,可以线程工厂中创建线程来完成这个操作。


扩展ThreadPoolExecutor

提供了几个钩子方法用来进行扩展beforeExecute, afterExecute, termiante.


并发递归:

public class TestCallable {
    public static void main(String[] args) throws ParserConfigurationException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder build = factory.newDocumentBuilder();
        Document doc = build.newDocument();
        Element root = doc.createElement("root");
        doc.appendChild(root);
        for (int i = 0; i < 30; i++) {
            Element e = doc.createElement("second" + i);

            for (int j = 0; j < 100; j++) {
                Element f = doc.createElement(e.getNodeName() + j);
                e.appendChild(f);
            }
            root.appendChild(e);
        }
        long start = System.currentTimeMillis();
        StringBuilder sb1 = new StringBuilder();
        list1(sb1, doc.getDocumentElement());
        long end = System.currentTimeMillis();
        System.out.println(sb1.toString());
        System.out.println(end - start);

        long start2 = System.currentTimeMillis();
        StringBuffer sb2 = new StringBuffer();
        ExecutorService exec = Executors.newFixedThreadPool(50);
        list2(exec, sb2, doc.getDocumentElement());
        try {
            exec.awaitTermination(0, TimeUnit.MILLISECONDS);
            exec.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sb2.toString());
        long end2 = System.currentTimeMillis();
        System.out.println(end2 - start2);
    }

    private static void list2(ExecutorService exec,final StringBuffer sb2, Node element) {
        if(element.hasChildNodes()){
            for (int i = 0; i < element.getChildNodes().getLength(); i++) {
                final Node n = element.getChildNodes().item(i);
                exec.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            TimeUnit.MILLISECONDS.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        sb2.append(n.getNodeName());
                    }
                });
                list2(exec, sb2, n);
            }
        }
    }

    private static void list1(StringBuilder sb1, Node element) {
        if(element.hasChildNodes()){
            for (int i = 0; i < element.getChildNodes().getLength(); i++) {
                Node n = element.getChildNodes().item(i);
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sb1.append(n.getNodeName());
                list1(sb1, n);
            }
        }

    }
}
把线性的程序变为并发,如上可以大大的缩短执行时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值