找工作的时候是否经常看到要求有高并发,分布式系统的开发设计经验,或者高并发,分布式系统的开发设计经验者优先等字样,这时候情不自禁的搜索一下 什么是并发,多少算高并发,再思索一下自己的经历哪些是符合这个要求的?那么什么是并发,开发中的并发是怎么处理的,简单了解一下吧。
在介绍并发之前我们先了解一下串行和并行:
热闹的景点,买票人很多,这时只有一个窗口售票,大家排队依次买票就可以理解为串行。
排队人太多了,旁边又加开了几个窗口,多人在不同的窗口同时买票可以理解为并行。
如果只能开一个窗口,这时好多着急的人围上来,有问价格的,有掏钱的,又有取票的,在这个过程中售票员在同时应对多个买票人,可以理解为并发。
我们经常在计算机上一边听歌一边写文档(或者处理其他的事情),这就是一种并发行为,表面看两个程序是同时进行,为什么不是并行呢?计算机只有一个 CPU所以只能支持一个线程运行。有人拍砖说:我家里计算机是多核CPU可以同时支持多个线程运行,确实是这样,为此我也特地去百度了一下有如下几点认 知:
1、虽然是多核CPU但是,系统总线,内存是共用的,在加载内存数据时仍然需要串行访问。
2、目前的程序设计语言仍然是过程型开发,没有和好的方法能自动的切割任务使并行计算。
3、操作系统在线程调度时随着内核的增加复杂性递增,目前最多支持8核
所以基于以上认知,我们在讨论并发和同步这个问题时仍然按照CPU单核来讨论。
那么计算机是如何做到一边播放歌曲一边支持文档编辑呢?操作系统会把CPU的执行时间划分微妙级别的时间片段,每一个时间片内去调度一个线程执行,多 个线程不断的切换执行,因此在人类可感知的时间段(秒级)内线程是同时执行的,所以多个线程在某个时间段内的同时执行就是并发。
串行、并行和并发如下图所示:
互联网应用基本上都是支持多用户多请求同时访问服务器端的,所以互联网应用都是支持并发的,那么高并发的主要困难是什么呢?操作系统会给每个线程分配 独立的内存空间和时间片,所以线程间是隔离的。但是如果线程访问线程外的内存空间,文件系统,输入输出设备,数据库或者其他存储设备时就会发生资源竞争, 共享资源的访问必须串行,保证串行访问资源的机制就是同步,JAVA中经常使用的同步机制有synchronized关键字,java.util.concurrent.locks.Lock系列类。
同步的场景有以下几种:
1、线程获取同步锁,获取失败则阻塞等待
适用场景:
a、同步获取序列号生成器,当有其他线程获取序列号时,其他线程等待
java代码实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class SynchronizedProcessor implements Processor { /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format( "%s开始处理,当前时间是%d" , name, System.currentTimeMillis())); synchronized ( this ) { System.out.println(String.format( "%s获得锁%s" , name, this .toString())); try { System.out.println(String.format( "%s开始sleep" , name)); Thread.sleep( 1000 ); System.out.println(String.format( "%s结束sleep" , name)); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(String.format( "%s释放锁%s" , name, this .toString())); System.out.println(String.format( "%s结束处理,当前时间是%d" , name, System.currentTimeMillis())); } } |
2、线程获取同步锁,获取失败结束
适用场景:
a、定时任务,前一个处理线程未完成时,新线程不能获取锁则直接结束
java代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class LockFailCloseProcessor implements Processor { private static Lock lock = new ReentrantLock(); /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format( "%s开始处理,当前时间是%d" , name, System.currentTimeMillis())); if (lock.tryLock()) { System.out.println(String.format( "%s获得锁%s" , name, this .toString())); try { System.out.println(String.format( "%s开始sleep" , name)); Thread.sleep( 1000 ); System.out.println(String.format( "%s结束sleep" , name)); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock(); System.out.println(String.format( "%s释放锁%s" , name, this .toString())); } else { System.out.println(String.format( "%s没有获得锁直接退出" , name)); } System.out.println(String.format( "%s结束处理,当前时间是%d" , name, System.currentTimeMillis())); } } |
3、线程获取同步锁后,因为其他资源不满足暂时释放同步锁,等待唤醒
适用场景:
a、即使通讯中,发送者获取同步锁发现队列写满时,释放锁等待接收者读取数据
java代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | public class SynchronizedWaitWriteProcessor implements Processor { /** * 是否可读标记,false:不可读,可写 true:可读,不可写 */ public static int maxSize = 5 ; public static List<String> content = new ArrayList<String>(); /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format( "%s开始处理,当前时间是%d" , name, System.currentTimeMillis())); synchronized (content) { System.out.println(String.format( "%s获得锁%s" , name, this .toString())); try { if (content.size() == maxSize) { System.out.println( String.format( "%s临时释放锁%s" , name, this .toString())); content.wait(); } System.out.println( String.format( "%s开始写入信息" , name)); Random random = new Random(); for ( int i = 0 ; i < maxSize; i++) { content.add( String.format( "写入信息%d" , random.nextInt( 1000 ))); } System.out.println( String.format( "%s结束写入信息" , name)); } catch (InterruptedException e) { e.printStackTrace(); } content.notify(); } System.out.println(String.format( "%s释放锁%s" , name, this .toString())); System.out.println(String.format( "%s结束处理,当前时间是%d" , name, System.currentTimeMillis())); } } |
4、线程获取同步锁后,因为其他资源不满足结束线程
适用场景:
a、即使通讯中,接收者获取同步锁发现队列无数据时,释放锁结束线程
java代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class SynchronizedWaitReadProcessor implements Processor { /* (non-Javadoc) * @see com.sunhaojie.test.thread.Processor#process(java.lang.String) */ public void process(String name) { System.out.println(String.format( "%s开始处理,当前时间是%d" , name, System.currentTimeMillis())); synchronized (SynchronizedWaitWriteProcessor.content) { System.out.println(String.format( "%s获得锁%s" , name, this .toString())); if (SynchronizedWaitWriteProcessor.content.size() != 0 ) { System.out.println( String.format( "%s开始读出信息" , name)); for ( int i = 0 ; i < SynchronizedWaitWriteProcessor.content.size(); i++) { System.out.println( "读出信息:" + SynchronizedWaitWriteProcessor.content.get(i)); } System.out.println( String.format( "%s结束读出信息" , name)); } SynchronizedWaitWriteProcessor.content.notify(); } System.out.println(String.format( "%s释放锁%s" , name, this .toString())); System.out.println(String.format( "%s结束处理,当前时间是%d" , name, System.currentTimeMillis())); } } |
最后送上运行以上程序的main方法和Processor 接口类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | public interface Processor { public void process(String name); } public class ThreadTest { public static void main(String[] args) throws InterruptedException { //测试SynchronizedProcessor // Processor processor = new SynchronizedProcessor(); // for (int i = 0; i < 10; i++) { // ProcessorThread threadProcessor = new ProcessorThread( "name" + i, processor); // threadProcessor.start(); // } //测试LockProcessor // Processor processor = new LockProcessor(); // for (int i = 0; i < 10; i++) { // ProcessorThread threadProcessor = new ProcessorThread( "name" + i, processor); // threadProcessor.start(); // } // Processor processor = new LockFailCloseProcessor(); // for (int i = 0; i < 10; i++) { // ProcessorThread threadProcessor = new ProcessorThread( "name" + i, // processor); // threadProcessor.start(); // } Processor readProcessor = new SynchronizedWaitReadProcessor(); ProcessorThread readThreadProcessor = new ProcessorThread( "read" , readProcessor); readThreadProcessor.start(); Processor writeProcessor = new SynchronizedWaitWriteProcessor(); ProcessorThread writeThreadProcessor = new ProcessorThread( "write" , writeProcessor); writeThreadProcessor.start(); Thread.sleep( 100 ); ProcessorThread read2ThreadProcessor = new ProcessorThread( "read2" , readProcessor); read2ThreadProcessor.start(); } } |
多线程可以大大提高性能,但是多线程的同步又增加了应用的复杂性,是否能平衡多线程的性能和复杂性是是否有高并发经验的要求。