资讯专栏INFORMATION COLUMN

并发编程高级篇一----JDK多任务执行框架,Concurrent.util工具类以及锁的高级深化

Shonim / 2038人阅读

摘要:的多任务执行框架提供了一套线程框架来帮助开发者有效的进行线程控制,扮演线程工厂的角色,其创建线程的方法如下返回固定数量的线程池,该方法的线程数始终不变。

1.Jdk的多任务执行框架

JDK提供了一套线程框架Executor来帮助开发者有效的进行线程控制,Executors扮演线程工厂的角色,其创建线程的方法如下

newFixedThreadPool() 返回固定数量的线程池,该方法的线程数始终不变。若线程空闲则立即执行 否则暂缓到队列中

newSingleThreadPool() 创建一个线程池,若线程空闲则立即执行 否则暂缓到队列中

newCachedThreadPool() 返回一个可根据实际情况调整线程个数的线程池 ,若线程空闲则立即执行 否则暂缓到队列中

newScheduledThreadPool() 返回一个ScheduledExecutorService对象,但该线程可以执行线程的数量(schedule 排定/安排/目录)

        ScheduledExecutorService scheduler = 
        Executors.newScheduledThreadPool(10);
        
        //command 就是一个Thread
        ScheduledFuture scheduledTask =
         scheduler.scheduleWithFixedDelay(command,5,1, TimeUnit.SECONDS);

若Executors工厂类无法满足我们的需求,可以自己去创建自定义的线程池。自定义线程池的构造方法如下

    
public ThreadPoolExecutor(int corePoolSize,//核心线程数
                          int maximumPoolSize,//最大线程数
                          long keepAliveTime,//线程的空闲时间
                          TimeUnit unit,//给定单元粒度的时间段 
                          BlockingQueue workQueue,//有界、无界队列
                          RejectedExecutoionHandler handler//任务拒绝策略
                          ){.....}

使用什么队列对该构造方法来说比较关键

使用有界队列 如有任务需要执行 如果实际线程数

    如果实际线程数>corePoolSize,则将任务添加到缓存队列
    如果缓存队列已满 ,总线程maximumPoolSize, 则执行拒绝策略

使用无界队列LinkedBlockingQueue 除非系统资源耗尽 否则不会出现入队失败

    如有任务需要执行 如果实际线程数corePoolSize,则将任务添加到缓存队列,
                                               直到资源耗尽 
BlockingQueue queue =
                    //new LinkedBlockingQueue();//无界队列
                    new ArrayBlockingQueue(10);//有界队列
            ExecutorService executor  = new ThreadPoolExecutor(
                    5,         //core
                    10,     //max
                    120L,     //120s
                    TimeUnit.SECONDS,
                    queue);

JDK的拒绝策略

AbortPolicy:直接抛出异常 系统正常工作

CallerRunsPolicy:只要线程池未被关闭 尝试运行被丢弃的任务

DiscardOldestPolicy:丢失最老的请求 尝试提交当前任务

DiscardPolicy:丢弃无法处理的任务不给予处理

JDK提供的拒绝策略不友好,可以自定义拒绝策略,实现RejectedExecutionHandler接口(添加日志等等)

    public class MyRejected implements RejectedExecutionHandler {
    
        public MyRejected(){}

        @Override
        public void rejectedExecution(Runnable r, 
        ThreadPoolExecutor executor) {
            System.out.println("自定义处理");
            System.out.println("当前被拒绝的任务为"+r.toString());
        }
    }

2.Concurrent.util工具类详解

CyclicBarrier
假设每一个线程代表一个运动员,当运动员都准备好了,才能一起出发。

CyclicBarrier barrier = new CyclicBarrier(3);

CountDownLatch
经常用于监听某些初始化操作,当初始化执行完毕以后,通知主线程继续工作

final CountDownLatch countDownLatch = new CountDownLatch(2);

