从线程到并发编程

2019-01-10  本文已影响0人  felixu

何为线程

说起线程,还是得从进程说起。那么进程是什么呢?现代操作系统在运行一个程序时,会为其创建一个进程。比如你电脑上打开个QQ或者是启动一个Java程序,操作系统都会为其创建一个进程。而线程是操作系统的最小调度单元,一个进程中可以有多个线程。OS调度会让多个线程之间高速切换,让我们以为是多个线程在同时执行。

线程的创建与销毁

线程的创建

那么怎么去创建一个线程呢。在Java中我们有且仅有一种(至于为啥说只有一种,而网上说有三种,那是因为其他两种并非创建线程,而是创建了线程的执行单元,最后还是交由Thread去执行)方式来创建线程,那就是new Thread(),但是我们却可以有以下三种方式来使用:

  1. 继承Thread类,重写run方法。

    public class ThreadDemo1 extends Thread {
    
        @Override
        public void run() {
            System.out.println("extends thread run");
        }
    
        public static void main(String[] args) {
            ThreadDemo1 thread1 = new ThreadDemo1();
            ThreadDemo1 thread2 = new ThreadDemo1();
            thread1.start();
            thread2.start();
        }
    }
    
  2. 实现Runnable接口,重写run方法。

    public class ThreadDemo2 implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " implements runnable run");
        }
    
        public static void main(String[] args) {
            new Thread(new ThreadDemo2(), "thread1").start();
            new Thread(new ThreadDemo2(), "thread2").start();
        }
    }
    
  3. 实现Callable接口,重写call方法,实现带返回值的线程。

    public class ThreadDemo3 implements Callable<String> {
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService executorService = newFixedThreadPool(1);
            ThreadDemo3 thread = new ThreadDemo3();
            Future<String> future = executorService.submit(thread);
            System.out.println(future.get());
            executorService.shutdown();
        }
    
        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName() + " implements callable");
            return Thread.currentThread().getName();
        }
    }
    

终止线程

  1. interrupt中断标志

    前面看完了如何创建一个线程,那么又怎么去终止一个线程呢。以前的Thread类中有个stop方法可以用来终止线程,而现在已经被标记过期了,其实也不建议使用stop方法来终止线程,为什么呢!因为我想用过Linux系统的都知道kill -9吧,stop方法与其类似,stop方法会强制杀死线程,而不管线程中的任务是否执行完毕,那么我们如何更加优雅的去终止一个线程呢。

    这里Thread类为我们提供了一个interrupt方法。

    当我们需要终止一个线程,可以调用它的interrupt方法,相当于告诉这个线程你可以终止了,而不是暴力的杀死该线程,线程会自行中断,我们可以使用isInterrupted方法来判断线程是否已经终止了,我们可以用下面的代码来加以验证:

    public class InterruptDemo {
        private static int i;
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(() -> {
                while(!Thread.currentThread().isInterrupted()){
                    i++;
                }
                System.out.println("result: " + i);
            }, "interrupt-test");
            thread.start();
            TimeUnit.SECONDS.sleep(2);
            thread.interrupt();
        }
    }
    

    如果interrupt方法无法终止线程,那么这个线程将会是死循环,而无法结束。这里使用interrupt以一种更加安全中断线程。

  2. volatile共享变量作为中断标志

    这里先不介绍volatile的内存语义以及原理,它可以解决共享变量的内存可见性问题,使其他线程可以及时看到被volatile变量修饰的共享变量的变更,所以我们也可以使用volatile来达到中断线程的目的。

    public class VolatileDemo {
    
        private volatile static boolean flag = false;
      
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(() -> {
                long i = 0L;
                while (!flag) {
                    i++;
                }
                System.out.println(i);
            }, "volatile-demo");
            thread.start();
            System.out.println("volatile-demo is start");
            Thread.sleep(1000);
            flag = true;
        }
    }
    

    比如上面示例中的代码,我们可以控制在特定的地方,改变共享变量,来达到让线程退出。

线程复位

为什么要并发编程

单线程有时候也可以解决问题啊,那么我们为什么还要并发编程呢,很大程度上是因为更好的利用CPU资源,提升我们系统的性能。根据摩尔定律(当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18-24个月翻一倍以上。这一定律揭示了信息技术进步的速度。)推算,不久就会有超强的计算能力,然而,事情并未像预料的那样发展。2004年,Intel宣布4GHz芯片的计划推迟到2005年,然后在2004年秋季,Intel宣布彻底取消4GHz的计划。现在虽然有4GHz的芯片但频率极限已逼近,而且近10年停留在4GHz,也就是摩尔定律应该是失效了。既然单核CPU的计算能力短期无法提升了,而我们伟大的硬件科学家们可不会承认自己的理论是错的,那既然一个CPU搞不定了,那么就多个嘛,多核CPU在此时应运而生。单线程毕竟只可能跑在一个核心上,浪费了CPU的资源,从而催生了并发编程,并发编程是为了发挥出多核CPU的计算能力,提升性能。

顶级计算机科学家Donald Ervin Knuth如此评价这种情况:在我看来,这种现象(并发)或多或少是由于硬件设计者无计可施了导致的,他们将摩尔定律的责任推给了软件开发者。

并发编程总结起来说大致有以下优点:

并发编程有哪些问题

看起来好像多线程确实很好,那么我们就可以尽量多的去开线程了嘛。也并不是这样的,多线程的性能也受多方面因素所影响:

所以,如何合理的使用线程需要我们在实践中具体去分析。

结语

并发编程一直是个难点,也是在面试中不可避免被问到的知识点,后面会更多讨论其他方面的知识点,也需要更多的动手实践,才能体会其中的一些深层意义。

参考自《Java并发编程的艺术》

上一篇 下一篇

猜你喜欢

热点阅读