学习《java并发编程的艺术》Chapter1

多线程执行需要考虑到的开销:

    1)线程创建开销

    2)CPU上下文切换开销

引起CPU上下文切换的条件:

    1)时间片执行完毕

    2)线程在时间片执行完毕前阻塞或结束

所以要减少CPU上下文切换的开销,从以上两点考虑,可以有以下做法

    1)无锁并发编程。

    2)CAS算法。在juc包下,有很多类是使用了CAS算法来更新数据的。CAS(compare and swap),有兴趣的同学自行查阅。

    3)使用最少线程。

    4)协程。在单线程里实现多任务的调度。主要应用场景为:如果存在长时间的IO操作,这个时候线程一直处于阻塞状态,在多线程场景下更会将资源占用的问题放大,使用协程自行维持多个任务的切换,能够有效减少开销。

下例使用了无锁并发编程和CAS算法实现1到1千万的累加。至于是不是最少线程,我才懒得去慢慢试呢 : )

而协程的话,在java里实在是太不常见了,有兴趣的同学自行查阅。下例中使用的callable和future姑且用来帮助理解协程吧。

public class Test1 {

    private static AtomicLong res = new AtomicLong(0);

    public static void main(String[] args) throws Exception{
        System.out.println(count());
    }

    /***
     * 计算1到1千万的和
    */
    private static long count() throws Exception{
        int n = 10000000;
        int num = 100;
        long itemLength = n / num;
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<Long>> futures = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Future<Long> future = executorService.submit(new CountThread(i, itemLength));
            futures.add(future);
        }
        for (Future<Long> future: futures) {
            res.addAndGet(future.get());
        }
        return res.get();
    }

    private static long countItem(long start, long end) {
        boolean isOdd = (end - start + 1) % 2 == 1;
        int pairs = (int) ((end - start + 1) / 2);
        return isOdd ? (long) ((pairs + 0.5) * (start + end)) : pairs * (start + end);
    }

    static class CountThread implements Callable<Long> {

        private int i;
        private long itemLength;

        CountThread(int i, long itemLength) {
            this.i = i;
            this.itemLength = itemLength;
        }

        @Override
        public Long call() throws Exception {
            return countItem(i * itemLength + 1, (i + 1) * itemLength);
        }
    }

}

死锁:

    简单来说就是,桌上有一碗汤和一个勺子,A拿着勺子B拿着汤,A和B都想喝汤,但是A不想把勺子给B,B不想把汤给A,两个人就僵着了。示例如下:

public class Test1 {

    private static Object resource1 = new Object();
    private static Object resource2 = new Object();

    public static void main(String[] args) {
        new Test1().deadLock();
    }

    private void deadLock() {
        new Thread(() -> {
            synchronized (resource1) {
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resource2) {
                    System.out.println("Thread1 get all resource");
                }
            }
        }).start();
        new Thread(() -> {
            synchronized (resource2) {
                synchronized (resource1) {
                    System.out.println("Thread2 get all resource");
                }
            }
        }).start();
    }
}

防止死锁的方法:其实就在于编程习惯上,要自行去规避一些可能造成死锁的场景。比如避免一个线程同时获取多个锁或锁内同时占用多个资源,比如避免解锁失败的情况等待。

资源限制:

这一概念主要是出现在硬件限制上,比如带宽、CPU、硬盘读写速度等。软件限制则是人为设定的数据库的连接数和socket连接数等。

解决途径也从软硬件角度出发:

硬件方面,哪个不行换哪个咯,单机没法解决的问题就用集群咯。软件方面则回到了并发的开销问题上。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值