Callable 和Future使用
Futrue模式费用适合在处理耗时很长的业务逻辑进行使用,可以有效的减小系统的影响,
提高系统的吞吐量

    public class UseFuture implements Callable{
        private String para;
        
        public UseFuture(String para){
            this.para = para;
        }
        
        /**
         * 这里是真实的业务逻辑,其执行可能很慢
         */
        @Override
        public String call() throws Exception {
            //模拟执行耗时
            Thread.sleep(5000);
            String result = this.para + "处理完成";
            return result;
        }
        
        //主控制函数
        public static void main(String[] args) throws Exception {
            String queryStr = "query";
            //构造FutureTask,并且传入需要真正进行业务逻辑处理的类,
            //该类一定是实现了Callable接口的类
            FutureTask future = 
            new FutureTask(new UseFuture(queryStr));
            
            FutureTask future2 = 
            new FutureTask(new UseFuture(queryStr));
            //创建一个固定线程的线程池且线程数为1,
            ExecutorService executor = Executors.newFixedThreadPool(2);
            //这里提交任务future,则开启线程执行RealData的call()方法执行
            //submit和execute的区别: 
            //第一点是submit可以传入实现Callable接口的实例对象,
            // 第二点是submit方法有返回值
            
            Future f1 = executor.submit(future);        
            //多带带启动一个线程去执行的
            Future f2 = executor.submit(future2);
            System.out.println("请求完毕");
            
            try {
                //这里可以做额外的数据操作,也就是主程序执行其他业务逻辑
                System.out.println("处理实际的业务逻辑...");
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //调用获取数据方法,如果call()方法没有执行完成,则依然会进行等待
            System.out.println("数据:" + future.get());
            System.out.println("数据:" + future2.get());
            
            executor.shutdown();
        }
    
    }

Semaphore信号量
可以控制系统的流量,拿到线程的信号量则访问否则等待
通过acquire和release来获取和释放线程

 final Semaphore semp = new Semaphore(5);

3.锁的高级深化

Lock and Condition
使用synchronized关键字可以实现线程间的同步互斥工作

使用Lock对象也可以实现同步互斥

如果多个线程之间需要实现协作 使用Object的wait和nofity,notifyAll

在使用Lock的时候可以使用一个新的等待/通知的类Condition 只针对一个具体的锁

    public class UseCondition {

        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
        
        public void method1(){
            try {
                lock.lock();
                System.out.println("当前线程:" + 
    Thread.currentThread().getName() + "进入等待状态..");
                Thread.sleep(3000);
                System.out.println("当前线程:" + 
    Thread.currentThread().getName() + "释放锁..");
                condition.await();    // Object wait 
                System.out.println("当前线程:" + 
    Thread.currentThread().getName() +"继续执行...");
                condition.signal();        //Object notify
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

ReentrantLock重入锁

    private Lock lock = new ReentrantLock(boolean isFair);//是否为公平锁

ReentrantReadWriteLock读写锁
核心是实现读写分离 在都多写少的情况下 性能高于重入锁

public class UseReentrantReadWriteLock {

    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private ReadLock readLock = rwLock.readLock();
    private WriteLock writeLock = rwLock.writeLock();
    
    public void read(){
        try {
            readLock.lock();
            System.out.println("当前线程:" + 
Thread.currentThread().getName() + "进入...");
            Thread.sleep(3000);
            System.out.println("当前线程:" + 
Thread.currentThread().getName() + "退出...");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
        }
    }
    
    public void write(){
        try {
            writeLock.lock();
            System.out.println("当前线程:" + 
Thread.currentThread().getName() + "进入...");
            Thread.sleep(3000);
            System.out.println("当前线程:" + 
Thread.currentThread().getName() + "退出...");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }
}

锁的优化

避免死锁

减少锁的持有时间

减少锁的粒度

锁的分离

尽量使用无锁的操作 比如原子类操作

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70552.html

相关文章

  • BATJ都爱问的线程面试题

    摘要:今天给大家总结一下,面试中出镜率很高的几个多线程面试题,希望对大家学习和面试都能有所帮助。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。使用可以禁止的指令重排,保证在多线程环境下也能正常运行。 下面最近发的一些并发编程的文章汇总,通过阅读这些文章大家再看大厂面试中的并发编程问题就没有那么头疼了。今天给大家总结一下,面试中出镜率很高的几个多线...

    高胜山 评论0 收藏0
  • 后端好书阅读与推荐

    摘要:后端好书阅读与推荐这一两年来养成了买书看书的习惯,陆陆续续也买了几十本书了,但是一直没有养成一个天天看书的习惯。高级程序设计高级程序设计第版豆瓣有人可能会有疑问,后端为啥要学呢其实就是为了更好的使用做铺垫。 后端好书阅读与推荐 这一两年来养成了买书看书的习惯,陆陆续续也买了几十本书了,但是一直没有养成一个天天看书的习惯。今天突然想要做个决定:每天至少花1-3小时用来看书。这里我准备把这...

    clasnake 评论0 收藏0
  • 后端好书阅读与推荐

    摘要:后端好书阅读与推荐这一两年来养成了买书看书的习惯,陆陆续续也买了几十本书了,但是一直没有养成一个天天看书的习惯。高级程序设计高级程序设计第版豆瓣有人可能会有疑问,后端为啥要学呢其实就是为了更好的使用做铺垫。 后端好书阅读与推荐 这一两年来养成了买书看书的习惯,陆陆续续也买了几十本书了,但是一直没有养成一个天天看书的习惯。今天突然想要做个决定:每天至少花1-3小时用来看书。这里我准备把这...

    Juven 评论0 收藏0
  • Java并发

    摘要:对象改变条件对象当前线程要等待线程终止之后才能从返回。如果线程在上的操作中被中断,通道会被关闭,线程的中断状态会被设置,并得到一个。清除线程的中断状态。非公平性锁虽然可能造成饥饿,但极少的线程切换,保证其更大的吞吐量。 声明:Java并发的内容是自己阅读《Java并发编程实战》和《Java并发编程的艺术》整理来的。 showImg(https://segmentfault.com/im...

    SKYZACK 评论0 收藏0
  • Java面试 32个核心必考点完全解析

    摘要:如问到是否使用某框架,实际是是问该框架的使用场景,有什么特点,和同类可框架对比一系列的问题。这两个方向的区分点在于工作方向的侧重点不同。 [TOC] 这是一份来自哔哩哔哩的Java面试Java面试 32个核心必考点完全解析(完) 课程预习 1.1 课程内容分为三个模块 基础模块: 技术岗位与面试 计算机基础 JVM原理 多线程 设计模式 数据结构与算法 应用模块: 常用工具集 ...

    JiaXinYi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<