美文网首页
详解多线程入门案例与java8的并行流

详解多线程入门案例与java8的并行流

作者: 平凡的柚子 | 来源:发表于2020-12-22 22:06 被阅读0次
    • 进程与线程
      进程是所有线程的集合,每一个线程是进程中的一条执行路径。
    • 多线程的创建方式,继承Thread\实现Runable
    /**
     * 第一种创建线程的方式,继承Thread
     */
    public class MultiThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"....."+i);
            }
        }
    }
    
    /**
     * 第二种,实现Runnable接口,更易于扩展。(多实现,单继承)
     */
    public class MultiThread2 implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"---"+i);
            }
        }
    }
    /**
     * 测试类
     */
    public class TestThread {
        public static void main(String[] args) {
            System.out.println(Thread.currentThread().getName()+"---开始");
            //1.多线程创建 继承Thread
            MultiThread multiThread = new MultiThread();
            Thread thread = new Thread(multiThread,"线程继承Thread-1");
            Thread thread2 = new Thread(multiThread,"线程继承Thread-2");
            //2.启动多线程调用的是start(),而不是run()
            thread.start();
            thread2.start();
            //3.打印效果 当前线程:线程1
            /**********************************/
            //2  实现Runnable
            Runnable runnable = new MultiThread2();
            Thread thread3 = new Thread(runnable,"线程实现Runnable-1");
            Thread thread4 = new Thread(runnable,"线程实现Runnable-2");
            thread3.start();
            thread4.start();
            //3 匿名内部类的方式
            Thread thread5 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 3; i++) {
                        System.out.println(Thread.currentThread().getName()+"匿名内部类----"+i);
                    }
                }
            });
            thread5.start();
            System.out.println(Thread.currentThread().getName()+"---结束");
        }
    }
    

    效果展示:并不是按照自上而下的执行


    • 线程的几个状态


    1. 新建状态:当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码。
    2. 就绪状态:一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
    3. 运行状态:当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。
    4. 阻塞状态: 线程运行过程中,可能由于各种原因进入阻塞状态:
      1>线程通过调用sleep方法进入睡眠状态;
      2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
      3>线程试图得到一个锁,而该锁正被其他线程持有;
      4>线程在等待某个触发条件;
    5. 死亡状态:有两个原因会导致线程死亡:1)run方法正常退出而自然死亡,2)一个未捕获的异常终止了run方法而使线程猝死。为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

    简单需求:目前蚂蚁课堂有10万个用户,现在蚂蚁课堂需要做活动,给每一个用户发送一条祝福短信

    /**
     * 提交实体
     */
    public class UserThread extends Thread {
        private List<UserEntity> uList;
    
        public UserThread(List<UserEntity> uList){
            this.uList = uList;
        }
        public List<UserEntity> getuList() {
            return uList;
        }
    
        public void setuList(List<UserEntity> uList) {
            this.uList = uList;
        }
    
        @Override
        public void run() {
            uList.stream().forEach(userEntity ->  {
                System.out.println("threadName:" + Thread.currentThread().getName() + "-用户记录序号:" + userEntity.getUno()
                        + "---用户名称:" + userEntity.getUname());
                //短信发送接口
            });
        }
    }
    /**
     * 测试类
     */
    public class TestThread {
        public static void main(String[] args) {
            //所有记录
            List<UserEntity> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                UserEntity userEntity = new UserEntity();
                userEntity.setUno("用户id"+i);
                userEntity.setUname("用户名字"+i);
                list.add(userEntity);
            }
            //将数据进行分割,每组四千,这个每组数据量最好取配置,使用高峰期调低,保证不会超时
            int everyNum  = 20;
            List<List<UserEntity>> lists= spiltList(list,everyNum);
            for (int i = 0; i < lists.size(); i++) {
                Thread th = new UserThread(lists.get(i));
                th.start();
            }
            //第二种java8 并行流,底层:ForkJoin  --todo
            //使用并行流的时候,最好不要修改数据,如果需要map里的返回值,则用Collectors进行收集,不要foreach里用list.add()
            lists.parallelStream().forEach( lst -> {
                lst.parallelStream().forEach( userEntity -> {
                    System.out.println("threadName:" + Thread.currentThread().getName() + "-用户记录序号:" + userEntity.getUno()
                            + "---用户名称:" + userEntity.getUname());
                });
            });
        }
    
        private static List<List<UserEntity>> spiltList(List<UserEntity> list, int everyNum) {
            //总数据量
            int total = list.size();
            //分组数量
            int group = total%everyNum == 0 ? total/everyNum : total/everyNum +1 ;
            //java8 stream
            List userList = Stream.iterate(0, n->n+1).
                            limit(group).parallel().
                            map(seed -> {
                                         List<UserEntity> list1 =  list.stream().skip(seed * everyNum).limit(everyNum).
                                                        collect(Collectors.toList());
                                        return list1;
                                    }
                            ).collect(ArrayList::new,ArrayList::add,List::addAll);
            return userList;
        }
    }
    
    

    打印效果如图,不是按顺序打印,但这不正好就是多线程需要的效果的了吗?



    最新2020整理收集的一些高频面试题(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,
    需要获取这些内容的朋友请加Q君样:
    11604713672

    相关文章

      网友评论

          本文标题:详解多线程入门案例与java8的并行流

          本文链接:https://www.haomeiwen.com/subject/lsaunktx.html