Java并发

作者: 贪婪的君子 | 来源:发表于2017-04-27 23:21 被阅读14次

    Java并发(concurrency)快速入门,想吗?

    1. 概述


    照例先啰嗦几句。
    编程是一门艺术,而并发式编程晚于程序语言出现的,并且可以说是计算机问世至今无法取代的艺术精品。
    在很久之前没有多核处理器的时候,程序员也好,科研人员也罢,无不在疯狂的攫取着单核处理器的潜在价值,直到单核处理器性能已经被榨取的差不多了,多核处理器的研究才开始再次被提上日程,这时候,另一个概念开始出现并崛起——并行。

    NOTE:
    并发是多个程序宏观上同时在推进,而并行则是讲究微观上多个程序同时推进,实现并行需要多个处理器(多核处理器)。
    与类型变量和类型参数相似,很多程序员将并发和并行混为一谈,并不多加区分。

    并发的内容其实有很多,仅一篇文章肯定是讲不了太多内容的,所以本文侧重于并发编程的入门,日后逐渐提高深度,主要大纲如下:

    1. 并发快速入门
    2. 线程间协同(Synchronized)工作
    3. 锁(Lock
    4. 原子变量(Atomic Variables
    5. 线程池(Thread Pools

    期间的细枝末节就暂不在此赘述了,直接开始本文的内容了。

    2. 创建一个多线程程序


    2.1 Thread和Runnable

    Thread和Runnable都能完成多线程,但是后者用的较为广泛,原因则先不说,先解读这俩玩意儿:

    1. Thread是一个类,通过继承该类可以直接实现线程的创建和管理new SubThread( ),也可以用以作为executor的传入参数。
    2. Runnable是一个接口,内部只有一个run( )方法,需要通过创建Thread类对象来实现new Thread(new Runnable( )) 线程的创建和管理。

    讲到这儿,原因就已经清楚了,Java是单继承模式,这意味着一旦继承了其他类(Thread类),便不能再继承Thread类(其他类),而Java却是可以实现多个接口的,多实现一个少接口或是少实现一个接口,问题不大。

    2.2 线程的创建及运行

    知道Thread类和Runnable接口还不够,通过一个例子了解具体的创建方法:
    示例程序

    public class SubThread extends Thread {
        private int i;
    
        public SubThread(int i) {
            this.i = i;
        }
    
        public void run() {
            System.out.println("Thread " + this.i + " has created!");
        }
    
        public static void main(String[] args) {
            new SubThread(1).start();
            new SubThread(2).start();
        }
    }
    

    在本段程序中,采取直接继承Thread类的方法进行线程的创建和管理,new SubThread() 为实例化本类对象,也即创建了一个线程,start() 则是启动该线程的方法。
    另外,在运行时会发现run方法打印的值顺序每次都可能不同,这是由于并发是多个程序宏观上的推进,但实际上由于共享处理器,处理器的调度算法决定如何分配CPU的时间片到各个线程。过程如图:

    交叉执行的一种情况

    示例程序

    public class NewRunnable implements Runnable {
        private int i;
    
        public NewRunnable(int i) {
            this.i = i;
        }
    
        public void run() {
            System.out.println("Thread " + this.i + " has created!");
        }
    
        public static void main(String[] args) {
            new Thread(new NewRunnable(1)).start();
            new Thread(new NewRunnable(2)).start();//需要通过创建Thread类对象,并将实现了Runnable接口的类对象作为参数传递给Thread(Runnable target)构造器
        }
    }
    

    通过这段程序就可以看出实现Runnable接口的类是如何创建新的线程,以及创建方式与继承Thread类的创建方式有什么不同了。
    还有一种使用匿名内部类的方式

    new Thread(new Runnable() {
                public void run() {
                    System.out.println("you");
                }
            }).start();
    

    2.3 简单同步机制

    感觉话是不是多了点?不过似乎是没什么废话吧......
    在上文的示例中可以看到有一个实例域 i,假如对某个实例对象的实例域进行运算,如自增,自减运算,会不会出现什么问题呢?比如说自增,自减在两个不同的方法中,而两个不同的线程又同时调用出现调用这两个方法的情况,最后i的值是多少,是不是所预期的结果?
    答案很显然,不一定。前文表述过,并发程序是交叉执行的,也就是说如果有一个打印操作需要在自增之后自减之前,但是由于线程运行的不受控,所以可能会出现在自增和自减操作执行结束后才开始进行打印操作,结果非预期,但是synchronized方法却可以简单有效的规避这种问题。

    public class SynchronizedCounter {
        private int c = 0;
    
        public synchronized void increment() {
            c++;
        }
    
        public synchronized void decrement() {
            c--;
        }
    
        public synchronized int value() {
            return c;
        }
    }
    

    同一个实例对象是禁止同时调用多个synchronized修饰的方法的,如此便避免了之前所提到的问题。

    3. 总结


    没有这个总结,总感觉有些奇怪。
    并发绝不仅仅是Java语言独有的,本文仅针对Java语言进行一个简单的介绍,浏览完本文,创建一个简单的多线程的程序,还不是手到擒来的事情嘛,在这一点上,笔者还是能打包票的。

    前文所列的主要纲要日后会继续深入完善并补上博文链接,以供日后的阅读。

    相关文章

      网友评论

        本文标题:Java并发

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