├── .classpath ├── .gitignore ├── .project ├── LICENSE ├── README.md ├── lib └── log4j-1.2.17.jar └── src ├── io └── github │ └── viscent │ └── mtpattern │ └── tpt │ ├── AbstractTerminatableThread.java │ ├── Terminatable.java │ ├── TerminationToken.java │ └── example │ ├── AlarmMgr.java │ ├── AlarmType.java │ ├── ProducerConsumerStop.java │ └── TestAlarmMgrShutdown.java ├── log4j.properties └── vh ├── activeobject ├── AsyncRequestPersistence.java ├── CustomScheduler.java ├── DiskbasedRequestPersistence.java ├── MMSDeliverRequest.java ├── RequestPersistence.java ├── Test.java └── lib │ ├── ActiveObjectProxy.java │ ├── AsyncPerformanceTest.java │ ├── PerformanceTest.java │ ├── SampleActiveObject.java │ ├── SampleActiveObjectImpl.java │ └── Test.java └── immutableobject ├── MMSCInfo.java ├── MMSCRouter.java ├── OMCAgent.java └── example ├── ImmutableLocation.java ├── Location.java └── VehicleTracker.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /classes 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | JavaConcurrencyPattern 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Viscent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Java Concurrency Pattern 2 | ====================== 3 | 4 | 《Java多线程编程模式实战指南》系列文章配套源码。这些文章已扩充为一本书:《Java多线程编程实战指南(设计模式篇)》,由电子工业出版社出版,当当、亚马逊、京东、互动出版网、淘宝等各大书店有售。 5 | 6 | 【样章】 7 | 8 | Active Object模式: 9 | 10 | http://www.infoq.com/cn/articles/Java-multithreaded-programming-mode-active-object-part1 11 | 12 | http://www.infoq.com/cn/articles/Java-multithreaded-programming-mode-active-object-part2 13 | 14 | Immutable Object模式: 15 | 16 | http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-immutable-object 17 | 18 | Two-phase Termination模式: 19 | 20 | http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-two-phase-termination 21 | 22 | http://viscent.iteye.com/category/328291 23 | 24 | 【前言】 25 | 26 | 随着现代CPU的生产工艺从提升CPU主频频率转向多核化,即在一块芯片上集成多个CPU内核(Core),以往那种靠CPU自身处理能力的提升所带来的软件计算性能提升的那种“免费午餐”不复存在。在此背景下,多线程编程在充分利用计算资源、提高软件服务质量方面扮演了越来越重要的角色。然而,多线程编程并非一个简单地使用多个线程进行编程的数量问题,其又有自身的问题。好比俗话说“一个和尚打水喝,两个和尚挑水喝,三个和尚没水喝”,简单地使用多个线程进行编程可能导致更加糟糕的计算效率。 27 | 设计模式相当于软件开发领域的“三十六计”,它为特定背景下反复出现的问题提供了一般性解决方案。多线程相关的设计模式为我们恰当地使用多线程进行编程并达到提升软件服务质量这一目的提供了指引和参考。当然,设计模式不是菜谱,即便是菜谱我们也不能指望照着菜谱做就能做出一道美味可口的菜肴,但我们又不能因此而否认菜谱存在的价值。 28 | 可惜的是,国外与多线程编程相关的设计模式书籍多数采用C++作为描述语言,且书中所举的例子又多与应用开发人员的实际工作经历相去甚远。本书作为国内第一本多线程编程相关设计模式的原创书籍,希望能够为Java开发者普及多线程相关的设计模式开一个头。 29 | 本书采用Java(JDK 1.6) 语言和UML(Unified Modeling Language)为描述语言,并结合作者多年工作经历的相关实战案例,介绍了多线程环境下常用设计模式的来龙去脉:各个设计模式是什么样的及其典型的实际应用场景、实际应用时需要注意的相关事项以及各个模式的可复用代码实现。 30 | 本书第1章对多线程编程基础进行了回顾,虽然该章讲的是基础但重点仍然是强调“实战”。所谓“温故而知新”,有一定多线程编程基础、经验的读者也不妨快速阅读一下本章,说不定有新的收获。 31 | 本书第3章到第14章逐一详细讲解了多线程编程相关的12个常用设计模式。针对每个设计模式,相应章节会从以下几个方面进行详细讲解。 32 | 模式简介。这部分简要介绍了相应设计模式的由来及其核心思想,以便读者能够快速地对其有个初步认识。 33 | 模式的架构。这部分会从静态(类及类与类之间的结构关系)和动态(类与类之间的交互)两个角度对相应设计模式进行详细讲解。模式架构分别使用UML类图(Class Diagram)和序列图(Sequence Diagram)对模式的静态和动态两个方面进行描述。 34 | 实战案例解析。在相应设计模式架构的基础上,本部分会给出相关的实战案例并对其进行解析。不同于教科书式的范例,实战案例强调的是“实战”这一背景。因此实战案例解析中,我们会先提出实际案例中我们面临的实际问题,并在此基础上结合相应设计模式讲解相应设计模式是如何解决这些问题的。实战案例解析中我们会给出相关的Java代码,并讲解这些代码与相应设计模式的架构间的对应关系,以便读者进一步理解相应设计模式。为了便于读者进行实验,本书给出的实战案例代码都力求做到可运行。实战案例解析有助于读者进一步理解相应的设计模式,并体验相应设计模式的应用场景。建议读者在阅读这部分时先关注重点,即实战案例中我们要解决哪些问题,相应设计模式又是如何解决这些问题的,实战案例的代码与相应设计模式的架构间的对应关系。而代码中其与设计模式非强相关的细节则可以稍后关注。 35 | 模式的评价与实现考量。这部分会对相应设计模式在实现和应用过程中需要注意的一些事项、问题进行讲解,并讨论应用相应设计模式所带来的好处及缺点。该节也会讨论相应设计模式的典型应用场景。 36 | 可复用实现代码。这部分给出相应设计模式的可复用实现代码。编写设计模式的可复用代码有助于读者进一步理解相应设计模式及其在实现和应用过程中需要注意的相关事项和问题,也便于读者在实际工作中应用相应设计模式。 37 | Java标准库实例。考虑到Java标准库的API设计过程中已经应用了许多设计模式,本书尽可能地给出相应设计模式在Java API中的应用情况。 38 | 相关模式。设计模式不是孤立存在的,一个具体的设计模式往往和其它设计模式之间存在某些联系。这部分会描述相应设计模式与其它设计模式之间存在的关系。这当中可能涉及GOF的设计模式,这类设计模式并不在本书的讨论范围之内。有需要的读者,请自行参考相关书籍。 39 | 本书的源码可以从http://github.com/Viscent/javamtp下载或博文视点官网http://www.broadview.com.cn相关图书页面。 40 | 41 | 【本书目录】见:http://www.phei.com.cn/module/goods/wssd_content.jsp?bookid=43821 42 | -------------------------------------------------------------------------------- /lib/log4j-1.2.17.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viscent/JavaConcurrencyPattern/c95b20a5dd9748c15ed267ef53ab328e298048f4/lib/log4j-1.2.17.jar -------------------------------------------------------------------------------- /src/io/github/viscent/mtpattern/tpt/AbstractTerminatableThread.java: -------------------------------------------------------------------------------- 1 | package io.github.viscent.mtpattern.tpt; 2 | 3 | public abstract class AbstractTerminatableThread extends Thread 4 | implements Terminatable { 5 | public final TerminationToken terminationToken; 6 | 7 | public AbstractTerminatableThread() { 8 | super(); 9 | this.terminationToken = new TerminationToken(); 10 | } 11 | 12 | /** 13 | * 14 | * @param terminationToken 线程间共享的线程终止标志实例 15 | */ 16 | public AbstractTerminatableThread(TerminationToken terminationToken) { 17 | super(); 18 | this.terminationToken = terminationToken; 19 | } 20 | 21 | protected abstract void doRun() throws Exception; 22 | 23 | protected void doCleanup(Exception cause) { 24 | //do nothing 25 | } 26 | 27 | protected void doTerminiate() { 28 | //do nothing 29 | } 30 | 31 | @Override 32 | public void run() { 33 | Exception ex = null; 34 | try { 35 | while (true) { 36 | /* 37 | * 在执行线程的处理逻辑前先判断线程停止的标志。 38 | */ 39 | if (terminationToken.isToShutdown() 40 | && terminationToken.reservations.get()<=0) { 41 | break; 42 | } 43 | doRun(); 44 | } 45 | 46 | } catch (Exception e) { 47 | // Allow the thread to terminate in response of an interrupt invocation 48 | ex = e; 49 | } finally { 50 | doCleanup(ex); 51 | } 52 | } 53 | 54 | @Override 55 | public void interrupt() { 56 | terminate(); 57 | } 58 | 59 | @Override 60 | public void terminate() { 61 | terminationToken.setToShutdown(true); 62 | try { 63 | doTerminiate(); 64 | } finally { 65 | // 若无待处理的任务,则试图强制终止线程 66 | if (terminationToken.reservations.get()<=0) { 67 | super.interrupt(); 68 | } 69 | } 70 | } 71 | 72 | public void terminate(boolean waitUtilThreadTerminated){ 73 | terminate(); 74 | if(waitUtilThreadTerminated){ 75 | try { 76 | this.join(); 77 | } catch (InterruptedException e) { 78 | Thread.currentThread().interrupt(); 79 | } 80 | } 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/io/github/viscent/mtpattern/tpt/Terminatable.java: -------------------------------------------------------------------------------- 1 | package io.github.viscent.mtpattern.tpt; 2 | 3 | public interface Terminatable { 4 | void terminate(); 5 | } -------------------------------------------------------------------------------- /src/io/github/viscent/mtpattern/tpt/TerminationToken.java: -------------------------------------------------------------------------------- 1 | package io.github.viscent.mtpattern.tpt; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | public class TerminationToken { 6 | //使用volatile修饰,以保证无需显示锁的情况下该变量的内存可见性 7 | protected volatile boolean toShutdown = false; 8 | public final AtomicInteger reservations = new AtomicInteger(0); 9 | 10 | public boolean isToShutdown() { 11 | return toShutdown; 12 | } 13 | 14 | protected void setToShutdown(boolean toShutdown) { 15 | this.toShutdown = true; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/io/github/viscent/mtpattern/tpt/example/AlarmMgr.java: -------------------------------------------------------------------------------- 1 | package io.github.viscent.mtpattern.tpt.example; 2 | 3 | import io.github.viscent.mtpattern.tpt.AbstractTerminatableThread; 4 | import io.github.viscent.mtpattern.tpt.TerminationToken; 5 | 6 | import java.util.concurrent.BlockingQueue; 7 | import java.util.concurrent.LinkedBlockingQueue; 8 | 9 | 10 | 11 | public class AlarmMgr { 12 | private final BlockingQueue alarms = new LinkedBlockingQueue(); 13 | // 告警系统客户端API 14 | private final AlarmAgent alarmAgent = new AlarmAgent(); 15 | // 告警发送线程 16 | private final AbstractTerminatableThread alarmSendingThread; 17 | 18 | private boolean shutdownRequested = false; 19 | 20 | private static final AlarmMgr INSTANCE = new AlarmMgr(); 21 | 22 | private AlarmMgr() { 23 | alarmSendingThread = new AbstractTerminatableThread() { 24 | @Override 25 | protected void doRun() throws Exception { 26 | if (alarmAgent.waitUntilConnected()) { 27 | AlarmInfo alarm; 28 | alarm = alarms.take(); 29 | terminationToken.reservations.decrementAndGet(); 30 | try { 31 | alarmAgent.sendAlarm(alarm); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | 38 | @Override 39 | protected void doCleanup(Exception exp) { 40 | if (null != exp) { 41 | exp.printStackTrace(); 42 | } 43 | alarmAgent.disconnect(); 44 | } 45 | 46 | }; 47 | 48 | alarmAgent.init(); 49 | } 50 | 51 | public static AlarmMgr getInstance() { 52 | return INSTANCE; 53 | } 54 | 55 | public void sendAlarm(AlarmType type, String id, String extraInfo) { 56 | final TerminationToken terminationToken = alarmSendingThread.terminationToken; 57 | if (terminationToken.isToShutdown()) { 58 | // log the alarm 59 | System.err.println("rejected alarm:" + id + "," + extraInfo); 60 | return; 61 | 62 | } 63 | try { 64 | AlarmInfo alarm = new AlarmInfo(id, type); 65 | alarm.setExtraInfo(extraInfo); 66 | terminationToken.reservations.incrementAndGet(); 67 | alarms.add(alarm); 68 | } catch (Throwable t) { 69 | t.printStackTrace(); 70 | } 71 | } 72 | 73 | public void init() { 74 | alarmSendingThread.start(); 75 | } 76 | 77 | public synchronized void shutdown() { 78 | if (shutdownRequested) { 79 | throw new IllegalStateException("shutdown already requested!"); 80 | } 81 | 82 | alarmSendingThread.terminate(); 83 | shutdownRequested = true; 84 | } 85 | 86 | public int pendingAlarms() { 87 | return alarmSendingThread.terminationToken.reservations.get(); 88 | } 89 | 90 | } 91 | 92 | class AlarmInfo { 93 | private String id; 94 | private String extraInfo; 95 | private AlarmType type; 96 | 97 | public AlarmInfo(String id, AlarmType type) { 98 | this.id = id; 99 | this.type = type; 100 | 101 | } 102 | 103 | public String getId() { 104 | return id; 105 | } 106 | 107 | public void setId(String id) { 108 | this.id = id; 109 | } 110 | 111 | public String getExtraInfo() { 112 | return extraInfo; 113 | } 114 | 115 | public void setExtraInfo(String extraInfo) { 116 | this.extraInfo = extraInfo; 117 | } 118 | 119 | @Override 120 | public int hashCode() { 121 | final int prime = 31; 122 | int result = 1; 123 | result = prime * result + ((extraInfo == null) ? 0 : extraInfo.hashCode()); 124 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 125 | return result; 126 | } 127 | 128 | @Override 129 | public boolean equals(Object obj) { 130 | if (this == obj) 131 | return true; 132 | if (obj == null) 133 | return false; 134 | if (getClass() != obj.getClass()) 135 | return false; 136 | AlarmInfo other = (AlarmInfo) obj; 137 | if (extraInfo == null) { 138 | if (other.extraInfo != null) 139 | return false; 140 | } else if (!extraInfo.equals(other.extraInfo)) 141 | return false; 142 | if (id == null) { 143 | if (other.id != null) 144 | return false; 145 | } else if (!id.equals(other.id)) 146 | return false; 147 | return true; 148 | } 149 | 150 | @Override 151 | public String toString() { 152 | return "AlarmInfo [type=" + type + ",id=" + id + ", extraInfo=[" 153 | + extraInfo + "]]"; 154 | } 155 | 156 | } 157 | 158 | class AlarmAgent { 159 | // 省略其它代码 160 | private volatile boolean connectedToServer = false; 161 | 162 | public void sendAlarm(AlarmInfo alarm) throws Exception { 163 | // 省略其它代码 164 | System.out.println("Sending " + alarm); 165 | try { 166 | Thread.sleep(50); 167 | } catch (Exception e) { 168 | 169 | } 170 | } 171 | 172 | public void init() { 173 | // 省略其它代码 174 | connectedToServer = true; 175 | } 176 | 177 | public void disconnect() { 178 | // 省略其它代码 179 | System.out.println("disconnected from alarm server."); 180 | } 181 | 182 | public boolean waitUntilConnected() { 183 | // 省略其它代码 184 | return connectedToServer; 185 | } 186 | } -------------------------------------------------------------------------------- /src/io/github/viscent/mtpattern/tpt/example/AlarmType.java: -------------------------------------------------------------------------------- 1 | package io.github.viscent.mtpattern.tpt.example; 2 | 3 | public enum AlarmType { 4 | FAULT, 5 | RESUME, 6 | EVENT 7 | } 8 | -------------------------------------------------------------------------------- /src/io/github/viscent/mtpattern/tpt/example/ProducerConsumerStop.java: -------------------------------------------------------------------------------- 1 | package io.github.viscent.mtpattern.tpt.example; 2 | 3 | import io.github.viscent.mtpattern.tpt.AbstractTerminatableThread; 4 | 5 | import java.util.concurrent.BlockingQueue; 6 | import java.util.concurrent.LinkedBlockingQueue; 7 | 8 | 9 | public class ProducerConsumerStop { 10 | private static class SampleConsumer

