博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java高并发编程(三)
阅读量:5291 次
发布时间:2019-06-14

本文共 7563 字,大约阅读时间需要 25 分钟。

 目录:

  1.线程安全单例模式的几种实现方式

  2.同步容器

  3.并发容器

  一、线程安全单例模式的几种实现方式

  1.饿汉式(不使用同步锁,典型的用空间换时间)

public class Singleton1 {     private static Singleton1 mySingleton = new Singleton1();    private Singleton1(){         System.out.println("single");    }         public static Singleton1 getSingle(){        return mySingleton;    }    public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i
{ Singleton1.getSingle(); }); } Arrays.asList(ths).forEach(o->o.start()); }}

运行结果:

  2.懒汉式(使用同步锁,延时加载,典型的时间换空间)

public class Singleton2 {          private static Singleton2 mySingleton;          private Singleton2 (){           System.out.println("single");     }             public static synchronized Singleton2 getSingle(){    //对获取实例的方法进行同步        if (mySingleton == null)                 mySingleton = new Singleton2();         return mySingleton;    }        public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i
{ Singleton2.getSingle(); }); } Arrays.asList(ths).forEach(o->o.start()); }}

运行结果:

  3.双重同步锁(缩小粒度,双重检查

public class Singleton3 {           private volatile static Singleton3 mySingleton;           private Singleton3 (){        System.out.println("single");    }            public static Singleton3 getSingle(){    //对获取实例的方法进行同步       if (mySingleton == null){           synchronized(Singleton3.class){               if (mySingleton == null)                   mySingleton = new Singleton3();            }       }       return mySingleton;    }        public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i
{ Singleton3.getSingle(); }); } Arrays.asList(ths).forEach(o->o.start()); }}

运行结果:

  为mySingleton加上volatile关键字,以确保能先行发生关系(happens-before relationship),使所有的写操作都发生于读操作之前,避免出现在另一个线程中看到一个初始化一半的mySingleton的情况,同时双重同步锁的效率也很高

  4.使用内部类的单例模式(既不用加锁,也能实现懒加载)

public class Singleton4 {        private Singleton4() {        System.out.println("single");    }        private static class Inner {        private static Singleton4 mySingleton = new Singleton4();    }        public static Singleton4 getSingle() {        return Inner.mySingleton;    }        public static void main(String[] args) {        Thread[] ths = new Thread[200];        for(int i=0; i
{ Singleton4.getSingle(); }); } Arrays.asList(ths).forEach(o->o.start()); } }

运行结果:

  二、同步容器

  1.vector、stack、hashtable等,保证了方法的原子性

  2.小例子程序演示同步容器

/** * 有N张火车票,每张票都有一个编号 * 同时有10个窗口对外售票 * 请写一个模拟程序 *  * 分析下面的程序可能会产生哪些问题? * 重复销售?超量销售? *  */package yxxy.c_024;import java.util.ArrayList;import java.util.List;public class TicketSeller1 {    static List
tickets = new ArrayList<>(); static { for(int i=0; i<10000; i++) tickets.add("票编号:" + i); } public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(()->{ while(tickets.size() > 0) { System.out.println("销售了--" + tickets.remove(0)); } }).start(); } }}

运行结果:

出现了越界情况,说明发生了超量销售与重复销售的情况

下面使用同步容器,以vector为例再做一遍售票操作

/** * 有N张火车票,每张票都有一个编号 * 同时有10个窗口对外售票 * 请写一个模拟程序 *  * 分析下面的程序可能会产生哪些问题? *   * 使用Vector或者Collections.synchronizedXXX * 分析一下,这样能解决问题吗? *  */package yxxy.c_024;import java.util.Vector;import java.util.concurrent.TimeUnit;public class TicketSeller2 {    static Vector
tickets = new Vector<>(); static { for(int i=0; i<1000; i++) tickets.add("票 编号:" + i); } public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(()->{ while(tickets.size() > 0) { /* try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }*/ System.out.println("销售了--" + tickets.remove(0)); } }).start(); } }}

运行结果:

...

从运行结果来看,同步容器似乎已经达到了我们想要的目的,但其中还有一些问题

当我们使用了同步容器时,虽然容器的方法是具有原子性的,但当判断与操作分离时,中间的部分还是可能会被其他线程所打断,下面我们模拟一下判断与remove之间有其他的逻辑

