并发编程基础

摘要:

         并发编程,是程序员必须掌握的一门课程。并发学习得怎么样,直接影响到程序的性能和健壮性。本文针对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;

      }

   }

 

posted on 2017-03-28 13:16  罗武  阅读(179)  评论(0)    收藏  举报