{ 11 | private final BlockingQueue

queue = new LinkedBlockingQueue

(); 12 | 13 | private AbstractTerminatableThread workThread 14 | = new AbstractTerminatableThread() { 15 | @Override 16 | protected void doRun() throws Exception { 17 | terminationToken.reservations.decrementAndGet(); 18 | P product = queue.take(); 19 | // ... 20 | System.out.println(product); 21 | } 22 | 23 | }; 24 | 25 | public void placeProduct(P product) { 26 | if (workThread.terminationToken.isToShutdown()) { 27 | throw new IllegalStateException("Thread shutdown"); 28 | } 29 | try { 30 | queue.put(product); 31 | workThread.terminationToken.reservations.incrementAndGet(); 32 | } catch (InterruptedException e) { 33 | 34 | } 35 | } 36 | 37 | public void shutdown() { 38 | workThread.terminate(); 39 | } 40 | 41 | public void start() { 42 | workThread.start(); 43 | } 44 | } 45 | 46 | public void test() { 47 | final SampleConsumer aConsumer = new SampleConsumer(); 48 | 49 | AbstractTerminatableThread aProducer = new AbstractTerminatableThread() { 50 | private int i = 0; 51 | 52 | @Override 53 | protected void doRun() throws Exception { 54 | aConsumer.placeProduct(String.valueOf(i)); 55 | } 56 | 57 | @Override 58 | protected void doCleanup(Exception cause) { 59 | // 生产者线程停止完毕后再请求停止消费者线程 60 | aConsumer.shutdown(); 61 | } 62 | 63 | }; 64 | 65 | aProducer.start(); 66 | aConsumer.start(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/io/github/viscent/mtpattern/tpt/example/TestAlarmMgrShutdown.java: -------------------------------------------------------------------------------- 1 | package io.github.viscent.mtpattern.tpt.example; 2 | 3 | import static org.junit.Assert.*; 4 | import io.github.viscent.mtpattern.tpt.AbstractTerminatableThread; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | 10 | public class TestAlarmMgrShutdown { 11 | private AlarmMgr alarmMgr; 12 | 13 | @Before 14 | public void setUp() throws Exception { 15 | alarmMgr = AlarmMgr.getInstance(); 16 | alarmMgr.init(); 17 | 18 | } 19 | 20 | 21 | @Test 22 | public void testShutdown() { 23 | AbstractTerminatableThread producer = new AbstractTerminatableThread() { 24 | private int i = 0; 25 | 26 | @Override 27 | protected void doRun() throws Exception { 28 | alarmMgr.sendAlarm(AlarmType.FAULT, "001", "key1=value" + (i++)); 29 | Thread.sleep(30); 30 | } 31 | 32 | @Override 33 | protected void doCleanup(Exception cause) { 34 | System.out.println("Alarms triggered:" + i + ",pending alarm:" 35 | + alarmMgr.pendingAlarms()); 36 | alarmMgr.shutdown(); 37 | } 38 | 39 | }; 40 | 41 | producer.start(); 42 | 43 | try { 44 | Thread.sleep(1000); 45 | } catch (InterruptedException e) { 46 | } 47 | 48 | producer.terminate(); 49 | 50 | try { 51 | Thread.sleep(1000); 52 | } catch (InterruptedException e) { 53 | } 54 | 55 | assertEquals(0, alarmMgr.pendingAlarms()); 56 | } 57 | 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/log4j.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | # An example log4j configuration file that outputs to System.out. The 18 | # output information consists of relative time, log level, thread 19 | # name, logger name, nested diagnostic context and the message in that 20 | # order. 21 | 22 | # For the general syntax of property based configuration files see the 23 | # documenation of org.apache.log4j.PropertyConfigurator. 24 | 25 | log4j.rootLogger=DEBUG, A1,file 26 | 27 | # A1 is set to be a ConsoleAppender which outputs to System.out. 28 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 29 | 30 | # A1 uses PatternLayout. 31 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 32 | 33 | # The conversion pattern uses format specifiers. You might want to 34 | # change the pattern an watch the output format change. 35 | log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %m%n 36 | 37 | # Direct log messages to a log file 38 | log4j.appender.file=org.apache.log4j.RollingFileAppender 39 | 40 | #Redirect to Tomcat logs folder 41 | #log4j.appender.file.File=${catalina.home}/logs/logging.log 42 | 43 | log4j.appender.file.File=/var/tmp/logs/ao.log 44 | log4j.appender.file.MaxFileSize=10MB 45 | log4j.appender.file.MaxBackupIndex=10 46 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 47 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 48 | 49 | # In this example, we are not really interested in INNER loop or SWAP 50 | # messages. See the effects of uncommenting and changing the levels of 51 | # the following loggers. 52 | # log4j.logger.org.apache.log4j.examples.SortAlgo.INNER=WARN 53 | 54 | log4j.logger.vh.activeobject.AsyncRequestPersistence=INFO 55 | 56 | -------------------------------------------------------------------------------- /src/vh/activeobject/AsyncRequestPersistence.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.LinkedBlockingQueue; 7 | import java.util.concurrent.ThreadFactory; 8 | import java.util.concurrent.ThreadPoolExecutor; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | import java.util.concurrent.atomic.AtomicLong; 12 | 13 | import org.apache.log4j.Logger; 14 | 15 | /** 16 | * 17 | * @author viscent 18 | * 19 | * Active Object, Proxy Benefits of applying Active Object: Simplifying 20 | * implementing fault isolation 21 | * 22 | * 实际运用考虑: 队列监控:监控处理队列是否积压 KeepAliveTime:空闲线程清理 队列容量:做好存储规划 23 | * 处理过慢,队列满的问题:无论是自己实现度列还是直接使用ThreadPoolExecutor 24 | * ,都需要处理该问题。而ThreadPoolExecutor了预置了一些队列满的处理策略。 25 | * 26 | * ThreadPoolExecutor:当线程池中工作线程数逐渐增加到核心工作线程数时,此时新提交的任务会被放入工作队列。 27 | * 当核心工作线程的处理速率小于工作任务的提交速率时, 28 | * 工作队列会逐渐积压。当工作队列满时,ThreadPoolExecutor会追加工作线程直到其数量到达线程池的最大大小 29 | * 。此时,新提交的工作任务会被拒绝(即提交失败) 30 | * 31 | * 32 | */ 33 | // ActiveObjectPattern.Proxy 34 | public class AsyncRequestPersistence implements RequestPersistence { 35 | private static final long ONE_MINUTE_IN_SECONDS = 60; 36 | private final Logger logger; 37 | private final AtomicLong taskTimeConsumedPerInterval = new AtomicLong(0); 38 | private final AtomicInteger requestSubmittedPerIterval = new AtomicInteger(0); 39 | 40 | // ActiveObjectPattern.Servant 41 | private final DiskbasedRequestPersistence delegate = new DiskbasedRequestPersistence(); 42 | 43 | // ActiveObjectPattern.Scheduler 44 | private final ThreadPoolExecutor scheduler; 45 | 46 | private static class InstanceHolder { 47 | final static RequestPersistence INSTANCE = new AsyncRequestPersistence(); 48 | } 49 | 50 | private AsyncRequestPersistence() { 51 | logger = Logger.getLogger(AsyncRequestPersistence.class); 52 | scheduler = new ThreadPoolExecutor(1, 3, 60 * ONE_MINUTE_IN_SECONDS, 53 | TimeUnit.SECONDS, 54 | // ActiveObjectPattern.ActivationQueue 55 | new LinkedBlockingQueue(200), new ThreadFactory() { 56 | @Override 57 | public Thread newThread(Runnable r) { 58 | Thread t; 59 | t = new Thread(r, "AsyncRequestPersistence"); 60 | return t; 61 | } 62 | 63 | }); 64 | 65 | scheduler 66 | .setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); 67 | 68 | // 启动队列监控定时任务 69 | Timer monitorTimer = new Timer(true); 70 | monitorTimer.scheduleAtFixedRate(new TimerTask() { 71 | 72 | @Override 73 | public void run() { 74 | if (logger.isInfoEnabled()) { 75 | 76 | logger.info("task count:" + requestSubmittedPerIterval 77 | + ",Queue size:" + scheduler.getQueue().size() 78 | + ",taskTimeConsumedPerInterval:" 79 | + taskTimeConsumedPerInterval.get() + " ms"); 80 | } 81 | 82 | taskTimeConsumedPerInterval.set(0); 83 | requestSubmittedPerIterval.set(0); 84 | 85 | } 86 | }, 0, ONE_MINUTE_IN_SECONDS * 1000); 87 | 88 | } 89 | 90 | public static RequestPersistence getInstance() { 91 | return InstanceHolder.INSTANCE; 92 | } 93 | 94 | @Override 95 | public void store(final MMSDeliverRequest request) { 96 | /* 97 | * 将对store方法的调用封装成MethodRequest对象, 并存入缓冲区。 98 | */ 99 | // ActiveObjectPattern.MethodRequest 100 | Callable methodRequest = new Callable() { 101 | @Override 102 | public Boolean call() throws Exception { 103 | long start = System.currentTimeMillis(); 104 | try { 105 | delegate.store(request); 106 | } finally { 107 | taskTimeConsumedPerInterval.addAndGet(System.currentTimeMillis() 108 | - start); 109 | } 110 | 111 | return Boolean.TRUE; 112 | } 113 | 114 | }; 115 | scheduler.submit(methodRequest); 116 | 117 | requestSubmittedPerIterval.incrementAndGet(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/vh/activeobject/CustomScheduler.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.Future; 6 | import java.util.concurrent.FutureTask; 7 | import java.util.concurrent.LinkedBlockingQueue; 8 | 9 | public class CustomScheduler implements Runnable { 10 | private LinkedBlockingQueue activationQueue = new LinkedBlockingQueue(); 11 | 12 | @Override 13 | public void run() { 14 | dispatch(); 15 | } 16 | 17 | public Future enqueue(Callable methodRequest) { 18 | final FutureTask task = new FutureTask(methodRequest) { 19 | 20 | @Override 21 | public void run() { 22 | try { 23 | super.run(); 24 | // 捕获所以可能抛出的对象,避免该任务运行失败而导致其所在的线程终止。 25 | } catch (Throwable t) { 26 | this.setException(t); 27 | } 28 | } 29 | 30 | }; 31 | 32 | try { 33 | activationQueue.put(task); 34 | } catch (InterruptedException e) { 35 | Thread.currentThread().interrupt(); 36 | } 37 | return task; 38 | } 39 | 40 | public void dispatch() { 41 | while (true) { 42 | Runnable methodRequest; 43 | try { 44 | methodRequest = activationQueue.take(); 45 | 46 | // 防止个别任务执行失败导致线程终止的代码在run方法中 47 | methodRequest.run(); 48 | } catch (InterruptedException e) { 49 | // 处理该异常 50 | } 51 | 52 | } 53 | } 54 | 55 | public static void main(String[] args) { 56 | 57 | CustomScheduler scheduler = new CustomScheduler(); 58 | Thread t = new Thread(scheduler); 59 | t.start(); 60 | Future result = scheduler.enqueue(new Callable() { 61 | 62 | @Override 63 | public String call() throws Exception { 64 | Thread.sleep(1500); 65 | int i = 1; 66 | if (1 == i) { 67 | throw new RuntimeException("test"); 68 | } 69 | return "ok"; 70 | } 71 | 72 | }); 73 | 74 | try { 75 | System.out.println(result.get()); 76 | ; 77 | } catch (InterruptedException e) { 78 | e.printStackTrace(); 79 | } catch (ExecutionException e) { 80 | e.printStackTrace(); 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/vh/activeobject/DiskbasedRequestPersistence.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.ObjectOutputStream; 8 | import java.text.DecimalFormat; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | import java.util.Deque; 12 | import java.util.HashMap; 13 | import java.util.LinkedList; 14 | import java.util.Map; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | import org.apache.log4j.Logger; 18 | 19 | public class DiskbasedRequestPersistence implements RequestPersistence { 20 | // 负责缓存文件的存储管理 21 | private final SectionBasedDiskStorage storage = new SectionBasedDiskStorage(); 22 | private final Logger logger = Logger 23 | .getLogger(DiskbasedRequestPersistence.class); 24 | 25 | @Override 26 | public void store(MMSDeliverRequest request) { 27 | // 申请缓存文件的文件名 28 | String[] fileNameParts = storage.apply4Filename(request); 29 | File file = new File(fileNameParts[0]); 30 | try { 31 | ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream( 32 | file)); 33 | try { 34 | objOut.writeObject(request); 35 | } finally { 36 | objOut.close(); 37 | } 38 | } catch (FileNotFoundException e) { 39 | storage.decrementSectionFileCount(fileNameParts[1]); 40 | logger.error("Failed to store request", e); 41 | } catch (IOException e) { 42 | storage.decrementSectionFileCount(fileNameParts[1]); 43 | logger.error("Failed to store request", e); 44 | } 45 | 46 | } 47 | 48 | class SectionBasedDiskStorage { 49 | private Deque sectionNames = new LinkedList(); 50 | /* 51 | * Key->value: 存储子目录名->子目录下缓存文件计数器 52 | */ 53 | private Map sectionFileCountMap = new HashMap(); 54 | private int maxFilesPerSection = 2000; 55 | private int maxSectionCount = 100; 56 | private String storageBaseDir = System.getProperty("user.dir") + "/vpn"; 57 | 58 | private final Object sectionLock = new Object(); 59 | 60 | public String[] apply4Filename(MMSDeliverRequest request) { 61 | String sectionName; 62 | int iFileCount; 63 | boolean need2RemoveSection = false; 64 | String[] fileName = new String[2]; 65 | synchronized (sectionLock) { 66 | // 获取当前的存储子目录名 67 | sectionName = this.getSectionName(); 68 | AtomicInteger fileCount; 69 | fileCount = sectionFileCountMap.get(sectionName); 70 | iFileCount = fileCount.get(); 71 | // 当前存储子目录已满 72 | if (iFileCount >= maxFilesPerSection) { 73 | if (sectionNames.size() >= maxSectionCount) { 74 | need2RemoveSection = true; 75 | } 76 | // 创建新的存储子目录 77 | sectionName = this.makeNewSectionDir(); 78 | fileCount = sectionFileCountMap.get(sectionName); 79 | 80 | } 81 | iFileCount = fileCount.addAndGet(1); 82 | 83 | } 84 | 85 | fileName[0] = storageBaseDir + "/" + sectionName + "/" 86 | + new DecimalFormat("0000").format(iFileCount) + "-" 87 | + request.getTimeStamp().getTime() / 1000 + "-" + request.getExpiry() 88 | + ".rq"; 89 | fileName[1] = sectionName; 90 | 91 | if (need2RemoveSection) { 92 | // 删除最老的存储子目录 93 | String oldestSectionName = sectionNames.removeFirst(); 94 | this.removeSection(oldestSectionName); 95 | } 96 | 97 | return fileName; 98 | } 99 | 100 | public void decrementSectionFileCount(String sectionName) { 101 | AtomicInteger fileCount = sectionFileCountMap.get(sectionName); 102 | if (null != fileCount) { 103 | fileCount.decrementAndGet(); 104 | } 105 | } 106 | 107 | private boolean removeSection(String sectionName) { 108 | boolean result = true; 109 | File dir = new File(storageBaseDir + "/" + sectionName); 110 | for (File file : dir.listFiles()) { 111 | result = result && file.delete(); 112 | } 113 | result = result && dir.delete(); 114 | return result; 115 | } 116 | 117 | private String getSectionName() { 118 | String sectionName; 119 | 120 | if (sectionNames.isEmpty()) { 121 | sectionName = this.makeNewSectionDir(); 122 | 123 | } else { 124 | sectionName = sectionNames.getLast(); 125 | } 126 | 127 | return sectionName; 128 | } 129 | 130 | private String makeNewSectionDir() { 131 | String sectionName; 132 | SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss"); 133 | sectionName = sdf.format(new Date()); 134 | File dir = new File(storageBaseDir + "/" + sectionName); 135 | if (dir.mkdir()) { 136 | sectionNames.addLast(sectionName); 137 | sectionFileCountMap.put(sectionName, new AtomicInteger(0)); 138 | } else { 139 | throw new RuntimeException("Cannot create section dir " + sectionName); 140 | } 141 | 142 | return sectionName; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/vh/activeobject/MMSDeliverRequest.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject; 2 | 3 | import java.io.Serializable; 4 | import java.util.Collections; 5 | import java.util.Date; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | public class MMSDeliverRequest implements Serializable { 10 | private String transactionID; 11 | private String messageType = "Delivery.req"; 12 | private String senderAddress; 13 | private Recipient recipient = new Recipient(); 14 | private String subject; 15 | private Attachment attachment = new Attachment(); 16 | 17 | public long getExpiry() { 18 | return expiry; 19 | } 20 | 21 | private long expiry; 22 | private Date timeStamp; 23 | 24 | public MMSDeliverRequest() { 25 | 26 | } 27 | 28 | public void setExpiry(long expiry) { 29 | this.expiry = expiry; 30 | } 31 | 32 | public Date getTimeStamp() { 33 | return timeStamp; 34 | } 35 | 36 | public void setTimeStamp(Date timeStamp) { 37 | this.timeStamp = timeStamp; 38 | } 39 | 40 | public String getTransactionID() { 41 | return transactionID; 42 | } 43 | 44 | public void setTransactionID(String transactionID) { 45 | this.transactionID = transactionID; 46 | } 47 | 48 | public String getMessageType() { 49 | return messageType; 50 | } 51 | 52 | public void setMessageType(String messageType) { 53 | this.messageType = messageType; 54 | } 55 | 56 | public String getSenderAddress() { 57 | return senderAddress; 58 | } 59 | 60 | public void setSenderAddress(String senderAddress) { 61 | this.senderAddress = senderAddress; 62 | } 63 | 64 | public Recipient getRecipient() { 65 | return recipient; 66 | } 67 | 68 | public void setRecipient(Recipient recipient) { 69 | this.recipient = recipient; 70 | } 71 | 72 | public Attachment getAttachment() { 73 | return attachment; 74 | } 75 | 76 | public void setAttachment(Attachment attachment) { 77 | this.attachment = attachment; 78 | } 79 | 80 | public String getSubject() { 81 | return subject; 82 | } 83 | 84 | public void setSubject(String subject) { 85 | this.subject = subject; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return "MM7DeliverRequest [transactionID=" + transactionID 91 | + ", messageType=" + messageType + ", senderAddress=" + senderAddress 92 | + ", recipient=" + recipient + ", subject=" + subject + ", attachment=" 93 | + attachment + "]"; 94 | } 95 | 96 | private static final long serialVersionUID = 302185079311891797L; 97 | 98 | } 99 | 100 | class Recipient implements Serializable { 101 | 102 | private static final long serialVersionUID = -5427696559429827584L; 103 | private Set to = new HashSet(); 104 | 105 | public void addTo(String msisdn) { 106 | to.add(msisdn); 107 | } 108 | 109 | public Set getToList() { 110 | return (Set) Collections.unmodifiableCollection(to); 111 | } 112 | 113 | } 114 | 115 | class Attachment implements Serializable { 116 | private static final long serialVersionUID = -313285270497968496L; 117 | private String contentType; 118 | private byte[] content = new byte[0]; 119 | 120 | public String getContentType() { 121 | return contentType; 122 | } 123 | 124 | public void setContentType(String contentType) { 125 | this.contentType = contentType; 126 | } 127 | 128 | public byte[] getContent() { 129 | return content; 130 | } 131 | 132 | public void setContent(byte[] content) { 133 | this.content = content; 134 | } 135 | 136 | @Override 137 | public String toString() { 138 | return "Attachment [contentType=" + contentType + ", content=" 139 | + content.length + "]"; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/vh/activeobject/RequestPersistence.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject; 2 | 3 | 4 | public interface RequestPersistence { 5 | 6 | void store(MMSDeliverRequest request); 7 | } 8 | -------------------------------------------------------------------------------- /src/vh/activeobject/Test.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.nio.ByteBuffer; 8 | import java.util.Date; 9 | import java.util.concurrent.LinkedBlockingQueue; 10 | import java.util.concurrent.ThreadPoolExecutor; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | import org.junit.Before; 15 | 16 | public class Test { 17 | private RequestPersistence persistence; 18 | private ThreadPoolExecutor executor; 19 | private Attachment attachment; 20 | 21 | @Before 22 | public void setUp() { 23 | persistence = AsyncRequestPersistence.getInstance(); 24 | executor = new ThreadPoolExecutor(80, 200, 60 * 3600, TimeUnit.SECONDS, 25 | new LinkedBlockingQueue(300)); 26 | try { 27 | File file = new File("/home/viscent/tmp/callstack.png"); 28 | ByteBuffer contentBuf = ByteBuffer.allocate((int) file.length()); 29 | FileInputStream fin = new FileInputStream(file); 30 | try { 31 | fin.getChannel().read(contentBuf); 32 | } finally { 33 | fin.close(); 34 | } 35 | attachment = new Attachment(); 36 | attachment.setContentType("image/png"); 37 | attachment.setContent(contentBuf.array()); 38 | } catch (FileNotFoundException e1) { 39 | e1.printStackTrace(); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | final AtomicInteger counter = new AtomicInteger(0); 46 | 47 | class RequestSenderThread extends Thread { 48 | private int chunkSize; 49 | private int timeSpan; 50 | 51 | public RequestSenderThread(int chunkSize, int timeSpan) { 52 | this.chunkSize = chunkSize; 53 | this.timeSpan = timeSpan; 54 | } 55 | 56 | @Override 57 | public void run() { 58 | int sleepCount = (chunkSize / timeSpan); 59 | for (int i = 0; i < chunkSize; i++) { 60 | 61 | executor.execute(new Runnable() { 62 | 63 | @Override 64 | public void run() { 65 | MMSDeliverRequest request = new MMSDeliverRequest(); 66 | request.setTransactionID(String.valueOf(counter.incrementAndGet())); 67 | request.setSenderAddress("13612345678"); 68 | request.setTimeStamp(new Date()); 69 | request.setExpiry((new Date().getTime() + 3600000) / 1000); 70 | 71 | request.setSubject("Hi"); 72 | request.getRecipient().addTo("776"); 73 | request.setAttachment(attachment); 74 | 75 | persistence.store(request); 76 | 77 | } 78 | }); 79 | 80 | // System.out.println(this.getId()+" sent "+i+1); 81 | if (0 == (i % sleepCount)) { 82 | try { 83 | Thread.sleep(1000); 84 | } catch (InterruptedException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | 89 | } 90 | } 91 | 92 | } 93 | 94 | @org.junit.Test 95 | public void testFaultIsolation() { 96 | RequestSenderThread sender; 97 | for (int i = 0; i < 10; i++) { 98 | sender = new RequestSenderThread(200000, 10000); 99 | sender.start(); 100 | } 101 | 102 | try { 103 | executor.awaitTermination(2L, TimeUnit.HOURS); 104 | } catch (InterruptedException e1) { 105 | e1.printStackTrace(); 106 | } 107 | 108 | } 109 | 110 | public void testTimeConsumption() { 111 | MMSDeliverRequest request = new MMSDeliverRequest(); 112 | request.setTransactionID(String.valueOf(counter.incrementAndGet())); 113 | request.setSenderAddress("13612345678"); 114 | request.setTimeStamp(new Date()); 115 | request.setExpiry((new Date().getTime() + 3600000) / 1000); 116 | 117 | request.setSubject("Hi"); 118 | request.getRecipient().addTo("776"); 119 | request.setAttachment(attachment); 120 | DiskbasedRequestPersistence rp = new DiskbasedRequestPersistence(); 121 | long start = System.currentTimeMillis(); 122 | rp.store(request); 123 | // About took 15ms to write a single file of 218KB 124 | System.out.println("Took " + (System.currentTimeMillis() - start)); 125 | } 126 | 127 | // private void a(){ 128 | // ActiveObject ao=...; 129 | // Future future=ao.doSomething("e"); 130 | // //其它代码 131 | // String result=future.get(); 132 | // System.out.println(result); 133 | // } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/vh/activeobject/lib/ActiveObjectProxy.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject.lib; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.Proxy; 7 | import java.util.concurrent.Callable; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | 11 | public abstract class ActiveObjectProxy { 12 | 13 | private static class DispatchInvocationHandler implements InvocationHandler { 14 | private final Object delegate; 15 | private final ExecutorService scheduler; 16 | 17 | public DispatchInvocationHandler(Object delegate, 18 | ExecutorService executorService) { 19 | this.delegate = delegate; 20 | this.scheduler = executorService; 21 | } 22 | 23 | private String makeDelegateMethodName(final Method method, 24 | final Object[] arg) { 25 | String name = method.getName(); 26 | name = "do" + Character.toUpperCase(name.charAt(0)) + name.substring(1); 27 | 28 | return name; 29 | } 30 | 31 | @Override 32 | public Object invoke(final Object proxy, final Method method, 33 | final Object[] args) throws Throwable { 34 | 35 | Object returnValue = null; 36 | final Object delegate = this.delegate; 37 | final Method delegateMethod; 38 | 39 | // 如果拦截到的被调用方法是异步方法,则将其转发到相应的doXXX方法 40 | if (Future.class.isAssignableFrom(method.getReturnType())) { 41 | delegateMethod = delegate.getClass().getMethod( 42 | makeDelegateMethodName(method, args), method.getParameterTypes()); 43 | 44 | final ExecutorService scheduler = this.scheduler; 45 | 46 | Callable methodRequest = new Callable() { 47 | @Override 48 | public Object call() throws Exception { 49 | Object rv = null; 50 | 51 | try { 52 | rv = delegateMethod.invoke(delegate, args); 53 | } catch (IllegalArgumentException e) { 54 | throw new Exception(e); 55 | } catch (IllegalAccessException e) { 56 | throw new Exception(e); 57 | } catch (InvocationTargetException e) { 58 | throw new Exception(e); 59 | } 60 | return rv; 61 | } 62 | }; 63 | Future future = scheduler.submit(methodRequest); 64 | returnValue = future; 65 | 66 | } else { 67 | 68 | // 若拦截到的方法调用不是异步方法,则直接转发 69 | delegateMethod = delegate.getClass().getMethod(method.getName(), 70 | method.getParameterTypes()); 71 | returnValue = delegateMethod.invoke(delegate, args); 72 | } 73 | 74 | return returnValue; 75 | } 76 | } 77 | 78 | /** 79 | * 生成一个实现指定接口的Active Object proxy实例。 对interf所定义的异步方法的调用会被装发到servant的相应doXXX方法。 80 | * 81 | * @param interf 82 | * 要实现的Active Object接口 83 | * @param servant 84 | * Active Object的Servant参与者实例 85 | * @param scheduler 86 | * Active Object的Scheduler参与者实例 87 | * @return Active Object的Proxy参与者实例 88 | */ 89 | public static T newInstance(Class interf, Object servant, 90 | ExecutorService scheduler) { 91 | 92 | @SuppressWarnings("unchecked") 93 | T f = (T) Proxy.newProxyInstance(interf.getClassLoader(), 94 | new Class[] { interf }, new DispatchInvocationHandler(servant, 95 | scheduler)); 96 | 97 | return f; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/vh/activeobject/lib/AsyncPerformanceTest.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject.lib; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | import java.util.concurrent.ArrayBlockingQueue; 6 | import java.util.concurrent.BlockingQueue; 7 | import java.util.concurrent.BrokenBarrierException; 8 | import java.util.concurrent.CyclicBarrier; 9 | import java.util.concurrent.Future; 10 | import java.util.concurrent.ThreadPoolExecutor; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | import java.util.concurrent.atomic.AtomicLong; 14 | 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | 18 | public class AsyncPerformanceTest { 19 | 20 | private AtomicInteger reqSubmitted; 21 | private AtomicInteger reqFailed; 22 | private AtomicLong resDelay; 23 | private CyclicBarrier barrier; 24 | private int N = 100; 25 | private Runnable runnable; 26 | 27 | private BlockingQueue q; 28 | 29 | private void processResult(String s) { 30 | 31 | } 32 | 33 | @Before 34 | public void setUp() throws Exception { 35 | barrier = new CyclicBarrier(N); 36 | reqSubmitted = new AtomicInteger(0); 37 | reqFailed = new AtomicInteger(0); 38 | resDelay = new AtomicLong(0); 39 | 40 | q=new ArrayBlockingQueue(300); 41 | 42 | runnable = new Runnable() { 43 | private final SampleActiveObject sao = ActiveObjectProxy.newInstance( 44 | SampleActiveObject.class, new SampleActiveObjectImpl(), 45 | new ThreadPoolExecutor(100, 200, 60 * 3600, 46 | TimeUnit.SECONDS, q)); 47 | //Executors.newCachedThreadPool()); 48 | 49 | @Override 50 | public void run() { 51 | try { 52 | barrier.await(); 53 | } catch (InterruptedException e1) { 54 | // TODO Auto-generated catch block 55 | e1.printStackTrace(); 56 | } catch (BrokenBarrierException e1) { 57 | // TODO Auto-generated catch block 58 | e1.printStackTrace(); 59 | } 60 | long begin; 61 | Future result; 62 | while (true) { 63 | begin = System.currentTimeMillis(); 64 | try { 65 | result = sao.process("sync", 1); 66 | try { 67 | Thread.sleep(50); 68 | } catch (InterruptedException e) { 69 | } 70 | 71 | processResult(result.get()); 72 | resDelay.addAndGet(System.currentTimeMillis() - begin); 73 | } catch (Exception e1) { 74 | reqFailed.incrementAndGet(); 75 | } finally { 76 | reqSubmitted.incrementAndGet(); 77 | 78 | } 79 | 80 | 81 | } 82 | 83 | } 84 | 85 | }; 86 | 87 | } 88 | 89 | @Test 90 | public void test() throws InterruptedException { 91 | Thread t; 92 | for (int i = 0; i < N; i++) { 93 | t = new Thread(runnable); 94 | t.start(); 95 | } 96 | 97 | new Timer().schedule(new TimerTask() { 98 | 99 | @Override 100 | public void run() { 101 | System.out.println(reqSubmitted + "," + reqFailed + "," + resDelay+ "," + q.size()); 102 | reqSubmitted.set(0); 103 | reqFailed.set(0); 104 | resDelay.set(0); 105 | 106 | } 107 | 108 | }, 2000, 2 * 1000); 109 | Thread.sleep(3600 * 1000); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/vh/activeobject/lib/PerformanceTest.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject.lib; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | import java.util.concurrent.BrokenBarrierException; 6 | import java.util.concurrent.CyclicBarrier; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | import java.util.concurrent.atomic.AtomicLong; 9 | 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | public class PerformanceTest { 15 | private AtomicInteger reqSubmitted; 16 | private AtomicInteger reqFailed; 17 | private AtomicLong resDelay; 18 | private CyclicBarrier barrier; 19 | private int N = 100; 20 | private Runnable runnable = new Runnable() { 21 | private final SampleActiveObjectImpl sao = new SampleActiveObjectImpl(); 22 | 23 | @Override 24 | public void run() { 25 | try { 26 | barrier.await(); 27 | } catch (InterruptedException e1) { 28 | // TODO Auto-generated catch block 29 | e1.printStackTrace(); 30 | } catch (BrokenBarrierException e1) { 31 | // TODO Auto-generated catch block 32 | e1.printStackTrace(); 33 | } 34 | long begin; 35 | String result; 36 | while (true) { 37 | begin = System.currentTimeMillis(); 38 | result=sao.doProcess("sync", 1); 39 | processResult(result); 40 | resDelay.addAndGet(System.currentTimeMillis() - begin); 41 | reqSubmitted.incrementAndGet(); 42 | try { 43 | Thread.sleep(50); 44 | } catch (InterruptedException e) { 45 | } 46 | } 47 | 48 | } 49 | 50 | }; 51 | 52 | 53 | private void processResult(String s){ 54 | 55 | } 56 | 57 | @Before 58 | public void setUp() throws Exception { 59 | barrier = new CyclicBarrier(N); 60 | reqSubmitted = new AtomicInteger(0); 61 | reqFailed = new AtomicInteger(0); 62 | resDelay = new AtomicLong(0); 63 | 64 | } 65 | 66 | @After 67 | public void tearDown() throws Exception { 68 | } 69 | 70 | @Test 71 | public void test() throws InterruptedException { 72 | Thread t; 73 | for (int i = 0; i < N; i++) { 74 | t = new Thread(runnable); 75 | t.start(); 76 | } 77 | 78 | new Timer().schedule(new TimerTask() { 79 | 80 | @Override 81 | public void run() { 82 | System.out.println(reqSubmitted + "," + reqFailed + "," + resDelay); 83 | reqSubmitted.set(0); 84 | reqFailed.set(0); 85 | resDelay.set(0); 86 | } 87 | 88 | }, 2000, 2 * 1000); 89 | Thread.sleep(3600 * 1000); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/vh/activeobject/lib/SampleActiveObject.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject.lib; 2 | 3 | import java.util.concurrent.Future; 4 | 5 | public interface SampleActiveObject { 6 | public Future process(String arg, int i); 7 | } -------------------------------------------------------------------------------- /src/vh/activeobject/lib/SampleActiveObjectImpl.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject.lib; 2 | 3 | public class SampleActiveObjectImpl { 4 | 5 | public String doProcess(String arg, int i) { 6 | try { 7 | // 模拟一个比较耗时的操作 8 | Thread.sleep(50); 9 | } catch (InterruptedException e) { 10 | e.printStackTrace(); 11 | } 12 | return arg + "-" + i; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/vh/activeobject/lib/Test.java: -------------------------------------------------------------------------------- 1 | package vh.activeobject.lib; 2 | 3 | import java.util.concurrent.*; 4 | 5 | public class Test { 6 | 7 | public static void main(String[] args) throws InterruptedException, 8 | ExecutionException { 9 | 10 | SampleActiveObject sao = ActiveObjectProxy.newInstance( 11 | SampleActiveObject.class, new SampleActiveObjectImpl(), 12 | Executors.newCachedThreadPool()); 13 | Future ft = null; 14 | try { 15 | ft = sao.process("Something", 1); 16 | } catch (Exception e) { 17 | e.printStackTrace(); 18 | } 19 | Thread.sleep(500); 20 | System.out.println(ft.get()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/vh/immutableobject/MMSCInfo.java: -------------------------------------------------------------------------------- 1 | package vh.immutableobject; 2 | 3 | /** 4 | * 彩信中心信息 5 | * 6 | * @author viscent 7 | * 8 | */ 9 | public final class MMSCInfo { 10 | /** 11 | * 设备编号 12 | */ 13 | private final String deviceID; 14 | /** 15 | * 彩信中心URL 16 | */ 17 | private final String url; 18 | /** 19 | * 该彩信中心允许的最大附件大小 20 | */ 21 | private final int maxAttachmentSizeInBytes; 22 | 23 | public MMSCInfo(String deviceID, String url, int maxAttachmentSizeInBytes) { 24 | this.deviceID = deviceID; 25 | this.url = url; 26 | this.maxAttachmentSizeInBytes = maxAttachmentSizeInBytes; 27 | } 28 | 29 | public MMSCInfo(MMSCInfo prototype) { 30 | this.deviceID = prototype.deviceID; 31 | this.url = prototype.url; 32 | this.maxAttachmentSizeInBytes = prototype.maxAttachmentSizeInBytes; 33 | } 34 | 35 | public String getDeviceID() { 36 | return deviceID; 37 | } 38 | 39 | public String getUrl() { 40 | return url; 41 | } 42 | 43 | public int getMaxAttachmentSizeInBytes() { 44 | return maxAttachmentSizeInBytes; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/vh/immutableobject/MMSCRouter.java: -------------------------------------------------------------------------------- 1 | package vh.immutableobject; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | *彩信中心路由规则管理器 9 | * 10 | */ 11 | public final class MMSCRouter { 12 | // 用volatile修饰,保证多线程环境下该变量的可见性 13 | private static volatile MMSCRouter instance = new MMSCRouter(); 14 | //维护手机号码前缀到彩信中心之间的映射关系 15 | private final Map routeMap; 16 | 17 | public MMSCRouter() { 18 | // 将数据库表中的数据加载到内存,存为Map 19 | this.routeMap = MMSCRouter.retrieveRouteMapFromDB(); 20 | } 21 | 22 | private static Map retrieveRouteMapFromDB() { 23 | Map map = new HashMap(); 24 | // 省略其它代码 25 | return map; 26 | } 27 | 28 | public static MMSCRouter getInstance() { 29 | 30 | return instance; 31 | } 32 | 33 | /** 34 | * 根据手机号码前缀获取对应的彩信中心信息 35 | * 36 | * @param msisdnPrefix 37 | * 手机号码前缀 38 | * @return 彩信中心信息 39 | */ 40 | public MMSCInfo getMMSC(String msisdnPrefix) { 41 | return routeMap.get(msisdnPrefix); 42 | 43 | } 44 | 45 | /** 46 | * 将当前MMSCRouter的实例更新为指定的新实例 47 | * 48 | * @param newInstance 49 | * 新的MMSCRouter实例 50 | */ 51 | public static void setInstance(MMSCRouter newInstance) { 52 | instance = newInstance; 53 | } 54 | 55 | private static Map deepCopy(Map m) { 56 | Map result = new HashMap(); 57 | for (String key : m.keySet()) { 58 | result.put(key, new MMSCInfo(m.get(key))); 59 | } 60 | return result; 61 | } 62 | 63 | public Map getRouteMap() { 64 | //做防御性拷贝 65 | return Collections.unmodifiableMap(deepCopy(routeMap)); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/vh/immutableobject/OMCAgent.java: -------------------------------------------------------------------------------- 1 | package vh.immutableobject; 2 | 3 | /** 4 | * 与运维中心(Operation and Maintenance Center)对接的类 5 | * 6 | */ 7 | public class OMCAgent extends Thread{ 8 | 9 | @Override 10 | public void run() { 11 | boolean isTableModificationMsg=false; 12 | String updatedTableName=null; 13 | while(true){ 14 | //省略其它代码 15 | /* 16 | * 从与OMC连接的Socket中读取消息并进行解析, 17 | * 解析到数据表更新消息后,重置MMSCRouter实例。 18 | */ 19 | if(isTableModificationMsg){ 20 | if("MMSCInfo".equals(updatedTableName)){ 21 | MMSCRouter.setInstance(new MMSCRouter()); 22 | } 23 | } 24 | //省略其它代码 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/vh/immutableobject/example/ImmutableLocation.java: -------------------------------------------------------------------------------- 1 | package vh.immutableobject.example; 2 | 3 | public final class ImmutableLocation { 4 | public final double x; 5 | public final double y; 6 | 7 | public ImmutableLocation(double x, double y) { 8 | this.x = x; 9 | this.y = y; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/vh/immutableobject/example/Location.java: -------------------------------------------------------------------------------- 1 | package vh.immutableobject.example; 2 | 3 | public class Location { 4 | 5 | private double x; 6 | private double y; 7 | 8 | public Location(double x, double y) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | 13 | public double getX() { 14 | return x; 15 | } 16 | 17 | public double getY() { 18 | return y; 19 | } 20 | 21 | public void setXY(double x, double y) { 22 | this.x = x; 23 | this.y = y; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/vh/immutableobject/example/VehicleTracker.java: -------------------------------------------------------------------------------- 1 | package vh.immutableobject.example; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | public class VehicleTracker { 7 | 8 | private Map locMap 9 | = new ConcurrentHashMap(); 10 | 11 | public void updateLocation(String vehicleId, Location newLocation) { 12 | locMap.put(vehicleId, newLocation); 13 | } 14 | 15 | } 16 | --------------------------------------------------------------------------------