/** * 有N张火车票,每张票都有一个编号 * 同时有10个窗口对外售票 * 请写一个模拟程序 *  * 分析下面的程序可能会产生哪些问题? *   * 使用Vector或者Collections.synchronizedXXX * 分析一下,这样能解决问题吗? *  */package yxxy.c_024;import java.util.Vector;import java.util.concurrent.TimeUnit;public class TicketSeller2 {    static Vector
tickets = new Vector<>(); static { for(int i=0; i<1000; i++) tickets.add("票 编号:" + i); } public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(()->{ while(tickets.size() > 0) { try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("销售了--" + tickets.remove(0)); } }).start(); } }}

运行结果:

我们看到程序出现了错误,所以虽然同步容器保证了方法的原子性,但可能会发生判断与操作分离,整体操作不具备原子性

 

  三、并发容器

  1.并发容器支持多线程

  ConcurrentSkipListMap跳表结构,他是排好序的容器执行插入操作较快

  ConcurrentHashMap将容器分成了16段,每次执行插入操作的时候只执行其中的一段,而HashTable为锁定一整段

  ConcurrentLinkedQueue无界队列,内存耗不完的情况下可以一直加(offer,add),拿(poll拿出来删掉,peek拿出来不删)

  BolckingQueue阻塞式队列,加(put如果满了就会等待),拿(take如果空了就会等待)

    几个特殊的阻塞式队列:

    1)TransferQueue转移队列,相较于其他队列,多了一个transfer方法生产者将任务直接发给消费者,不进入结束队列,在transfer找不到消费者情形下阻塞,实时处理用的较多,队列还有一定容量

    2)DelayQueue计时任务队列

    3)synchronusQueue同步队列,特殊的transferQueue,没有容量的队列,不能调用add方法,只能调用put方法阻塞等待消费者消费,生产者生产的任务直接给消费者消费

 还是之前的售票程序,现在改用并发容器以ConcurrentLinkedQueue来重新实验一遍

/** * 有N张火车票,每张票都有一个编号 * 同时有10个窗口对外售票 * 请写一个模拟程序 *  * 使用ConcurrentQueue提高并发性 */package yxxy.c_024;import java.util.Queue;import java.util.concurrent.ConcurrentLinkedQueue;import java.util.concurrent.TimeUnit;public class TicketSeller4 {    static Queue
tickets = new ConcurrentLinkedQueue<>(); static { for(int i=0; i<1000; i++) tickets.add("票 编号:" + i); } public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(()->{ while(true) { String s = tickets.poll(); if(s == null) break; else System.out.println("销售了--" + s); } }).start(); } }}

运行结果:

当我们改用并发容器ConcurrentLinkedQueue时,我们调用poll方法售票直至票数为0时,字符串s会变为null,所以在接下来的if判断后会跳出

当我们在此例中使用了并发容器时,就具有高的效率同时也不会出现问题

  四、其他常用同步容器

  1.Collocations.synchronizedXXX()将未加锁的容器加锁,在并发度不高的情况下可以使用此方法,在并发度较高且需排序的情况下使用concurrentSkipListMap

  2.CopyOnWriteList写时复制链接表,在读取时不加锁,因此读取的时候效率高,但写的效率特别低,应用场景:在需要大量读取少量写入时使用

  五、总结

  1.对于map/set的使用

    无线程安全要求:hashmap、treemap、linkedhashmap

    并发度低:   hashtable、Collections.synchronizedXXX()

    并发度高:   concurrentHashMap、concurrentSkipListMap

  2.list

    无线程安全要求:ArrayList、LinkedList

    并发度低:   Collections.synchronizedXXX()

    并发度高:   Queue

              concurrentLinkedQueue

              BlockingQueue

                LinkedBQ

                ArrayBQ

                TransferQueue

                synchronusQueue

              DelayQueue

转载于:https://www.cnblogs.com/ghoster/p/7484831.html

你可能感兴趣的文章
转-四层和七层负载均衡的区别
查看>>
docker私有库搭建过程(Registry)
查看>>
kubernetes重置(重装)node的flannel网络
查看>>
etcdctl环境变量设置
查看>>
kubectl patch
查看>>
高中地理思维导图(浙江省学考专用)
查看>>
MySQL---Mybatis 批处理(增,改,删)
查看>>
Date相关处理
查看>>
三种List集合的遍历方式
查看>>
对List集合中的对象进行按某个属性排序
查看>>
MySql查询当天、本周、本月、本季度、本年的数据
查看>>
dashboard安装
查看>>
heapset水平自动扩容
查看>>
mysql explain用法和结果的含义
查看>>
处理Mybatis返回的结果集为Map类型
查看>>
无法恢复master数据库。SQL Server 无法运行
查看>>
无法打开数据库‘Data’.恢复操作已将数据库标记为SUSPECT。
查看>>
活久现
查看>>
asp.net mvc中配置全局异常过滤器
查看>>
B/S神思SS628(100)身份证阅读器开发
查看>>