zoukankan      html  css  js  c++  java
  • 并发编程基础

    并发编程基础

    摘要:

             并发编程,是程序员必须掌握的一门课程。并发学习得怎么样,直接影响到程序的性能和健壮性。本文针对java中常见的并发问题,及并发编程中的一些常用概念,进行了初步的扫盲,希望能通过一些知识点的理解,加深程序员对并发的理解。让我们一起进入并发的世界 。

    1       何谓并发

    并发,是指多线程同时访问资源,而产生资源争抢的问题。

    在目前的应用环境中,所有的程序,都是运行在并发的基础上的。初学者可能没有意识到,并发的东西,已经被项目里的框架给隐藏处理了。

    在JDK中,通过synchronized、valitile、wait notify等等技术,来处理并发问题。

    1.1     Synchronized

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

    作为一个重量级的同步锁,能修饰一下对象:

    1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

    2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

    3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

    4. 修饰一个类synchronized(SyncThread.class),其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。实现了全局锁的效果,相当于锁住了代码段。

    使用注意点:

    1. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

    2. 每个对象有且仅有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

    3 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

    4、set方法加锁,对get方法没有加锁,就会出现脏读。

    5、synchronized关键字不能继承。

    1.2     Valitile

    理解valitile关键字,首先要了解jvm的内存模型。

    volatile的就是强制线程到主内存中读取数据,而不去线程工作内存区域读取数据,从而实现的多个线程之间的变量的可见性。

    Jvm的内存模型参见:http://www.cnblogs.com/ironyoda/p/6289847.html

    1.3     线程通信

    线程通信主要是使用wait和notify方法进行。

     

    使用注意:

    1、waitnotify都必须配合synchronized方法使用

    2、wait方法释放锁,notify方法不释放锁

    import java.util.LinkedList;

    import java.util.concurrent.atomic.AtomicInteger;

    /**

     * 模拟Queue

     * @author alienware

     */

    public class MyQueue {

     

    private final LinkedList<Object> list = new LinkedList<Object>();

    private final AtomicInteger count = new AtomicInteger(0);

    private final int maxSize;

    private final int minSize = 0;

    private final Object lock = new Object();

    public MyQueue (int maxSize){

               this.maxSize = maxSize;

    }

    public void put (Object obj) {

               synchronized(lock){

                        while(count.get() == maxSize){

                                 try {

                                           lock.wait();

                                 } catch (InterruptedException e) {

                                           e.printStackTrace();

                                 }

                        }

                        list.add(obj);

                        count.getAndIncrement();

                        System.out.println(" 元素 " + obj + " 被添加 ");

                        lock.notify();

               }

    }

     

    public Object take(){

               Object temp = null;

               synchronized (lock) {

                        while(count.get() == minSize){

                                 try {

                                           lock.wait();

                                 } catch (InterruptedException e) {

                                           e.printStackTrace();

                                 }

                        }

                        count.getAndDecrement();

                        temp = list.removeFirst();

                        System.out.println(" 元素 " + temp + " 被消费 ");

                        lock.notify();

               }

               return temp;

    }

     

    public int size(){

               return count.get();

    }

     

    public static void main(String[] args) throws Exception {

              

               final MyQueue m = new MyQueue(5);

               m.put("a");

               m.put("b");

               m.put("c");

             m.put("d");

               m.put("e");

               System.out.println("当前元素个数:" + m.size());

               Thread t1 = new Thread(new Runnable() {

                        @Override

                        public void run() {

                                 m.put("h");

                                 m.put("i");

                        }

               }, "t1");

              

               Thread t2 = new Thread(new Runnable() {

                        @Override

                        public void run() {

                                 try {

                                           Thread.sleep(1000);

                                           Object t1 = m.take();

                                           //System.out.println("被取走的元素为:" + t1);

                                           Thread.sleep(1000);

                                           Object t2 = m.take();

                                           //System.out.println("被取走的元素为:" + t2);

                                 } catch (InterruptedException e) {

                                           e.printStackTrace();

                                 }

                        }

               }, "t2");

               t1.start();

               Thread.sleep(1000);

               t2.start();

    }

    }

    2       如何并发

    目前,并发主要使用Concurrent包下的容器、并发类设计模式、并发类框架来实现。

    2.1     同步类容器

    同步类容器都是线程安全的。

    古老的如:Vector和HashTable都是同步类容器,一般都是JDK的Collections.synchronized等方法创建实现的。底层都是使用synchronized的同步锁实现,无法满足目前大并发量的应用。

    2.2     并发类容器

    并发类容器大大改善了同步类容器的性能。目前,并发类容器使用ConcurrentHashMap替代了HashTable。用CopyonWriteArrayList替代了Vector。

    CopyonWriteArrayList适用于大量读,少量写的线程安全的链表。

    还有大量使用的ConcurrentLinkedQueue和LinkedBlockingQueue.

    2.2.1  ConcurrentHashMap:

    内部分成16个Segment,然后每个Segment有一个锁,相当于将锁的粒度减小,从而大大提高并发吞吐量。

     

    ConcurrentLinkedQueue:

    一个使用于高并发的场景下的队列,通过无锁的方式,实现了高并发状态下的搞性能,通常concurrentLinkedQueue性能浩宇BlockingQueue,他是一个基于链接节点的无界线程队列。

     

    2.3     阻塞型队列:

    2.3.1  ArrayBlockingQueue:

    基于数据实现的阻塞型队列,没有实现读写分离,长度固定的有界队列,很多场景适合。

    2.3.2    LinkedBlockingQueue:

    基于链表实现的阻塞性队列,无界限,采用读写分离,读和写使用不同的锁,从而时间了生产者和消费者操作完全并行。

    2.3.3    PriorityBlockingQueue:

    有优先级的阻塞队列。

    2.3.4  SynchronousQueue:

    一个没有缓冲的队列,生产者产生的数据直接被消费者获取并消费。

    3       并行设计模式

    3.1     Future模式

    Future模式的核心在于:去除了主函数的等待时间,并使得原本需要等待的时间段可以用于处理其他业务逻辑。Future模式只是生产者-消费者模型的扩展。经典“生产者-消费者”模型中消息的生产者不关心消费者何时处理完该条消息,也不关心处理结果。Future模式则可以让消息的生产者等待直到消息处理结束,如果需要的话还可以取得处理结果。

    原理:Future模式在请求发生时,会先产生一个Future对象给发出请求的客户。它的作用类似于代理(Proxy)对象,而同时所代理的真正目标对象的生成是由一个新的线程持续进行。真正的目标对象生成之后,将之设置到Future之中,而当客户端真正需要目标对象时,目标对象也已经准备好,可以让客户提取使用。

    具体细节,请查阅相关资料。

    3.2     生产者、消费者模式

    最常用的模式,这里不做介绍

    3.3     Master-Worker模式

    Master-Worker模式是常用的并行模式之一,它的核心思想是:系统由两类进程协同工作,即Master进程和Worker进程,Master负责接收和分配任务,Wroker负责处理子任务。当各个Worker进程将子任务处理完成后,将结果返回给Master进程,由Master进程进行汇总,从而得到最终的结果。

    4       单例

    单例有懒汉模式和饥饿模式、静态内部类、双层校验模式。

    4.1 懒汉模式:

       public class Singleton {

          private static Singleton instance;

          private Singleton() {

          }

          public static synchronized Singleton getInstance() {

             if (instance == null) {

                instance = new Singleton();

             }

             return instance;

          }

       }

     

    4.2     饥饿模式:

    public class Singleton {

          private Singleton instance = null;

          static {

             instance = new Singleton();

          }

          private Singleton() {

          }

          public static Singleton getInstance() {

             return this.instance;

          }

       }

     

    推荐使用:

    4.3     静态内部类:

    public class Singleton {

          private static class SingletonHolder {

             private static final Singleton INSTANCE = new Singleton();

          }

          private Singleton() {

          }

          public static final Singleton getInstance() {

             return SingletonHolder.INSTANCE;

          }

       }

    4.4     双层校验模式:

    public class Singleton {

          private volatile static Singleton singleton;

     

          private Singleton() {

          }

          public static Singleton getSingleton() {

             if (singleton == null) {

                synchronized (Singleton.class) {

                    if (singleton == null) {

                       singleton = new Singleton();

                    }

                }

             }

             return singleton;

          }

       }

  • 相关阅读:
    U盘安装Ubuntu 14.04 LTS
    VS2013配置OPENCV2.4.9(OPENCV3.X)
    make、makefile、cmake、qmake对比
    Google C++ Style
    Ubuntu16.04搜狗输入法无法输入中文
    Ubuntu16.04安装使用wineqq
    Ubuntu卸载软件
    [机器学习入门篇]-梯度下降法
    [机器学习入门篇]-正则化
    2014年度最受好评的十佳工具
  • 原文地址:https://www.cnblogs.com/ironyoda/p/6633962.html
Copyright © 2011-2022 走看看