zoukankan      html  css  js  c++  java
  • 线程基本知识

    线程的状态以及状态之间的切换

    线程从创建到死亡有七个状态,分别是初始状态,准备运行,运行状态,阻塞状态,睡眠状态(超时等待状态),等待状态,死亡状态,关系如图所示:

    线程的创建

    实现线程的几种方式:

    • 继承Thread类
    • 实现Runnable接口
    • 匿名内部类的方式
    • 带返回值的线程
    • 定时器
    • 线程池实现
    • Lambda表达式实现

    继承Thread

    // 无名
    public class Demo01 extends Thread {
        @Override
        public void run() {
            System.out.println(getName() + "执行了。。。");
        }
    
        public static void main(String[] args) {
            Demo01 d1 = new Demo01();
            Demo01 d2 = new Demo01();
            d1.start();
            d2.start();
        }
    }
    
    // 有名
    public class Demo01 extends Thread {
        @Override
        public void run() {
            System.out.println(getName() + "执行了。。。");
        }
    
        public Demo01(String name) {
            super(name);
        }
    
        public static void main(String[] args) {
            Demo01 d1 = new Demo01("first thread");
            Demo01 d2 = new Demo01("second thread");
            d1.start();
            d2.start();
        }
    }
    

    实现Runnable接口

    实现了runnable接口的类是作为一个线程任务存在的,需要将实例化后的对象传入Thread中

    public class Demo02 implements Runnable {
        @Override
        public void run() {
            System.out.println("线程启动");
        }
    
        public static void main(String[] args) {
            Thread t = new Thread(new Demo02());
            t.start();
        }
    }
    

    匿名内部类实现

    // 继承Thread类的方式
    public class Demo03 {
        public static void main(String[] args) {
            new Thread() {
                @Override
                public void run() {
                    System.out.println("启动线程。。。");
                }
            }.start();
        }
    }
    
    // 实现Runnable接口的方式
    public class Demo03 {
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程启动。。。");
                }
            }).start();
        }
    }
    

    带返回值的线程

    上面创建的线程都是不带返回值以及不能抛异常的线程,接下来实现一种带返回值的线程

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class Demo04 implements Callable<Integer> {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            Demo04 d4 = new Demo04();
    
            FutureTask<Integer> task = new FutureTask<>(d4);
            Thread t = new Thread(task);
            t.start();
            System.out.println("计算结果是:" + task.get());
        }
    
    
        @Override
        public Integer call() throws Exception {
            System.out.println("正在进行紧张的计算。。。");
            Thread.sleep(3000);
            return 1;
        }
    }
    

    定时器

    定时器Timer通过schedule方法主席那个定时任务,可以指定延时多久执行,每隔多久执行,甚至是指定一个日期执行;或者是指定第一次执行的日期,然后每隔多久执行等等。

    import java.util.Timer;
    import java.util.TimerTask;
    
    public class Demo05 {
        public static void main(String[] args) {
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    // 定时器执行的任务
                    System.out.println("定时器正在执行");
                }
            }, 0, 1000);   // 延时0秒执行,每隔一秒执行一次
        }
    }
    

    基于线程池实现

    线程池用于降低线程创建和销毁的开销,需要线程时直接从池子里面拿,不需要时也不会销毁而是放回池子里面等待下一次使用,典型的拿空间换时间。

    Executors创建

    线程池最顶级的接口是Executor,Executor里面只有一个execute方法,这个方法用于执行线程任务,但是线程池在执行完所有的任务后不会停止,需要执行shutdown()方法,但是这个方法没有在Executor中声明,而是在ExecutorService类中实现的,所以使用ExecutorService方法接收线程池对象从而执行shutdown()方法关闭线程池

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Demo06 {
        public static void main(String[] args) {
            ExecutorService e = Executors.newFixedThreadPool(10);
    
            for (int  i = 0; i < 100; i ++) {
                e.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName());
                    }
                });
            }
            e.shutdown();
        }
    }
    

    上面是通过Executors静态工厂创建的线程池,Executors一共提供了五个核心方法,分别是:

    • Executors.newWorkStealingPool():这个方法在JDK1.8中引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争
    • Executors.newCachedThreadPool():maximumPoolSize最大可以到Integer.MAX_VLUE,线程数不够会自动增加,比较弹性。存在OOM异常,能够回收不工作的线程,keepAliveTime默认60,也就是60秒回收一个空闲线程
    • Executors.newScheduledThreadPool():线程数最大至Integer.MAX_VLUE,同样存在OOM风险,支持定时及周期性执行任务,优于Timer,不会回收不工作的线程
    • Executors.newSingleThreadExecutor():创建一个单线程的线程池,相当于单线程串行执行所有任务,保证按任务的提交顺序依次执行
    • Executors.newFixedThreadPool():输入的参数就是固定的线程数,既是核心线程数也是最大线程数,不存在空闲线程,所以keepAliveTime为0

    使用ThreadPoolExecutor创建

    一般不推荐使用Executors创建二十建议使用ThreadPollExecutor,因为Executors中的方法允许的请求队列长度是Integer.MAX_VALUE,可能会导致堆积大量的请求,从而导致OOM

    ThreadPoolExecutor的构造方法由7个参数:

    • corePoolSize:表示核心线程数,核心线程在任务执行完毕后不会销毁
    • maximumPoolSize:线程池能够容纳的最大线程数,必须大于1,如果执行的任务数超过了该值,多出的任务数会被放入队列中等待执行
    • keepAliveTime:表示线程池中允许线程空闲的时间,当空闲时间达到keepAliveTime时该线程就会被销毁,直到池中只有corePoolSize数量的线程为止。如果想让核心线程超时后也被回收,只需要将ThreadPoolExecutor的allowCoreThreadTimeOut变量设置为true
    • TimeUnit:表示keepAliveTime的时间单位,通常时TimeUnit.SECONDS
    • workQueue:缓存队列,当请求的任务数大于maxiumPoolSize后,超过的任务会进入BlockingQueue阻塞队列中
    • threadFactory:用于生产一组相同任务的线程,线程的命名就是通过给这个factory增加组名前缀来实现的
    • handler:执行拒绝策略的对象,当执行的任务数超过第五个参数workQueue的缓存限制时,及就可以使用该策略处理请求,比方说将请求转向某个提示页面表示当前执行任务数过多

    上面的队列、线程工厂、拒绝处理服务都必须有实例对象,但是再实际编程中都是通过Executors这个线程池静态工厂提供默认的实现,Executors用法上面已经说明,那么现在使用ThreadPoolExecutor来创建一个线程池

    第一步:实现队列,线程工厂,拒绝服务
    // 线程工厂
    class UserThreadFactory implements ThreadFactory {
        private final String namePrefix;
        private final AtomicInteger nextId = new AtomicInteger(1);
    
        UserThreadFactory(String name) {
            namePrefix = "该线程来自:" + name + "----这是该线程池中的第";
        }
        /**
         * 该方法用于创建线程池中的线程
         * @param r
         * @return
         */
        @Override
        public Thread newThread(Runnable r) {
            String name = namePrefix + nextId.getAndIncrement()  + "个线程";
            Thread thread = new Thread(null, r, name, 0);
            System.out.println(thread.getName());   // 打印线程名称
            return thread;
        }
    }
    
    // 拒绝服务实现
    class UserRejectHandler implements RejectedExecutionHandler {
    
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            System.out.println("任务太多啦,被拒绝了" + executor.toString());
        }
    }
    
    第二步:实现线程任务
    class Task implements Runnable {
    
        @Override
        public void run() {
            System.out.println("执行中。。。");
        }
    }
    
    创建线程池
    public class Demo07 {
        public static void main(String[] args) {
            // 设置了缓存队列的大小为2
            ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS,
                    new LinkedBlockingDeque<>(2), new UserThreadFactory("线程池工厂一"), new UserRejectHandler());
            for (int i = 0; i < 200; i++) {
                tpe.execute(new Task());
            }
        }
    }
    

    Lambda表达式实现

    使用parallelStream()实现

  • 相关阅读:
    SQLServer2008 行转列2
    SQLServer2008 行转列
    关于删除数据仓库的数据
    PowerDesign不让name和code联动
    提高SQL查询效率(SQL优化)(转载)
    SQL优化----百万数据查询优化(转载)
    运行程序向一个Java类中动态添加注解。
    开阔自己的视野,勇敢的接触新知识(转)
    [置顶] JAVA识别身份证号码,H5识别身份证号码,tesseract-ocr识别(一)(转)
    一个谷歌程序员的算法学习之路
  • 原文地址:https://www.cnblogs.com/Myarticles/p/12045999.html
Copyright © 2011-2022 走看看