java并发编程的艺术和并发编程这一篇就够了

java并发编程的艺术(精华提炼)

通常我们在使用编发编程时,主要目的是为了程序能够更快的处理,但是并不是说更多的线程就一定能够让程序变得足够快,有时候太多的线程反而消耗了更多的资源,反而让程序执行得更缓慢

一.CPU的上下文切换
就算是单核CPU是能够处理多线程任务的,它只是不停的切换线程来执行,让我们感觉是多线程执行

下图是串行执行和并发执行的耗时对比
在这里插入图片描述
从图中我们可以看到,在数量不达到千万级的时候,串行和并行的耗时几乎差不多,这是因为线程在创建和上下文切换时需要一定的时间开销.

如何减少上下文的切换似乎是我们在并发编程时需要注意的一点.

  • 无锁编发编程,多线程并发强占锁时,会引起频繁的上下文切换,我们可以使用分段式的方法来处理数据,不同的线程处理不同段的数据,避免使用锁
  • 使用最少的线程.避免创建大量无效线程,任务量很少的情况,会让大量线程处于等待状态,浪费资源

二.多线程在java中的使用
volatile和synchronize
当我们使用volatile变量修饰时会引发的两件事:
1.当前处理器的数据会回写到系统内存
2.这个回写内存会使处理器中的这个内存地址无效化
这两件事说明了什么呢.在java中使用volatile可以让多个线程共享一个变量,在多个线程获取这个变量的时候,该线程会等待正在改变这个变量的线程释放掉才会去读取,这让我们多线程在共享这个变量时,才能保持唯一性

synchronized实现同步的基础
1.对于普通方法,锁的是当前实例对象
2.对于静态方法.锁的是当前class对象
3.对于方法块,锁的是作用域里的所有对象
通俗一点说,一个普通方法锁,其实是锁的是实例对象来调用时,针对于这个实例对象来说他是同步的,如果我们有多个这个实例对象同时调用它内部同步方法时,实际上还是并发请求,对于静态方法来说,他在java中只有一份,那么无论调用几次他都是一个同步调用,最后的方法块类似于我们的普通方法,不同的地方就是锁的作用域内的所有对象都被加锁,属于这个线程的私有变量

三.并发编程模型的两个关键问题
1.线程之间如何通信
2.线程之间如何同步
大部分时间中,我们不会关心线程之间如何通信,我们在实际编程中会遇到线程之间如何同步一个变量的问题,使用volatile来修饰一个变量是一个非常好的方法,在jdk8之后的jvm内存模型中,对象的实例是存储于本地栈,一个变量在使用volatile后,该对象实例的引用还是在本地内存中,而该对象的的变量则被拷贝到了主内存中用于共享,不在私有,简单来说,就是各个线程任然保留有这个变量的内存地址只是不同的内存地址被指向了主内存中的同一个变量
在这里插入图片描述
三.Java并发容器和框架
ConcurrentHashMap的实现原理与使用
ConcurrentHashMap是线程安全且高效的HashMap
ConcurrentHashMap的结构图
ConcurrentHashMap的结构图

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色;HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和HashMap类似,是一种数组和链表结构。一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得与它对应的Segment锁

Java里的阻塞队列
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。

  1. LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  2. PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  3. DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  4. SynchronousQueue:一个不存储元素的阻塞队列。
  5. LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  6. LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

四.线程池
线程池是我们使用得最多的线程框架,合理地使用它可以为我们带来很多好处
1.降低资源消耗,避免重复创建线程
2.提高响应速度,保持一定量的等待线程,不用每次都重新创建线程
3.提高线程的管理性

这里不得不提一下阿里的java编码规范中明确的指出了:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险,


    /**
     * 配置线程池
     *
     * @return Executor线程池
     */
    @Bean("threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor getExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //线程池维护线程的最少数量
        executor.setCorePoolSize(10);
        //线程池维护线程的最大数量
        executor.setMaxPoolSize(50);
        //缓存队列
        executor.setQueueCapacity(99999);
        //对拒绝task的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //设置线程名称
        executor.setThreadNamePrefix(QINGDUO_UPMS_SERVICE);
        //允许的空闲时间
        executor.setKeepAliveSeconds(60);
        //执行初始化
        executor.initialize();
        return executor;
    }

五.并发编程实践
生产者和消费者模式

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,便有了生产者和消费者模式。

我写了一个简单的生产消费,但是并没有同步锁也没有同享变量,于是产生了消费者为-1的情况
在这里插入图片描述

于是我让这个变量在线程中共享,但是抛出了IllegalMonitorStateException异常,大致意思就是说抛出这个异常表明线程尝试等待一个对象的监视器或者去通知其他正在等待这个对象监视器的线程时,但是没有拥有这个监视器的所有权,这就证明了,其实对于共享变量操作时实际上只能有一个线程去操作
在这里插入图片描述
因为我们对锁的操作不正确,无法释放锁,所以我用ReentrantLock实现了

/**
 * 生产者和消费者,ReentrantLock的实现
 *
 */
public class Test1 {
    private static Integer count = 0;
    private static final Integer FULL = 10;
    //创建一个锁对象
    private Lock lock = new ReentrantLock();
    //创建两个条件变量,一个为缓冲区非满,一个为缓冲区非空
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    public static void main(String[] args) {
        Test1 test2 = new Test1();
        new Thread(test2.new Producer()).start();
        new Thread(test2.new Consumer()).start();
        new Thread(test2.new Producer()).start();
        new Thread(test2.new Consumer()).start();
        new Thread(test2.new Producer()).start();
        new Thread(test2.new Consumer()).start();
        new Thread(test2.new Producer()).start();
        new Thread(test2.new Consumer()).start();
    }
    class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //获取锁
                lock.lock();
                try {
                    while (count == FULL) {
                        try {
                            notFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count++;
                    System.out.println(Thread.currentThread().getName()
                            + "生产者生产,目前总共有" + count);
                    //唤醒消费者
                    notEmpty.signal();
                } finally {
                    //释放锁
                    lock.unlock();
                }
            }
        }
    }
    class Consumer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                lock.lock();
                try {
                    while (count == 0) {
                        try {
                            notEmpty.await();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName()
                            + "消费者消费,目前总共有" + count);
                    notFull.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值