extends AbstractList implements RandomAccess, Cloneable, java.io.Serializable
23 |
24 | @enduml
25 | ....
26 |
27 | `Vector` 内部实现与 `ArrayList` 类似,都是使用数组来存储元素。不同的是,`Vector` 在方法上加了 `synchronized` 修饰词,来实现线程安全。
28 |
--------------------------------------------------------------------------------
/docs/java.util.WeakHashMap.adoc:
--------------------------------------------------------------------------------
1 | [#util-WeakHashMap]
2 | = WeakHashMap
3 |
4 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.CompletableFuture.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-CompletableFuture]
2 | = CompletableFuture
3 |
4 | Java 中的 Promise。
5 |
6 | 问题:在一大堆任务中,如何获取第一个完成的返回值?
7 |
8 | [{java_src_attr}]
9 | ----
10 | include::{sourcedir}/concurrent/CompletableFutureTest.java[]
11 | ----
12 |
13 | `CompletableFuture` 实现了 `Future` 和 `CompletionStage` 两个接口。
14 |
15 | `CompletionStage` 接口声明了大量方法, `thenApply*` 接受 `Function` 对象,可以实现将任务的结果转化成另外一个对象,类似 Java Stream API 中的 `map` 操作; `thenAccept` 接受 `Consumer` 对象,见文知意,就是“消费”异步任务的结果值,类似 Java Stream API 的“终止操作”。
16 |
17 |
18 |
19 | == 参考资料
20 |
21 | . https://www.baeldung.com/java-completablefuture[Guide To CompletableFuture | Baeldung]
22 | . https://xie.infoq.cn/article/12fb1f7f825bb27795679ad13[Java 8 的异步利器:CompletableFuture源码解析^]
23 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.ConcurrentHashMap.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-ConcurrentHashMap]
2 | = `ConcurrentHashMap`
3 |
4 |
5 | image::images/ConcurrentHashMap-segment-lock.png[]
6 |
7 | image::images/JDK1.8-ConcurrentHashMap-Structure.jpg[]
8 |
9 | == 参考资料
10 |
11 | * http://note.youdao.com/share/?spm=5176.100239.blogcont36781.3.nHffVb&id=dde7a10b98aee57676408bc475ab0680&type=note#/[ConcurrentHashMap源码分析--Java8]
12 | * http://www.cnblogs.com/huaizuo/p/5413069.html[探索jdk8之ConcurrentHashMap 的实现机制 - 淮左 - 博客园] -- 参考资料非常棒,建议都看看!
13 | * http://blog.csdn.net/u010723709/article/details/48007881[ConcurrentHashMap源码分析(JDK8版本) - 惟愿无事 - 博客频道 - CSDN.NET]
14 | * https://www.cnblogs.com/chengxiao/p/6842045.html[ConcurrentHashMap实现原理及源码分析 - dreamcatcher-cx - 博客园]
15 | * https://www.jianshu.com/p/d10256f0ebea[ConcurrentHashMap 原理解析(JDK1.8) - 简书]
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.ConcurrentLinkedQueue.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-ConcurrentLinkedQueue]
2 | = ConcurrentLinkedQueue
3 |
4 | `ConcurrentLinkedQueue` 主要使用 CAS 非阻塞算法来实现线程安全。这是一个非阻塞队列。对比来看,`LinkedBlockingQueue` 是一个可以阻塞的队列。
5 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.ConcurrentSkipListMap.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-ConcurrentSkipListMap]
2 | = `ConcurrentSkipListMap`
3 |
4 | 没想到 JDK 中,居然有跳跃表的实现!
5 |
6 | image::images/ConcurrentSkipListMap-search.jpg[]
7 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.CyclicBarrier.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-CyclicBarrier]
2 | = CyclicBarrier
3 |
4 | [{java_src_attr}]
5 | ----
6 | include::{sourcedir}/concurrent/CyclicBarrierTest.java[]
7 | ----
8 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.DelayQueue.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-DelayQueue]
2 | = DelayQueue
3 |
4 | `DelayQueue` 对元素进行持有直到一个特定的延迟到期。注入其中的元素必须实现 `java.util.concurrent.Delayed` 接口,该接口定义:
5 |
6 | [{java_src_attr}]
7 | ----
8 | package java.util.concurrent;
9 |
10 | /**
11 | * A mix-in style interface for marking objects that should be
12 | * acted upon after a given delay.
13 | *
14 | * An implementation of this interface must define a
15 | * {@code compareTo} method that provides an ordering consistent with
16 | * its {@code getDelay} method.
17 | *
18 | * @since 1.5
19 | * @author Doug Lea
20 | */
21 | public interface Delayed extends Comparable {
22 |
23 | /**
24 | * Returns the remaining delay associated with this object, in the
25 | * given time unit.
26 | *
27 | * @param unit the time unit
28 | * @return the remaining delay; zero or negative values indicate
29 | * that the delay has already elapsed
30 | */
31 | long getDelay(TimeUnit unit);
32 | }
33 | ----
34 |
35 | 加入延迟队列的元素都必须实现 `Delayed` 接口。延迟队列内部是利用 `PriorityQueue` 实现的,所以还是利用优先队列!`Delayed` 接口继承了 `Comparable`。因此优先队列是通过 `delay` 来排序的。
36 |
37 | 示例如下:
38 |
39 | [{java_src_attr}]
40 | ----
41 | include::{sourcedir}/concurrent/DelayQueueTest.java[]
42 | ----
43 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.Exchanger.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-Exchanger]
2 | = Exchanger
3 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.Flow.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-Flow]
2 | = `Flow`
3 |
4 |
5 | 早在 2013年,一些知名的有影响力的网络公司提出 http://www.reactive-streams.org/[Reactive Streams^] 提案,旨在标准版软件组件之间的异步数据交换。
6 |
7 | 为了减少重复和不兼容性,Java 9 引入了 `java.util.concurrent.Flow` 类,统一并规范了 Reactive Streams 的接口。但是, `Flow` 只定义了接口,并没有给出具体实现。下面是一个简单实现:
8 |
9 | [{java_src_attr}]
10 | ----
11 | include::{sourcedir}/concurrent/FlowTest.java[]
12 | ----
13 |
14 |
15 |
16 | == 参考资料
17 |
18 | . https://dzone.com/articles/reactive-streams-in-java-9[Reactive Streams in Java 9^]
19 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.ForkJoinPool.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-ForkJoinPool]
2 | = ForkJoinPool
3 |
4 | image::images/ForkJoinPool-fork-join.webp[]
5 |
6 | image::images/ForkJoinPool-work-stealing.webp[]
7 |
8 | image::images/ForkJoinPool-invoke-link.png[]
9 |
10 | image::images/ForkJoinPool-data-structures.png[]
11 |
12 | image::images/ForkJoinPool-ctl.png[]
13 |
14 | [{java_src_attr}]
15 | ----
16 | include::{sourcedir}/concurrent/ForkJoinPoolTest.java[]
17 | ----
18 |
19 |
20 |
21 |
22 | == 参考资料
23 |
24 | . https://blog.csdn.net/u010841296/article/details/83963637[ForkJoinPool实现原理和源码解析_Java_Java程序员的进阶之路-CSDN博客]
25 | . https://segmentfault.com/a/1190000016781127[Java多线程进阶(四三)—— J.U.C之executors框架:Fork/Join框架(1) 原理 - 透彻理解Java并发编程 - SegmentFault 思否]
26 | . https://segmentfault.com/a/1190000016877931[Java多线程进阶(四四)—— J.U.C之executors框架:Fork/Join框架(2)实现 - 透彻理解Java并发编程 - SegmentFault 思否]
27 | . https://liuyehcf.github.io/2017/08/01/Java-concurrent-Fork-Join-%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90/[Java-concurrent-Fork-Join-源码剖析 | Liuye Blog]
28 | . https://www.jianshu.com/p/32a15ef2f1bf[JUC源码分析-线程池篇(四):ForkJoinPool - 1 - 简书]
29 | . https://www.jianshu.com/p/6a14d0b54b8d[JUC源码分析-线程池篇(五):ForkJoinPool - 2 - 简书]
30 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.ForkJoinTask.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-ForkJoinTask]
2 | = ForkJoinTask
3 |
4 | == 类图
5 |
6 | 先来看一下 `ForkJoinTask` 的类图:
7 |
8 | [plantuml,{diagram_attr}]
9 | ....
10 | @startuml
11 | skinparam nodesep 100
12 | 'skinparam ranksep 60
13 |
14 | title ForkJoinTask
15 |
16 | interface Future
17 |
18 | abstract class ForkJoinTask implements Future, Serializable
19 |
20 | @enduml
21 | ....
22 |
23 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.Future.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-Future]
2 | = JUC 包基础类分析
3 |
4 | JUC 包中有一些提交比较小的类,这类类单列出来重量太小,不够篇幅。
5 |
6 |
7 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.LinkedBlockingQueue.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-LinkedBlockingQueue]
2 | = LinkedBlockingQueue
3 |
4 | `LinkedBlockingQueue` 底层是一个单向链表结构。如果需要的话,这一链式结构可以选择一个上限。如果没有定义上限,将使用 `Integer.MAX_VALUE` 作为上限。
5 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.Phaser.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-Phaser]
2 | = Phaser
3 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.PriorityBlockingQueue.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-PriorityBlockingQueue]
2 | = PriorityBlockingQueue
3 |
4 | `PriorityBlockingQueue` 是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 `compareTo()` 方法来指定元素排序规则,或者初始化时通过构造器参数 `Comparator` 来指定排序规则。
5 |
6 | `PriorityBlockingQueue` 并发控制采用的是 `ReentrantLock`,队列为无界队列(`ArrayBlockingQueue` 是有界队列,`LinkedBlockingQueue` 也可以通过在构造函数中传入 `capacity` 指定队列最大的容量,但是 `PriorityBlockingQueue` 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会自动扩容)。
7 |
8 |
9 | == 参考资料
10 |
11 | . https://www.javadoop.com/post/java-concurrent-queue[解读 java 并发队列 BlockingQueue_Javadoop]
12 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.ScheduledThreadPoolExecutor.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-ScheduledThreadPoolExecutor]
2 | = `ScheduledThreadPoolExecutor`
3 |
4 | `Timer`、`ScheduledThreadPool` 和 `DelayQueue`,总结的说下它们都是通过优先队列来获取最早需要执行的任务,因此插入和删除任务的时间复杂度都为 O(logn),并且 `Timer` 、`ScheduledThreadPool` 的周期性任务是通过重置任务的下一次执行时间来完成的。
5 |
6 | 问题就出在时间复杂度上,插入删除时间复杂度是O(logn),那么假设频繁插入删除次数为 `m`,总的时间复杂度就是 O(mlogn),这种时间复杂度满足不了 Kafka 这类中间件对性能的要求,而时间轮算法的插入删除时间复杂度是 O(1)。我们来看看时间轮算法是如何实现的。
7 |
8 |
9 | == 参考资料
10 |
11 | . https://mp.weixin.qq.com/s/xBB72hJGn8geZ7SkM0FqJw[面试官:知道时间轮算法吗?在Netty和Kafka中如何应用的?^]
12 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.Semaphore.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-Semaphore]
2 | = Semaphore
3 |
4 | 信号量
5 |
6 | [{java_src_attr}]
7 | ----
8 | include::{sourcedir}/concurrent/SemaphoreTest.java[]
9 | ----
10 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-overview]
2 | = 并发库概述
3 |
4 | image::images/java-concurrent-overview.png[]
5 |
6 | == Happy-Before 原则
7 |
8 | . Single Thread Rule 单一线程原则
9 | . Monitor Lock Rule 管程锁定规则
10 | . Volatile Variable Rule `volatile` 变量规则
11 | . Thread Start Rule 线程启动规则
12 | . Thread Join Rule 线程加入规则
13 | . Thread Interruption Rule 线程中断规则
14 | . Finalizer Rule 对象终结规则
15 | . Transitivity 传递性
16 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.atomic.AtomicInteger.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-atomic-AtomicInteger]
2 | = AtomicInteger
3 |
4 | `AtomicInteger` 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 `synchronized` 的高开销,执行效率大为提升。
5 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.atomic.LongAdder.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-atomic-LongAdder]
2 | = LongAdder
3 |
4 | [{java_src_attr}]
5 | ----
6 | include::{sourcedir}/concurrent/LongAdderTest.java[]
7 | ----
8 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.locks.LockSupport.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-locks-LockSupport]
2 | = LockSupport
3 |
4 | [{java_src_attr}]
5 | ----
6 | include::{sourcedir}/concurrent/LockSupportTest.java[]
7 | ----
8 |
9 | `synchronized` 关键字在方法上使用时,在方法修饰符上增加了一个标志位 `flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED`。而用在代码块时,则是生成了 `monitorenter` 和 `monitorexit` 指令。
10 |
11 | [{java_src_attr}]
12 | ----
13 | include::{sourcedir}/concurrent/SynchronizedTest.java[]
14 | ----
15 |
16 | 可以利用 jclasslib Bytecode viewer 工具,或者 `javap -c -v XXX.class` 来查看。
17 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.locks.ReentrantLock.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-locks-ReentrantLock]
2 | = ReentrantLock
3 |
4 | `ReentrantLock` 是重入锁,也是排他锁。
5 |
6 | == 谈谈 synchronized 和 ReentrantLock 的区别
7 |
8 | . 两者都是可重入锁
9 | +
10 | 两者都是可重入锁。“可重入锁”概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。
11 | +
12 | . synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API
13 | +
14 | synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。
15 | +
16 | . ReentrantLock 比 synchronized 增加了一些高级功能
17 | +
18 | 相比synchronized,ReentrantLock增加了一些高级功能。主要来说主要有三点:**①等待可中断;②可实现公平锁;③可实现选择性通知(锁可以绑定多个条件)**
19 | +
20 | .. **ReentrantLock提供了一种能够中断等待锁的线程的机制**,通过lock.lockInterruptibly()来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
21 | .. *ReentrantLock可以指定是公平锁还是非公平锁。**而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。 ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
22 | .. synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),**线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用notify()/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知”**,这个功能非常重要,而且是Condition接口默认提供的。而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。
23 | +
24 | 如果你想使用上述功能,那么选择ReentrantLock是一个不错的选择。
25 | +
26 | . 性能已不是选择标准
27 |
28 | [{java_src_attr}]
29 | ----
30 | include::{sourcedir}/concurrent/ReentrantLockTest.java[]
31 | ----
32 |
33 |
34 | [{java_src_attr}]
35 | ----
36 | /**
37 | * Acquires in exclusive mode, aborting if interrupted.
38 | * Implemented by first checking interrupt status, then invoking
39 | * at least once {@link #tryAcquire}, returning on
40 | * success. Otherwise the thread is queued, possibly repeatedly
41 | * blocking and unblocking, invoking {@link #tryAcquire}
42 | * until success or the thread is interrupted. This method can be
43 | * used to implement method {@link Lock#lockInterruptibly}.
44 | *
45 | * @param arg the acquire argument. This value is conveyed to
46 | * {@link #tryAcquire} but is otherwise uninterpreted and
47 | * can represent anything you like.
48 | * @throws InterruptedException if the current thread is interrupted
49 | */
50 | public final void acquireInterruptibly(int arg)
51 | throws InterruptedException {
52 | if (Thread.interrupted())
53 | throw new InterruptedException();
54 | if (!tryAcquire(arg))
55 | doAcquireInterruptibly(arg);
56 | }
57 | ----
58 |
59 | 先判断是否有中断,有则响应。从这里就可以看出,在加锁之前也可以被中断。
60 |
61 |
62 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.locks.ReentrantReadWriteLock.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-locks-ReentrantReadWriteLock]
2 | = ReentrantReadWriteLock
3 |
4 | [{java_src_attr}]
5 | ----
6 | include::{sourcedir}/concurrent/ReentrantReadWriteLockTest.java[]
7 | ----
8 |
--------------------------------------------------------------------------------
/docs/java.util.concurrent.locks.StampedLock.adoc:
--------------------------------------------------------------------------------
1 | [#concurrent-locks-StampedLock]
2 | = StampedLock
3 |
4 | [{java_src_attr}]
5 | ----
6 | include::{sourcedir}/concurrent/StampedLockTest.java[]
7 | ----
8 |
--------------------------------------------------------------------------------
/docs/java.util.regex.Pattern.adoc:
--------------------------------------------------------------------------------
1 | [#regex-Pattern]
2 | = `Pattern`
3 |
4 | [{java_src_attr}]
5 | ----
6 | include::{sourcedir}/regex/PatternTest.java[]
7 | ----
8 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ArrayListAddTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2019-12-12 18:48
10 | */
11 | public class ArrayListAddTest extends ArrayListBaseTest {
12 | // tag::testAddAtTail[]
13 | @Test
14 | public void testAddAtTail() {
15 | int initialCapacity = 8;
16 | ArrayList integers = new ArrayList<>(initialCapacity);
17 | for (int i = 0; i < initialCapacity * 2; i++) {
18 | xray(integers);
19 | integers.add(i);
20 | }
21 | }
22 | // end::testAddAtTail[]
23 |
24 | // tag::testAddAtHeader[]
25 | @Test
26 | public void testAddAtHeader() {
27 | int initialCapacity = 8;
28 | ArrayList integers = new ArrayList<>(initialCapacity);
29 | for (int i = 0; i < initialCapacity * 2; i++) {
30 | xray(integers);
31 | integers.add(0, i);
32 | }
33 | }
34 | // end::testAddAtHeader[]
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ArrayListBaseTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.ArrayList;
5 | import java.util.Objects;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-03 11:10
10 | */
11 | public class ArrayListBaseTest {
12 | /**
13 | * 通过反射查看 {@link ArrayList} 的内部属性
14 | */
15 | public void xray(ArrayList list) {
16 | Class clazz = list.getClass();
17 | try {
18 | Field elementData = clazz.getDeclaredField("elementData");
19 | elementData.setAccessible(true);
20 | Object[] objects = (Object[]) elementData.get(list);
21 | Field sizeField = clazz.getDeclaredField("size");
22 | sizeField.setAccessible(true);
23 |
24 | int size = 0;
25 | for (int i = 0; i < objects.length; i++) {
26 | if (Objects.nonNull(objects[i])) {
27 | ++size;
28 | }
29 | }
30 | System.out.println("length = " + objects.length
31 | + ", size = " + sizeField.get(list)
32 | + ", arraySize = " + size);
33 | } catch (Throwable e) {
34 | e.printStackTrace();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ArrayListIteratorSpeedTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.openjdk.jmh.annotations.*;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Iterator;
7 |
8 | /**
9 | * @author D瓜哥, https://www.diguage.com/
10 | * @since 2020-03-03 13:09
11 | */
12 | @BenchmarkMode(Mode.Throughput)
13 | @Warmup(iterations = 3)
14 | @State(Scope.Benchmark)
15 | @Threads(8)
16 | public class ArrayListIteratorSpeedTest {
17 |
18 | private ArrayList arrayList = null;
19 |
20 | @Setup(Level.Iteration)
21 | public void setup() {
22 | int capacity = 1_000_000;
23 | arrayList = new ArrayList<>(capacity);
24 | for (int i = 0; i < capacity; i++) {
25 | arrayList.add(i);
26 | }
27 | }
28 |
29 | @Benchmark
30 | public void testIterator() {
31 | Integer iteratorValue = null;
32 | Iterator iterator = arrayList.iterator();
33 | while (iterator.hasNext()) {
34 | iteratorValue = iterator.next();
35 | }
36 | }
37 |
38 | @Benchmark
39 | public void testRandomAccess() {
40 | Integer randomAccessValue = null;
41 | int size = arrayList.size();
42 | for (int i = 0; i < size; i++) {
43 | randomAccessValue = arrayList.get(i);
44 | }
45 | }
46 |
47 | @Benchmark
48 | public void testForEachLambda() {
49 | arrayList.forEach(this::devnull);
50 | }
51 |
52 | public void devnull(Integer value) {
53 | Integer forEachLambdaValue = value;
54 | }
55 |
56 | @Benchmark
57 | public void testForEach() {
58 | Integer forEachValue = null;
59 | for (Integer integer : arrayList) {
60 | forEachValue = integer;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ArrayListRemoveTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-03 11:35
10 | */
11 | public class ArrayListRemoveTest extends ArrayListBaseTest {
12 | @Test
13 | public void testRemoveTail() {
14 | int initialCapacity = 8;
15 | ArrayList integers = new ArrayList<>(initialCapacity);
16 | for (int i = 0; i < initialCapacity * 2; i++) {
17 | xray(integers);
18 | integers.add(i);
19 | }
20 |
21 | for (int i = initialCapacity * 2 - 1; i >= 0; i--) {
22 | xray(integers);
23 | integers.remove(i);
24 | }
25 | }
26 |
27 | @Test
28 | public void testRemoveHeader() {
29 | int initialCapacity = 8;
30 | ArrayList integers = new ArrayList<>(initialCapacity);
31 | for (int i = 0; i < initialCapacity * 2; i++) {
32 | integers.add(i);
33 | }
34 |
35 | for (int i = 0; i < initialCapacity * 2; i++) {
36 | xray(integers);
37 | integers.remove(i);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ArrayTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import java.util.Arrays;
4 | import java.util.Objects;
5 |
6 | public class ArrayTest {
7 | public static void main(String[] args) {
8 | int[] i1 = new int[0];
9 | int[] i2 = new int[0];
10 | System.out.println(i1.hashCode());
11 | System.out.println(i2.hashCode());
12 | System.out.println(Objects.equals(i1, i2));
13 | System.out.println(Arrays.equals(i1, i2));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/BloomFilterTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import com.google.common.hash.BloomFilter;
4 | import com.google.common.hash.Funnels;
5 | import org.junit.jupiter.api.Test;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-10 14:38
10 | */
11 | public class BloomFilterTest {
12 | @Test
13 | public void test() {
14 | BloomFilter filter = BloomFilter.create(Funnels.integerFunnel(), 1500, 0.01);
15 | System.out.println(filter.mightContain(1));
16 | System.out.println(filter.mightContain(2));
17 |
18 | filter.put(1);
19 | filter.put(2);
20 |
21 | System.out.println(filter.mightContain(1));
22 | System.out.println(filter.mightContain(2));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/CollectionTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.Collection;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2019-12-12 18:40
10 | */
11 | public abstract class CollectionTest {
12 | public abstract Collection newInstance();
13 |
14 | public abstract int initLength();
15 |
16 | public abstract int totalLength();
17 |
18 | @Test
19 | public void add() {
20 | Collection integers = newInstance();
21 | int start = 1;
22 | for (int i = 0; i < initLength(); i++) {
23 | integers.add(start++);
24 | }
25 |
26 | for (int i = start - 1; i < totalLength(); i++) {
27 | integers.add(start++);
28 | }
29 | }
30 |
31 | @Test
32 | public void remove() {
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ComparatorTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 | import java.util.stream.Collectors;
8 |
9 | public class ComparatorTest {
10 | public static class User {
11 | public User(String name, int age, int sex) {
12 | this.name = name;
13 | this.age = age;
14 | this.sex = sex;
15 | }
16 |
17 | String name;
18 | int age;
19 |
20 | int sex;
21 |
22 | @Override
23 | public String toString() {
24 | return "User{" +
25 | "name='" + name + '\'' +
26 | ", age=" + age +
27 | ", sex=" + sex +
28 | '}';
29 | }
30 | }
31 |
32 | @Test
33 | public void test() {
34 | List users = Arrays.asList(
35 | new User("u4", 4, 1),
36 | new User("u2", 2, 1),
37 | new User("u1", 1, 0),
38 | new User("u3", 2, 0));
39 | List result = users.stream()
40 | .sorted((a, b) -> {
41 | if (0 == a.sex) {
42 | return -1;
43 | }
44 | if (0 == b.sex) {
45 | return 1;
46 | }
47 | return 0;
48 | })
49 | .collect(Collectors.toList());
50 | System.out.println(Arrays.toString(result.toArray()));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ConcurrentSkipListMapTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.ConcurrentSkipListMap;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-08 16:43
10 | */
11 | public class ConcurrentSkipListMapTest {
12 | @Test
13 | public void test() {
14 | ConcurrentSkipListMap map = new ConcurrentSkipListMap<>();
15 | for (int i = 0; i < 10; i++) {
16 | map.put(i, i);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/EmptyStringEquals.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.openjdk.jmh.annotations.*;
4 |
5 | import java.util.concurrent.TimeUnit;
6 |
7 | /**
8 | * [Micro optimizations in Java. String.equals() | Medium^]
9 | *
10 | * https://medium.com/javarevisited/micro-optimizations-in-java-string-equals-22be19fd8416
11 | *
12 | * @author D瓜哥, https://www.diguage.com/
13 | * @since 2020-08-10 17:16
14 | */
15 | @BenchmarkMode(Mode.AverageTime)
16 | @Fork(1)
17 | @State(Scope.Thread)
18 | @Warmup(iterations = 5, time = 1)
19 | @OutputTimeUnit(TimeUnit.NANOSECONDS)
20 | @Measurement(iterations = 10, time = 1)
21 | public class EmptyStringEquals {
22 | @Param({"", "nonEmptyString"})
23 | private String strParams;
24 |
25 | @Benchmark
26 | public boolean nonNullAndIsEmpty() {
27 | return strParams != null && strParams.isEmpty();
28 | }
29 |
30 | @Benchmark
31 | public boolean equalsPost() {
32 | return strParams != null && strParams.equals("");
33 | }
34 |
35 | @Benchmark
36 | public boolean preEquals() {
37 | return "".equals(strParams);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/HashMapTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import static org.assertj.core.api.Assertions.assertThat;
9 |
10 |
11 | public class HashMapTest {
12 | @Test
13 | public void test() {
14 | Map map = new HashMap<>();
15 | map.put("123", null);
16 | assertThat(map.getOrDefault("123", "456")).isNull();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/LinkedListBaseTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.LinkedList;
5 | import java.util.Objects;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-03 16:16
10 | */
11 | public class LinkedListBaseTest {
12 | /**
13 | * 使用反射读取 LinkedList 内部属性
14 | */
15 | public void xray(LinkedList> list) {
16 | Class extends LinkedList> clazz = list.getClass();
17 | try {
18 | Field nodeField = clazz.getDeclaredField("first");
19 | nodeField.setAccessible(true);
20 | Object node = nodeField.get(list);
21 | System.out.println("length=" + length(node) + ", size=" + list.size());
22 | } catch (Throwable e) {
23 | e.printStackTrace();
24 | }
25 | }
26 |
27 | public int length(Object node) {
28 | int result = 0;
29 | if (Objects.isNull(node)) {
30 | return result;
31 | }
32 | try {
33 | Class> nodeClass = node.getClass();
34 | Field nextField = nodeClass.getDeclaredField("next");
35 | nextField.setAccessible(true);
36 | while (Objects.nonNull(node)) {
37 | node = nextField.get(node);
38 | result++;
39 | }
40 | } catch (Throwable e) {
41 | e.printStackTrace();
42 | }
43 | return result;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/LinkedListTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.LinkedList;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-03 15:53
10 | */
11 | public class LinkedListTest extends LinkedListBaseTest {
12 |
13 | // tag::testAddAtTail[]
14 | @Test
15 | public void testAddAtTail() {
16 | LinkedList list = new LinkedList<>();
17 | for (int i = 0; i < 16; i++) {
18 | xray(list);
19 | list.add(i);
20 | }
21 | }
22 | // end::testAddAtTail[]
23 |
24 | // tag::testAddAtHeader[]
25 | @Test
26 | public void testAddAtHeader() {
27 | LinkedList list = new LinkedList<>();
28 | for (int i = 0; i < 16; i++) {
29 | xray(list);
30 | list.addFirst(i);
31 | }
32 | }
33 | // end::testAddAtHeader[]
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ListSortTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.*;
6 |
7 | public class ListSortTest {
8 | @Test
9 | public void testSort() {
10 | List list = new ArrayList<>(Arrays.asList(new Date(1000), new Date(2000), new Date(3000)));
11 | list.sort(Comparator.comparingInt(Date::getSeconds));
12 | list.forEach(d -> System.out.println(d.getSeconds()));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/LongTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | public class LongTest {
6 | @Test
7 | public void test() {
8 | System.out.println(Long.toHexString(Long.MAX_VALUE));
9 | System.out.println(Long.toHexString(System.nanoTime()));
10 | System.out.println(Long.toHexString(System.currentTimeMillis()));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/OuterClass.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import java.io.*;
4 | import java.util.StringJoiner;
5 |
6 | public class OuterClass implements Serializable {
7 | private int age = 119;
8 | private String name = "D瓜哥";
9 |
10 | public int getAge() {
11 | return age;
12 | }
13 |
14 | public void setAge(int age) {
15 | this.age = age;
16 | }
17 |
18 | public String getName() {
19 | return name;
20 | }
21 |
22 | public void setName(String name) {
23 | this.name = name;
24 | }
25 |
26 | @Override
27 | public String toString() {
28 | return new StringJoiner(", ", OuterClass.class.getSimpleName() + "[", "]")
29 | .add("age=" + age)
30 | .add("name='" + name + "'")
31 | .toString();
32 | }
33 |
34 | public static class InnerClass {
35 | private int iage = 120;
36 | private String iname = "https://www.diguage.com";
37 |
38 | public int getIage() {
39 | return iage;
40 | }
41 |
42 | public void setIage(int iage) {
43 | this.iage = iage;
44 | }
45 |
46 | public String getIname() {
47 | return iname;
48 | }
49 |
50 | public void setIname(String iname) {
51 | this.iname = iname;
52 | }
53 |
54 | @Override
55 | public String toString() {
56 | return new StringJoiner(", ", InnerClass.class.getSimpleName() + "[", "]")
57 | .add("iage=" + iage)
58 | .add("iname='" + iname + "'")
59 | .toString();
60 | }
61 |
62 | public static void main(String[] args) throws Throwable {
63 | test(new OuterClass());
64 | test(new InnerClass());
65 | }
66 |
67 | private static void test(Object param) throws Exception {
68 | System.out.println("param = " + param);
69 | ByteArrayOutputStream baos = new ByteArrayOutputStream(10240);
70 | ObjectOutputStream oos = new ObjectOutputStream(baos);
71 | oos.writeObject(param);
72 |
73 | ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
74 | ObjectInputStream ois = new ObjectInputStream(bais);
75 | Object object = ois.readObject();
76 | System.out.println("deser = " + object);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/PriorityQueueTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.PriorityQueue;
6 |
7 | import static org.assertj.core.api.Assertions.assertThat;
8 |
9 |
10 | /**
11 | * @author D瓜哥, https://www.diguage.com/
12 | * @since 2020-04-22 20:48
13 | */
14 | public class PriorityQueueTest {
15 | // tag::size[]
16 | @Test
17 | public void testSize() {
18 | PriorityQueue queue = new PriorityQueue<>(5);
19 | for (int i = 0; i < 10; i++) {
20 | queue.add(i);
21 | }
22 | assertThat(queue).hasSize(10);
23 | }
24 |
25 | @Test
26 | public void test() {
27 | int capacity = 5;
28 | PriorityQueue queue = new PriorityQueue<>(capacity);
29 | for (int i = 0; i < 10; i++) {
30 | queue.add(i);
31 | if (queue.size() > capacity) {
32 | Integer num = queue.poll();
33 | System.out.println(num);
34 | }
35 | }
36 | assertThat(queue).hasSize(capacity);
37 | assertThat(queue).contains(9);
38 | assertThat(queue).doesNotContain(0);
39 | Integer num = queue.poll();
40 | // 可见,默认就是最小堆。
41 | assertThat(num).isEqualTo(5);
42 | }
43 | // end::size[]
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ServiceLoaderSay.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | /**
4 | * @author D瓜哥, https://www.diguage.com/
5 | * @since 2020-04-08 10:47
6 | */
7 | public interface ServiceLoaderSay {
8 | void say();
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ServiceLoaderSayGoodbye.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | /**
4 | * @author D瓜哥, https://www.diguage.com/
5 | * @since 2020-04-08 10:48
6 | */
7 | public class ServiceLoaderSayGoodbye implements ServiceLoaderSay {
8 | @Override
9 | public void say() {
10 | System.out.println("Goodbye, https://www.diguage.com/");
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ServiceLoaderSayHello.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | /**
4 | * @author D瓜哥, https://www.diguage.com/
5 | * @since 2020-04-08 10:48
6 | */
7 | public class ServiceLoaderSayHello implements ServiceLoaderSay {
8 | @Override
9 | public void say() {
10 | System.out.println("Hello, https://www.diguage.com/");
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/ServiceLoaderTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.Iterator;
6 | import java.util.ServiceLoader;
7 |
8 | /**
9 | * @author D瓜哥, https://www.diguage.com/
10 | * @since 2020-04-08 10:40
11 | */
12 | public class ServiceLoaderTest {
13 | @Test
14 | public void test() {
15 | ServiceLoader loader
16 | = ServiceLoader.load(ServiceLoaderSay.class);
17 | Iterator iterator = loader.iterator();
18 | while (iterator.hasNext()) {
19 | ServiceLoaderSay say = iterator.next();
20 | say.say();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/StringTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.*;
6 | import java.util.concurrent.TimeUnit;
7 | import java.util.concurrent.locks.LockSupport;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
12 |
13 | public class StringTest {
14 |
15 |
16 | @Test
17 | public void testDedup() {
18 | List lists = new ArrayList<>(1);
19 | for (int i = 0; i < Integer.MAX_VALUE; i++) {
20 | String is = String.valueOf(i);
21 | String s1 = "D瓜哥 · https://www.digauge.com".repeat(i % 10) + is;
22 | lists.add(new String(s1.substring(0, s1.length() - is.length())));
23 | String s2 = i + "D瓜哥 · https://www.digauge.com";
24 | lists.add(new String(s2.substring(is.length())));
25 | System.out.println(lists.size());
26 | if (i % 1000 == 0) {
27 | LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2L));
28 | }
29 | }
30 | }
31 |
32 | @Test
33 | public void testSplit() {
34 | String s = "abc";
35 | System.out.println(Arrays.toString(s.split("\\|")));
36 |
37 |
38 | // 调用 split 方法,如果参数为 null 时,则抛异常
39 | assertThatThrownBy(() -> s.split(null)).isInstanceOf(NullPointerException.class);
40 | }
41 |
42 | @Test
43 | public void testReplaceAll() {
44 | String value = "${abc} 是一个占位符,${abc}";
45 | Set placeholders = getAllPlaceholders(value);
46 |
47 | for (String placeholder : placeholders) {
48 | System.out.println(value.replaceAll(placeholder, "ABC"));
49 | }
50 | }
51 |
52 | /**
53 | * 占位符正则表达式:${\w*}
54 | */
55 | private static final Pattern PH_PATTERN = Pattern.compile("(\\u0024\\{\\w*\\})+");
56 |
57 | private static Set getAllPlaceholders(String value) {
58 | Matcher matcher = PH_PATTERN.matcher(value);
59 | Set placeholders = new HashSet<>();
60 | int matcherStart = 0;
61 | while (matcher.find(matcherStart)) {
62 | String group = matcher.group();
63 | placeholders.add(group);
64 | matcherStart = matcher.end();
65 | }
66 | System.out.println(Arrays.toString(placeholders.toArray(new String[0])));
67 | return placeholders;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/StringUtilTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | import org.openjdk.jmh.annotations.*;
4 |
5 | import java.util.concurrent.TimeUnit;
6 |
7 | import static com.diguage.truman.StringUtils.format;
8 | import static com.diguage.truman.StringUtils.switchFormat;
9 |
10 | @BenchmarkMode(Mode.Throughput)
11 | @Measurement(iterations = 10, time = 30)
12 | @OutputTimeUnit(TimeUnit.SECONDS)
13 | @Warmup(iterations = 3, time = 5)
14 | @State(Scope.Thread)
15 | public class StringUtilTest {
16 |
17 | // TODO 怎么书写测试用例?
18 | @Param({"1", "2"})
19 | int num;
20 |
21 | private static final int[] lens = {1, 2, 3, 4, 5, 6, 7};
22 |
23 | @Benchmark
24 | public String testStringFormat() {
25 | return format(num, num);
26 | }
27 |
28 | @Benchmark
29 | public String testSwitchFormat() {
30 | return switchFormat(num, num);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/StringUtils.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman;
2 |
3 | public class StringUtils {
4 | public static String switchFormat(int cur, int length) {
5 | String str = "" + cur;
6 | int q = length - str.length();
7 | switch (q) {
8 | case 0:
9 | break;
10 | case 1:
11 | str = "0" + str;
12 | break;
13 | case 2:
14 | str = "00" + str;
15 | break;
16 | case 3:
17 | str = "000" + str;
18 | break;
19 | case 4:
20 | str = "0000" + str;
21 | break;
22 | case 5:
23 | str = "00000" + str;
24 | break;
25 | case 6:
26 | str = "000000" + str;
27 | break;
28 | default:
29 | break;
30 | }
31 | return str;
32 | }
33 |
34 | public static String format(int cur, int len) {
35 | return String.format("%0" + len + "d", cur);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/ArrayBlockingQueueTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.ArrayBlockingQueue;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.Executors;
8 | import java.util.concurrent.TimeUnit;
9 | import java.util.concurrent.locks.LockSupport;
10 |
11 | /**
12 | * @author D瓜哥, https://www.diguage.com/
13 | * @since 2020-04-22 15:11
14 | */
15 | public class ArrayBlockingQueueTest {
16 |
17 | @Test
18 | public void testTimeoutPoll() {
19 | ExecutorService executorService = Executors.newFixedThreadPool(5);
20 | ArrayBlockingQueue queue = new ArrayBlockingQueue(5);
21 | executorService.execute(() -> {
22 | for (long i = 0; i < 5; i++) {
23 | queue.add(i);
24 | try {
25 | Thread.sleep(2 * 60 * 1000);
26 | } catch (InterruptedException e) {
27 | e.printStackTrace();
28 | }
29 | }
30 | });
31 | for (int i = 0; i < 10; i++) {
32 | final long time = i;
33 | executorService.execute(() -> {
34 | try {
35 | Long num = queue.poll(time, TimeUnit.MINUTES);
36 | System.out.println("poll:" + num);
37 | } catch (InterruptedException e) {
38 | e.printStackTrace();
39 | }
40 | });
41 | }
42 | LockSupport.parkNanos(TimeUnit.MINUTES.toNanos(10));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/CallableTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.Callable;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-04-30 11:24
10 | */
11 | public class CallableTest {
12 | @Test
13 | public void test() {
14 | Callable task = () -> {
15 | return null;
16 | };
17 |
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/CompletableFutureTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.concurrent.*;
8 |
9 | /**
10 | * @author D瓜哥, https://www.diguage.com/
11 | * @since 2020-03-19 16:04
12 | */
13 | public class CompletableFutureTest {
14 | @Test
15 | public void test() throws ExecutionException, InterruptedException {
16 | ExecutorService executorService = Executors.newFixedThreadPool(5);
17 | FutureTask futureTask = new FutureTask<>(new Task());
18 |
19 | executorService.submit(futureTask);
20 | System.out.println("FutureTask...");
21 | System.out.println(futureTask.get());
22 | System.out.println("FutureTask done.");
23 |
24 | List> futures = new ArrayList<>();
25 | for (int i = 0; i < 20; i++) {
26 | futures.add(executorService.submit(new Task()));
27 | }
28 | executorService.shutdown();
29 | while (!executorService.isTerminated()) {
30 | }
31 |
32 | for (Future future : futures) {
33 | if (future.isDone()) {
34 | System.out.println(future.get());
35 | }
36 | }
37 | CompletableFuture.runAsync(() -> System.out.println(""));
38 | System.out.println("All tasks were done.");
39 | }
40 |
41 | public static class Task implements Callable {
42 | @Override
43 | public Integer call() throws Exception {
44 | int second = 0;
45 | try {
46 | ThreadLocalRandom random = ThreadLocalRandom.current();
47 | second = random.nextInt(10000);
48 | Thread.sleep(second);
49 | } catch (InterruptedException e) {
50 | e.printStackTrace();
51 | }
52 | return second;
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/ConcurrentMapTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.ConcurrentHashMap;
6 | import java.util.concurrent.ConcurrentMap;
7 | import java.util.concurrent.atomic.AtomicInteger;
8 |
9 | public class ConcurrentMapTest {
10 | @Test
11 | public void test() {
12 | ConcurrentMap map = new ConcurrentHashMap<>();
13 | // AtomicInteger cnt = map.putIfAbsent("abc", new AtomicInteger(10));
14 | AtomicInteger cnt = map.computeIfAbsent("abc", (k) -> new AtomicInteger(10));
15 | System.out.println(cnt);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/CountDownLatchTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.time.LocalDateTime;
6 | import java.util.Random;
7 | import java.util.concurrent.CountDownLatch;
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 | import java.util.concurrent.TimeUnit;
11 | import java.util.concurrent.locks.LockSupport;
12 |
13 | /**
14 | * @author D瓜哥, https://www.diguage.com/
15 | * @since 2020-03-16 17:23
16 | */
17 | public class CountDownLatchTest {
18 | private int count = 2;
19 |
20 | @Test
21 | public void test() throws InterruptedException {
22 | CountDownLatch latch = new CountDownLatch(count);
23 | ExecutorService executorService = Executors.newFixedThreadPool(count);
24 | for (int i = 0; i < count; i++) {
25 | executorService.execute(new Task(latch));
26 | }
27 | latch.await();
28 | System.out.println("Fire...");
29 | executorService.shutdown();
30 | while (executorService.isTerminated()) {
31 | }
32 | System.out.println("All task were done.");
33 | LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
34 | System.out.println("Terminal at " + LocalDateTime.now());
35 | }
36 |
37 | static class Task implements Runnable {
38 | private final CountDownLatch latch;
39 |
40 | public Task(CountDownLatch latch) {
41 | this.latch = latch;
42 | }
43 |
44 | @Override
45 | public void run() {
46 | try {
47 | int time = new Random().nextInt(5000);
48 | latch.countDown();
49 | System.out.println(Thread.currentThread().getId() + " time = " + LocalDateTime.now());
50 | Thread.sleep(time);
51 | System.out.println(Thread.currentThread().getId() + " sleep = " + time + ": check finished.");
52 | } catch (InterruptedException e) {
53 | e.printStackTrace();
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/CyclicBarrierTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.Random;
6 | import java.util.concurrent.BrokenBarrierException;
7 | import java.util.concurrent.CyclicBarrier;
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 |
11 | /**
12 | * @author D瓜哥, https://www.diguage.com/
13 | * @since 2020-03-16 17:24
14 | */
15 | public class CyclicBarrierTest {
16 | @Test
17 | public void test() {
18 | CyclicBarrier barrier = new CyclicBarrier(5,
19 | () -> System.out.println("集合完毕,出发……"));
20 | ExecutorService executorService = Executors.newFixedThreadPool(5);
21 | for (int i = 0; i < 5; i++) {
22 | executorService.execute(new Task(barrier, "Task-" + (i + 1)));
23 | }
24 | executorService.shutdown();
25 | while (!executorService.isTerminated()) {
26 | }
27 | System.out.println("Finished.");
28 | }
29 |
30 | static class Task implements Runnable {
31 | private final CyclicBarrier barrier;
32 | private final String name;
33 |
34 | public Task(CyclicBarrier barrier, String name) {
35 | this.barrier = barrier;
36 | this.name = name;
37 | }
38 |
39 | @Override
40 | public void run() {
41 | try {
42 | System.out.println(name + " start...");
43 | barrier.await();
44 | Thread.sleep(new Random().nextInt(1000));
45 | System.out.println(name + " running...");
46 | barrier.await();
47 | } catch (InterruptedException | BrokenBarrierException e) {
48 | e.printStackTrace();
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/DelayQueueTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.Objects;
6 | import java.util.concurrent.DelayQueue;
7 | import java.util.concurrent.Delayed;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * @author D瓜哥, https://www.diguage.com/
12 | * @since 2020-04-22 16:38
13 | */
14 | public class DelayQueueTest {
15 | @Test
16 | public void test() throws InterruptedException {
17 | DelayQueue delayQueue = new DelayQueue<>();
18 | for (int i = 0; i < 10; i++) {
19 | delayQueue.add(new IntDelay(i));
20 | }
21 | while (!delayQueue.isEmpty()) {
22 | IntDelay delay = delayQueue.take();
23 | if (Objects.nonNull(delay)) {
24 | System.out.println(delay.num);
25 | }
26 | }
27 | }
28 |
29 | public static class IntDelay implements Delayed {
30 |
31 | private int num;
32 | private long deadline;
33 |
34 | public IntDelay(int num) {
35 | this.num = num;
36 | deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(num);
37 | }
38 |
39 | @Override
40 | public long getDelay(TimeUnit unit) {
41 | return deadline - System.currentTimeMillis();
42 | }
43 |
44 | @Override
45 | public int compareTo(Delayed o) {
46 | IntDelay param = (IntDelay) o;
47 | return Integer.compare(this.num, param.num);
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/FlowTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.SubmissionPublisher;
6 | import java.util.concurrent.TimeUnit;
7 |
8 | import static java.util.concurrent.Flow.Subscriber;
9 | import static java.util.concurrent.Flow.Subscription;
10 |
11 | public class FlowTest {
12 |
13 | @Test
14 | public void test() throws InterruptedException {
15 | SubmissionPublisher publisher = new SubmissionPublisher<>();
16 | publisher.subscribe(new PrintSubscriber());
17 |
18 | System.out.println("Submitting items...");
19 | for (int i = 0; i < 10; i++) {
20 | publisher.submit(i);
21 | }
22 |
23 | TimeUnit.SECONDS.sleep(1);
24 | publisher.close();
25 | }
26 |
27 | public static class PrintSubscriber implements Subscriber {
28 | private Subscription subscription;
29 |
30 | @Override
31 | public void onSubscribe(Subscription subscription) {
32 | this.subscription = subscription;
33 | subscription.request(1);
34 | }
35 |
36 | @Override
37 | public void onNext(Integer item) {
38 | System.out.println("Received item: " + item);
39 | subscription.request(1);
40 | }
41 |
42 | @Override
43 | public void onError(Throwable throwable) {
44 | System.out.println("Error occurred: " + throwable.getMessage());
45 | }
46 |
47 | @Override
48 | public void onComplete() {
49 | System.out.println("PrintSubscriber is complete");
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/ForkJoinPoolTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.io.File;
6 | import java.util.LinkedList;
7 | import java.util.List;
8 | import java.util.Objects;
9 | import java.util.concurrent.ExecutionException;
10 | import java.util.concurrent.ForkJoinPool;
11 | import java.util.concurrent.ForkJoinTask;
12 | import java.util.concurrent.RecursiveTask;
13 |
14 | /**
15 | * @author D瓜哥, https://www.diguage.com/
16 | * @since 2020-03-12 10:54
17 | */
18 | public class ForkJoinPoolTest {
19 | @Test
20 | public void test() {
21 | ForkJoinPool pool = new ForkJoinPool(2);
22 | String homePath = System.getProperty("user.home");
23 | FileCountTask task = new FileCountTask(homePath);
24 | ForkJoinTask result = pool.submit(task);
25 | try {
26 | Integer count = result.get();
27 | System.out.println("file count = " + count);
28 | } catch (InterruptedException | ExecutionException e) {
29 | e.printStackTrace();
30 | }
31 | pool.shutdown();
32 | while (!pool.isTerminated()) {
33 | }
34 | System.out.println("All thread finish...");
35 | }
36 |
37 | public static class FileCountTask extends RecursiveTask {
38 | private File file;
39 |
40 | public FileCountTask(File file) {
41 | this.file = file;
42 | }
43 |
44 | public FileCountTask(String file) {
45 | this.file = new File(file);
46 | }
47 |
48 | @Override
49 | protected Integer compute() {
50 | int count = 0;
51 | if (file.isFile()) {
52 | count += 1;
53 | } else {
54 | File[] files = file.listFiles();
55 | if (Objects.isNull(files)) {
56 | files = new File[0];
57 | }
58 | List subTasks = new LinkedList<>();
59 | for (File f : files) {
60 | if (f.isDirectory()) {
61 | FileCountTask task = new FileCountTask(f);
62 | subTasks.add(task);
63 | task.fork();
64 | } else {
65 | count += 1;
66 | }
67 | }
68 | for (FileCountTask subTask : subTasks) {
69 | count += subTask.join();
70 | }
71 | }
72 | System.out.printf("%8d %s %n", count, file.getAbsolutePath());
73 | return count;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/JolTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.openjdk.jol.info.ClassLayout;
4 | import org.openjdk.jol.vm.VM;
5 |
6 | /**
7 | * @author D瓜哥, https://www.diguage.com/
8 | * @since 2020-04-08 16:10
9 | */
10 | public class JolTest {
11 | /**
12 | * Java Object Layout
13 | */
14 | public static void main(String[] args) {
15 |
16 | System.out.println(VM.current().details());
17 | System.out.println("--o = 12--------------");
18 | Object o = new Object() {};
19 | System.out.println(ClassLayout.parseInstance(o).toPrintable());
20 |
21 | System.out.println("--o2 = 12--------------");
22 | Object o2 = new Object() {
23 | private String name = "";
24 | private long age = 0;
25 | };
26 | System.out.println(ClassLayout.parseInstance(o2).toPrintable());
27 | System.out.println("--\"119\"--------------");
28 | String s = "119";
29 | System.out.println(s.hashCode());
30 | System.out.println(ClassLayout.parseInstance(s).toPrintable());
31 | System.out.println("--119L--------------");
32 | System.out.println(ClassLayout.parseInstance(119L).toPrintable());
33 |
34 | System.out.println("--o[] = 16--------------");
35 | System.out.println(ClassLayout.parseInstance(new Object[0]).toPrintable());
36 |
37 | System.out.println("--o[1]--------------");
38 | System.out.println(ClassLayout.parseInstance(new Object[]{new Object()}).toPrintable());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/LockSupportTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.locks.LockSupport;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-16 19:16
10 | */
11 | public class LockSupportTest {
12 | @Test
13 | public void test() throws InterruptedException {
14 | Thread t1 = new Thread(new Task("t1"));
15 | Thread t2 = new Thread(new Task("t2"));
16 | t1.start();
17 | Thread.sleep(1000);
18 | t2.start();
19 | LockSupport.unpark(t1);
20 | LockSupport.unpark(t2);
21 | t1.join();
22 | t2.join();
23 | System.out.println("finish...");
24 | }
25 |
26 | static class Task implements Runnable {
27 | private String name;
28 |
29 | public Task(String name) {
30 | this.name = name;
31 | }
32 |
33 | @Override
34 | public void run() {
35 | synchronized (LockSupportTest.class) {
36 | System.out.println("in " + name);
37 | LockSupport.park();
38 | }
39 | }
40 | }
41 |
42 | @Test
43 | public void testParkAndUnpark() throws InterruptedException {
44 | System.out.println("--m1------");
45 | Thread thread = new Thread(() -> {
46 | System.out.println("--t1------");
47 | LockSupport.park();
48 | System.out.println("--t2------");
49 | });
50 | thread.start();
51 | Thread.sleep(5000);
52 | LockSupport.unpark(thread);
53 | System.out.println("--m2------");
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/LongAdderTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.atomic.LongAdder;
8 | import java.util.concurrent.locks.LockSupport;
9 |
10 | /**
11 | * @author D瓜哥, https://www.diguage.com/
12 | * @since 2020-04-09 14:23
13 | */
14 | public class LongAdderTest {
15 | @Test
16 | public void test() {
17 | LongAdder adder = new LongAdder();
18 | int processors = Runtime.getRuntime().availableProcessors();
19 | System.out.println(processors);
20 | ExecutorService executor = Executors.newFixedThreadPool(processors);
21 | for (int i = 0; i < processors - 1; i++) {
22 | executor.execute(() -> {
23 | for (int j = 0; j < Integer.MAX_VALUE; j++) {
24 | adder.increment();
25 | }
26 | });
27 | }
28 | executor.execute(() -> {
29 | while (true) {
30 | try {
31 | System.out.println(adder.sum());
32 | Thread.sleep(100);
33 | } catch (InterruptedException e) {
34 | e.printStackTrace();
35 | }
36 | }
37 | });
38 | executor.shutdown();
39 | LockSupport.park();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/Mutex.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import java.util.concurrent.TimeUnit;
4 | import java.util.concurrent.locks.AbstractQueuedSynchronizer;
5 | import java.util.concurrent.locks.Condition;
6 | import java.util.concurrent.locks.Lock;
7 |
8 | /**
9 | * @author D瓜哥, https://www.diguage.com/
10 | * @since 2020-03-31 10:27
11 | */
12 | public class Mutex implements Lock {
13 |
14 | private static class Sync extends AbstractQueuedSynchronizer {
15 | @Override
16 | protected boolean tryAcquire(int arg) {
17 | if (compareAndSetState(0, 1)) {
18 | setExclusiveOwnerThread(Thread.currentThread());
19 | return true;
20 | }
21 | return false;
22 | }
23 |
24 | @Override
25 | protected boolean tryRelease(int arg) {
26 | if (getState() == 0) {
27 | throw new IllegalMonitorStateException();
28 | }
29 | setExclusiveOwnerThread(null);
30 | setState(0);
31 | return true;
32 | }
33 |
34 | @Override
35 | protected boolean isHeldExclusively() {
36 | return getState() == 1;
37 | }
38 |
39 | Condition newCodition() {
40 | return new ConditionObject();
41 | }
42 | }
43 |
44 | private final Sync sync = new Sync();
45 |
46 | @Override
47 | public void lock() {
48 | sync.acquire(1);
49 | }
50 |
51 | @Override
52 | public void lockInterruptibly() throws InterruptedException {
53 | sync.acquireInterruptibly(1);
54 | }
55 |
56 | @Override
57 | public boolean tryLock() {
58 | return sync.tryAcquire(1);
59 | }
60 |
61 | @Override
62 | public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
63 | return sync.tryAcquireSharedNanos(1, unit.toNanos(time));
64 | }
65 |
66 | @Override
67 | public void unlock() {
68 | sync.release(1);
69 | }
70 |
71 | @Override
72 | public Condition newCondition() {
73 | return sync.newCodition();
74 | }
75 |
76 | public boolean isLocked() {
77 | return sync.isHeldExclusively();
78 | }
79 |
80 | public boolean hasQueuedThreads() {
81 | return sync.hasQueuedThreads();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/ReentrantReadWriteLockTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.Random;
6 | import java.util.concurrent.locks.Lock;
7 | import java.util.concurrent.locks.ReentrantReadWriteLock;
8 |
9 | /**
10 | * @author D瓜哥, https://www.diguage.com/
11 | * @since 2020-03-16 17:07
12 | */
13 | public class ReentrantReadWriteLockTest {
14 | private volatile int value;
15 |
16 | public int handleRead(Lock lock) throws InterruptedException {
17 | try {
18 | lock.lock();
19 | Thread.sleep(1000);
20 | System.out.println(Thread.currentThread().getId() + " : read done!");
21 | return value;
22 | } finally {
23 | lock.unlock();
24 | }
25 | }
26 |
27 | public void handleWrite(Lock lock, int value) throws InterruptedException {
28 | try {
29 | lock.lock();
30 | Thread.sleep(1000);
31 | this.value = value;
32 | System.out.println(Thread.currentThread().getId() + " : write done!");
33 | } finally {
34 | lock.unlock();
35 | }
36 | }
37 |
38 | @Test
39 | public void test() throws InterruptedException {
40 | ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
41 | ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
42 | ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
43 |
44 | Runnable readTask = () -> {
45 | try {
46 | handleRead(readLock);
47 | } catch (InterruptedException e) {
48 | e.printStackTrace();
49 | }
50 | };
51 |
52 | Runnable writeTask = () -> {
53 | try {
54 | handleWrite(writeLock, new Random().nextInt());
55 | } catch (InterruptedException e) {
56 | e.printStackTrace();
57 | }
58 | };
59 |
60 | for (int i = 1; i <= 20; i++) {
61 | if (i % 10 == 5) {
62 | new Thread(writeTask).start();
63 | } else {
64 | new Thread(readTask).start();
65 | }
66 | }
67 | Thread.sleep(20 * 1000);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/ScheduledThreadPoolExecutorTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.time.LocalDateTime;
6 | import java.util.concurrent.ScheduledExecutorService;
7 | import java.util.concurrent.ScheduledThreadPoolExecutor;
8 | import java.util.concurrent.TimeUnit;
9 | import java.util.concurrent.locks.LockSupport;
10 |
11 | public class ScheduledThreadPoolExecutorTest {
12 | @Test
13 | public void test() {
14 | ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(0);
15 | executor.scheduleAtFixedRate(() -> System.out.println("OKKK"), 0, 10, TimeUnit.MINUTES);
16 | LockSupport.parkNanos(TimeUnit.MINUTES.toNanos(1));
17 | System.out.println(executor);
18 | }
19 |
20 | @Test
21 | public void testSchedule() {
22 | ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(0);
23 | for (int i = 0; i < 100; i++) {
24 | executor.schedule(() -> System.out.println(LocalDateTime.now() + " OKKK"), 0, TimeUnit.MINUTES);
25 | }
26 | LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(10));
27 | System.out.println(executor);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/SemaphoreTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.Semaphore;
8 | import java.util.concurrent.TimeUnit;
9 | import java.util.concurrent.locks.LockSupport;
10 |
11 | /**
12 | * @author D瓜哥, https://www.diguage.com/
13 | * @since 2020-03-16 16:51
14 | */
15 | public class SemaphoreTest {
16 | @Test
17 | public void test() {
18 | ExecutorService executorService = Executors.newFixedThreadPool(20);
19 | Semaphore semaphore = new Semaphore(5);
20 | for (int i = 0; i < 20; i++) {
21 | executorService.execute(new Task(semaphore));
22 | }
23 | executorService.shutdown();
24 | while (!executorService.isTerminated()) {
25 | }
26 | System.out.println("Ok...");
27 | }
28 |
29 | static class Task implements Runnable {
30 | private final Semaphore semaphore;
31 |
32 | public Task(Semaphore semaphore) {
33 | this.semaphore = semaphore;
34 | }
35 |
36 | @Override
37 | public void run() {
38 | try {
39 | semaphore.acquire();
40 | Thread.sleep(2000);
41 | System.out.println(Thread.currentThread().getId() + " :done!");
42 | } catch (InterruptedException e) {
43 | e.printStackTrace();
44 | } finally {
45 | semaphore.release();
46 | }
47 | }
48 | }
49 |
50 | @Test
51 | public void testReentrant() {
52 | // 将 Semaphore 的参数分别设置成 1 和 5 运行看结果
53 | // 递归调用的次数跟 Semaphore 的参数一致
54 | // 说明,如果 Semaphore 参数为 1 时,它不支持重入。
55 | Semaphore semaphore = new Semaphore(5);
56 | class Task implements Runnable {
57 | private final Semaphore semaphore;
58 | private int len = 1;
59 |
60 | public Task(Semaphore semaphore) {
61 | this.semaphore = semaphore;
62 | }
63 |
64 | @Override
65 | public void run() {
66 | try {
67 | semaphore.acquire();
68 | System.out.println(len++);
69 | run();
70 | } catch (InterruptedException e) {
71 | e.printStackTrace();
72 | } finally {
73 | semaphore.release();
74 | }
75 | }
76 | }
77 | new Thread(new Task(semaphore)).start();
78 | LockSupport.parkNanos(TimeUnit.MINUTES.toNanos(1));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/StampedLockTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.ThreadLocalRandom;
8 | import java.util.concurrent.locks.StampedLock;
9 |
10 | /**
11 | * @author D瓜哥, https://www.diguage.com/
12 | * @since 2020-03-16 23:15
13 | */
14 | public class StampedLockTest {
15 | @Test
16 | public void test() {
17 | StampedLock lock = new StampedLock();
18 | Point point = new Point(lock);
19 | ExecutorService executorService = Executors.newFixedThreadPool(10);
20 | for (int i = 0; i < 1000; i++) {
21 | executorService.execute(() -> {
22 | ThreadLocalRandom random = ThreadLocalRandom.current();
23 | point.move(random.nextDouble(100), random.nextDouble(100));
24 | System.out.println("move point...");
25 | });
26 |
27 | executorService.execute(() -> {
28 | double distance = point.distanceFromOrigin();
29 | System.out.println("current distance = " + distance);
30 | });
31 | }
32 | }
33 |
34 | static class Point {
35 | private volatile double x, y;
36 | private final StampedLock lock;
37 |
38 | public Point(StampedLock lock) {
39 | this.lock = lock;
40 | }
41 |
42 | void move(double deltaX, double deltaxY) {
43 | long stamp = lock.writeLock();
44 | try {
45 | x += deltaX;
46 | y += deltaxY;
47 | } finally {
48 | lock.unlock(stamp);
49 | }
50 | }
51 |
52 | double distanceFromOrigin() {
53 | long stamp = lock.tryOptimisticRead();
54 | double currentX = x, currentY = y;
55 | if (!lock.validate(stamp)) {
56 | stamp = lock.readLock();
57 | try {
58 | currentX = x;
59 | currentY = y;
60 | } finally {
61 | lock.unlock(stamp);
62 | }
63 | }
64 | return Math.sqrt(currentX * currentX + currentY * currentY);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/SynchronizedTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.util.concurrent.locks.LockSupport;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-04-08 19:26
10 | */
11 | public class SynchronizedTest {
12 | public synchronized void lockMethod() {
13 | System.out.println("lock method");
14 | }
15 |
16 | public void lockObject() {
17 | synchronized (this) {
18 | System.out.println("lock object");
19 | }
20 | }
21 |
22 | @Test
23 | public void testInstanceLock() {
24 | SynMain main = new SynMain();
25 | new Thread(main::getInstanceLock1).start();
26 | new Thread(main::getInstanceLock2).start();
27 | new Thread(SynMain::getStaticLock1).start();
28 | new Thread(SynMain::getStaticLock2).start();
29 | LockSupport.park();
30 | }
31 |
32 |
33 | public static class SynMain {
34 | public static synchronized void getStaticLock1() {
35 | System.out.println("getStaticLock1 get lock, running...");
36 | try {
37 | Thread.sleep(Integer.MAX_VALUE);
38 | } catch (InterruptedException e) {
39 | e.printStackTrace();
40 | }
41 | }
42 |
43 | public static synchronized void getStaticLock2() {
44 | System.out.println("getStaticLock2 get lock, running...");
45 | try {
46 | Thread.sleep(Integer.MAX_VALUE);
47 | } catch (InterruptedException e) {
48 | e.printStackTrace();
49 | }
50 | }
51 |
52 | public synchronized void getInstanceLock1() {
53 | System.out.println("getInstanceLock1 get lock, running...");
54 | try {
55 | Thread.sleep(Integer.MAX_VALUE);
56 | } catch (InterruptedException e) {
57 | e.printStackTrace();
58 | }
59 | }
60 |
61 | public synchronized void getInstanceLock2() {
62 | System.out.println("getInstanceLock2 get lock, running...");
63 | try {
64 | Thread.sleep(Integer.MAX_VALUE);
65 | } catch (InterruptedException e) {
66 | e.printStackTrace();
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/ThreadLocalTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | /**
6 | * @author D瓜哥, https://www.diguage.com/
7 | * @since 2020-03-09 20:20
8 | */
9 | public class ThreadLocalTest {
10 | @Test
11 | public void test() {
12 | new Thread(new FirstApp()).start();
13 | }
14 |
15 | @Test
16 | public void testInheritableThreadLocal() {
17 | // TODO: InheritableThreadLocal
18 | }
19 |
20 | private static class FirstApp implements Runnable {
21 | private ThreadLocal threadLocal
22 | = ThreadLocal.withInitial(() -> "FirstApp-1");
23 |
24 | private ThreadLocal threadLocal2
25 | = ThreadLocal.withInitial(() -> "FirstApp-2");
26 |
27 | private SecondApp secondApp = new SecondApp();
28 | private ThridApp thridApp = new ThridApp();
29 |
30 | @Override
31 | public void run() {
32 | System.out.println(threadLocal.get());
33 | System.out.println(threadLocal2.get());
34 | new Thread(secondApp).start();
35 | thridApp.run();
36 | }
37 | }
38 |
39 | private static class SecondApp implements Runnable {
40 | private ThreadLocal threadLocal
41 | = ThreadLocal.withInitial(() -> "SecondApp");
42 |
43 | @Override
44 | public void run() {
45 | System.out.println(threadLocal.get());
46 | }
47 | }
48 |
49 | private static class ThridApp implements Runnable {
50 | private ThreadLocal threadLocal
51 | = ThreadLocal.withInitial(() -> getClass().getName());
52 |
53 | @Override
54 | public void run() {
55 | threadLocal.set("new-ThridApp-value");
56 | System.out.println(threadLocal.get());
57 | }
58 | }
59 | }
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/concurrent/ThreadMain.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.concurrent;
2 |
3 | import java.lang.management.ManagementFactory;
4 | import java.lang.management.ThreadInfo;
5 | import java.lang.management.ThreadMXBean;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-03-09 19:28
10 | */
11 | public class ThreadMain {
12 | public static void main(String[] args) {
13 | ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
14 | ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
15 | for (ThreadInfo info : threadInfos) {
16 | System.out.printf("[%d]%s\n", info.getThreadId(), info.getThreadName());
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/io/FileTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.io;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 | import java.nio.charset.StandardCharsets;
8 | import java.nio.file.Files;
9 | import java.nio.file.Paths;
10 | import java.time.ZoneId;
11 | import java.time.format.DateTimeFormatter;
12 | import java.util.Date;
13 |
14 | import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
15 |
16 | public class FileTest {
17 |
18 | private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
19 |
20 | @Test
21 | public void test() {
22 | File file = new File("/Users/lijun695/vm.log");
23 | long mod = file.lastModified();
24 | System.out.println(new Date(mod).toInstant().atZone(ZoneId.systemDefault()));
25 | System.out.println(file.getName());
26 | }
27 |
28 | public static void main(String[] args) throws IOException {
29 | for (int i = 0; i < 10; i++) {
30 | Files.write(Paths.get("/tmp/abc123/456/20230609183256983"),
31 | ("" + i + " -- D瓜哥 · https://www.diguage.com").getBytes(StandardCharsets.UTF_8), TRUNCATE_EXISTING);
32 | }
33 | }
34 |
35 | public static void createDirectory(String path) {
36 | File file = new File(path);
37 | if (file.exists() && !file.isDirectory()) {
38 | file.delete();
39 | }
40 | if (!file.exists()) {
41 | try {
42 | Files.createDirectories(Paths.get(path));
43 | } catch (IOException e) {
44 | e.printStackTrace();
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/jgroups/ChatClient01.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.jgroups;
2 |
3 | import java.util.Scanner;
4 |
5 | /**
6 | * 客户端01
7 | */
8 | public class ChatClient01 {
9 | // private static final String CONFIG_XML = "jgroups-chat-tcp.xml";
10 | //
11 | // public static void main(String[] args) {
12 | // SimpleChat4 chat01 = new SimpleChat4(CONFIG_XML);
13 | // chat01.start();
14 | // scannerChat(chat01);
15 | // }
16 | //
17 | // private static void scannerChat(SimpleChat4 chat01) {
18 | // Scanner scanner = new Scanner(System.in);
19 | // while (true) {
20 | // System.out.print("> ");
21 | // System.out.flush();
22 | // String line = scanner.next();
23 | // if (line.startsWith("quit") || line.startsWith("exit")) {
24 | // System.exit(-1);
25 | // break;
26 | // }
27 | // chat01.sendMessage(null, line);
28 | // }
29 | // }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/jgroups/ChatClient02.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.jgroups;
2 |
3 | import java.util.Scanner;
4 |
5 | /**
6 | * 客户端02
7 | */
8 | public class ChatClient02 {
9 | // private static final String CONFIG_XML = "jgroups-chat-tcp.xml";
10 | //
11 | // public static void main(String[] args) {
12 | // SimpleChat4 chat02 = new SimpleChat4(CONFIG_XML);
13 | // chat02.start();
14 | // scannerChat(chat02);
15 | // }
16 | //
17 | // private static void scannerChat(SimpleChat4 chat01) {
18 | // Scanner scanner = new Scanner(System.in);
19 | // while (true) {
20 | // System.out.print("> ");
21 | // System.out.flush();
22 | // String line = scanner.next();
23 | // if (line.startsWith("quit") || line.startsWith("exit")) {
24 | // System.exit(-1);
25 | // break;
26 | // }
27 | // chat01.sendMessage(null, line);
28 | // }
29 | // }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/jgroups/SimpleChat.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.jgroups;
2 |
3 | import org.jgroups.*;
4 | import org.jgroups.util.Util;
5 |
6 | import java.io.*;
7 | import java.time.LocalTime;
8 | import java.util.LinkedList;
9 | import java.util.List;
10 |
11 | public class SimpleChat implements Receiver {
12 | JChannel channel;
13 | String user_name = System.getProperty("user.name", LocalTime.now().toString());
14 | final List state = new LinkedList<>();
15 |
16 | public void viewAccepted(View new_view) {
17 | System.out.println("** view: " + new_view);
18 | }
19 |
20 | public void receive(Message msg) {
21 | String line = msg.getSrc() + ": " + msg.getObject();
22 | System.out.println(line);
23 | synchronized (state) {
24 | state.add(line);
25 | }
26 | }
27 |
28 | public void getState(OutputStream output) throws Exception {
29 | synchronized (state) {
30 | Util.objectToStream(state, new DataOutputStream(output));
31 | }
32 | }
33 |
34 | public void setState(InputStream input) throws Exception {
35 | List list = Util.objectFromStream(new DataInputStream(input));
36 | synchronized (state) {
37 | state.clear();
38 | state.addAll(list);
39 | }
40 | System.out.println("received state (" + list.size() + " messages in chat history):");
41 | list.forEach(System.out::println);
42 | }
43 |
44 |
45 | private void start() throws Exception {
46 | // channel = new JChannel(Thread.currentThread().getContextClassLoader().getResourceAsStream("jgroups-chat-udp.xml"));
47 | channel = new JChannel("jgroups-chat-tcp.xml");
48 | channel.setReceiver(this);
49 | channel.connect("ChatCluster");
50 | channel.getState(null, 10000);
51 | eventLoop();
52 | channel.close();
53 | }
54 |
55 | private void eventLoop() {
56 | BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
57 | while (true) {
58 | try {
59 | System.out.print("> ");
60 | System.out.flush();
61 | String line = in.readLine().toLowerCase();
62 | if (line.startsWith("quit") || line.startsWith("exit")) {
63 | break;
64 | }
65 | line = "[" + user_name + "] " + line;
66 | Message msg = new ObjectMessage(null, line);
67 | channel.send(msg);
68 | } catch (Exception e) {
69 | }
70 | }
71 | }
72 |
73 |
74 | public static void main(String[] args) throws Exception {
75 | new SimpleChat().start();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/jgroups/SimpleChat2.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.jgroups;
2 |
3 | import org.jgroups.*;
4 | import org.jgroups.util.Util;
5 |
6 | import java.io.*;
7 | import java.time.LocalTime;
8 | import java.util.LinkedList;
9 | import java.util.List;
10 |
11 | public class SimpleChat2 implements Receiver {
12 | JChannel channel;
13 | String user_name = System.getProperty("user.name", LocalTime.now().toString());
14 | final List state = new LinkedList<>();
15 |
16 | public void viewAccepted(View new_view) {
17 | System.out.println("** view: " + new_view);
18 | }
19 |
20 | public void receive(Message msg) {
21 | String line = msg.getSrc() + ": " + msg.getObject();
22 | System.out.println(line);
23 | synchronized (state) {
24 | state.add(line);
25 | }
26 | }
27 |
28 | public void getState(OutputStream output) throws Exception {
29 | synchronized (state) {
30 | Util.objectToStream(state, new DataOutputStream(output));
31 | }
32 | }
33 |
34 | public void setState(InputStream input) throws Exception {
35 | List list = Util.objectFromStream(new DataInputStream(input));
36 | synchronized (state) {
37 | state.clear();
38 | state.addAll(list);
39 | }
40 | System.out.println("received state (" + list.size() + " messages in chat history):");
41 | list.forEach(System.out::println);
42 | }
43 |
44 |
45 | private void start() throws Exception {
46 | // channel = new JChannel(Thread.currentThread().getContextClassLoader().getResourceAsStream("jgroups-chat-udp.xml"));
47 | channel = new JChannel("jgroups-chat-tcp.xml");
48 | channel.setReceiver(this);
49 | channel.connect("ChatCluster");
50 | channel.getState(null, 10000);
51 | eventLoop();
52 | channel.close();
53 | }
54 |
55 | private void eventLoop() {
56 | BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
57 | while (true) {
58 | try {
59 | System.out.print("> ");
60 | System.out.flush();
61 | String line = in.readLine().toLowerCase();
62 | if (line.startsWith("quit") || line.startsWith("exit")) {
63 | break;
64 | }
65 | line = "[" + user_name + "] " + line;
66 | Message msg = new ObjectMessage(null, line);
67 | channel.send(msg);
68 | } catch (Exception e) {
69 | }
70 | }
71 | }
72 |
73 |
74 | public static void main(String[] args) throws Exception {
75 | new SimpleChat2().start();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/jgroups/SimpleChat4.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.jgroups;
2 |
3 | import java.io.InputStream;
4 | import java.nio.charset.Charset;
5 | import org.jgroups.Address;
6 | import org.jgroups.JChannel;
7 | import org.jgroups.Message;
8 | //import org.jgroups.ReceiverAdapter;
9 | import org.jgroups.View;
10 |
11 | /**
12 | * jGroups
13 | */
14 | public class SimpleChat4 {
15 | // private static final String DEFULT_CONFIG_XML = "jgroups-chat-udp.xml";
16 | // /**
17 | // * 配置文件.
18 | // */
19 | // private String confXml;
20 | //
21 | // /**
22 | // * 集群名称.
23 | // */
24 | // private static final String CLUSTER_NAME = "WTCLOSYN-SIMPLE-CHAT";
25 | // /**
26 | // * 字符编码
27 | // */
28 | // private static final Charset CHARSET = Charset.defaultCharset();
29 | // /**
30 | // * 节点通道.
31 | // */
32 | // private JChannel channel = null;
33 | //
34 | // public SimpleChat4() {
35 | // this.confXml = DEFULT_CONFIG_XML;
36 | // }
37 | //
38 | // public SimpleChat4(String confXml) {
39 | // this.confXml = confXml;
40 | // }
41 | //
42 | // /**
43 | // * 发送消息
44 | // */
45 | // public void start() {
46 | // try {
47 | // InputStream cfg = SimpleChat4.class.getClassLoader().getResourceAsStream(confXml);
48 | // channel = new JChannel(cfg);
49 | // //连接到集群
50 | // channel.connect(CLUSTER_NAME);
51 | // channel.setDiscardOwnMessages(true);
52 | // //指定Receiver用来收消息和得到View改变的通知
53 | // channel.setReceiver(this);
54 | // }catch (Exception e){
55 | // System.out.println("启动Chat失败!");
56 | // }
57 | // }
58 | //
59 | // public void sendMessage(Address dst, String text){
60 | // try {
61 | // //Message构造函数的第一个参数代表目的地地址,这里传null代表要发消息给集群内的所有地址
62 | // //第二个参数表示源地址,传null即可,框架会自动赋值
63 | // //第三个参数line会被序列化成byte[]然后发送,推荐自己序列化而不是用java自带的序列化
64 | // Message msg = new Message(dst, null, text.getBytes(CHARSET));
65 | // //发消息到集群
66 | // channel.send(msg);
67 | // } catch (Exception e) {
68 | // System.out.println("Chat发送消息失败!");
69 | // }
70 | // }
71 | //
72 | // @Override
73 | // public void receive(Message msg) {
74 | // String line = msg.getSrc() + ":" + new String(msg.getBuffer(), CHARSET);
75 | // System.out.println(line);
76 | // }
77 | //
78 | // @Override
79 | // public void viewAccepted(View view) {
80 | // System.out.println("A client has changed!" + view.toString());
81 | // }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/math/BigDecimalTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.math;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.math.BigDecimal;
6 |
7 | public class BigDecimalTest {
8 | @Test
9 | public void test() {
10 | BigDecimal decimal = new BigDecimal("10.");
11 | System.out.println(decimal);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/NettyTest.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | /**
6 | * @author D瓜哥, https://www.diguage.com/
7 | * @since 2020-06-03 22:09
8 | */
9 | public class NettyTest {
10 | @Test
11 | public void test() {
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/buf/NettyByteBuf.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.buf;
2 |
3 |
4 | import io.netty.buffer.ByteBuf;
5 | import io.netty.buffer.Unpooled;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import static java.nio.charset.StandardCharsets.UTF_8;
9 |
10 | /**
11 | * @author D瓜哥, https://www.diguage.com/
12 | * @since 2020-06-28 10:26
13 | */
14 | public class NettyByteBuf {
15 | @Test
16 | public void test01() {
17 | // 1. 创建对象,对象包含一个 byte[10] 的数组
18 | // 2. 在 Netty 的 ByteBuf 中,不需要使用 flip 进行反转
19 | // 底层维护了 readIndex 和 writeIndex
20 | // 3. 通过 readIndex、 writeIndex 和 capacity,将 buffer 分成三个区域
21 | // 3.1 0 -- readIndex 已读
22 | // 3.2 readIndex - writeIndex 可读
23 | // 3.3 writeIndex - capacity 可写
24 | ByteBuf buffer = Unpooled.buffer(10);
25 | for (int i = 0; i < 10; i++) {
26 | buffer.writeByte(i);
27 | }
28 |
29 | System.out.println("capacity=" + buffer.capacity());
30 | // 输出
31 | for (int i = 0; i < buffer.capacity(); i++) {
32 | System.out.println(buffer.getByte(i));
33 | }
34 |
35 | for (int i = 0; i < buffer.capacity(); i++) {
36 | System.out.println(buffer.readByte());
37 | }
38 | }
39 |
40 | @Test
41 | public void test02() {
42 | // 创建 ByteBuf
43 | ByteBuf byteBuf = Unpooled.copiedBuffer("Hello, D瓜哥!", UTF_8);
44 |
45 | // 使用相关方法
46 | if (byteBuf.hasArray()) {
47 | byte[] content = byteBuf.array();
48 | // 将 content 转成字符串
49 | System.out.println(new String(content, UTF_8));
50 |
51 | System.out.println("byteBuf=" + byteBuf);
52 | System.out.println(byteBuf.arrayOffset());
53 | System.out.println(byteBuf.readerIndex());
54 | System.out.println(byteBuf.writerIndex());
55 | System.out.println(byteBuf.capacity());
56 | System.out.println(byteBuf.readByte());
57 |
58 | int len = byteBuf.readableBytes(); // 可读取的字符数
59 | System.out.println(len);
60 |
61 | for (int i = 0; i < len; i++) {
62 | System.out.println((char) byteBuf.getByte(i));
63 | }
64 |
65 | // 按照范围读取
66 | System.out.println(byteBuf.getCharSequence(0, 4, UTF_8));
67 | }
68 | }
69 |
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/chats/GroupChatClient.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.chats;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.Channel;
5 | import io.netty.channel.ChannelFuture;
6 | import io.netty.channel.ChannelInitializer;
7 | import io.netty.channel.ChannelPipeline;
8 | import io.netty.channel.nio.NioEventLoopGroup;
9 | import io.netty.channel.socket.SocketChannel;
10 | import io.netty.channel.socket.nio.NioSocketChannel;
11 | import io.netty.handler.codec.string.StringDecoder;
12 | import io.netty.handler.codec.string.StringEncoder;
13 | import org.junit.jupiter.api.Test;
14 |
15 | import java.util.Scanner;
16 |
17 | /**
18 | * @author D瓜哥, https://www.diguage.com/
19 | * @since 2020-06-28 11:24
20 | */
21 | public class GroupChatClient {
22 | // 属性
23 | private final String host;
24 | private final int port;
25 |
26 | public GroupChatClient(String host, int port) {
27 | this.host = host;
28 | this.port = port;
29 | }
30 |
31 | public void run() throws InterruptedException {
32 | NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
33 | try {
34 | Bootstrap bootstrap = new Bootstrap();
35 | bootstrap.group(eventExecutors)
36 | .channel(NioSocketChannel.class)
37 | .handler(new ChannelInitializer() {
38 | @Override
39 | protected void initChannel(SocketChannel ch) throws Exception {
40 | ChannelPipeline pipeline = ch.pipeline();
41 | pipeline.addLast("decoder", new StringDecoder());
42 | pipeline.addLast("encoder", new StringEncoder());
43 | // 加入自定义的 handler TODO
44 | pipeline.addLast(new GroupChatClientHandler());
45 | }
46 | });
47 | ChannelFuture future = bootstrap.connect(host, port).sync();
48 | Channel channel = future.channel();
49 | System.out.println("-----" + channel.localAddress() + "------");
50 | // 客户端需要输入信息,创建一个扫描器
51 | // TODO 还不能输入,调试一下,看看怎么回事?
52 | Scanner scanner = new Scanner(System.in);
53 | while (scanner.hasNextLine()) {
54 | String msg = scanner.nextLine();
55 | channel.writeAndFlush(msg + "\r\n");
56 | }
57 | } finally {
58 | eventExecutors.shutdownGracefully();
59 | }
60 | }
61 |
62 | public static class Clients {
63 | @Test
64 | public void client1() throws InterruptedException {
65 | new GroupChatClient("127.0.0.1", 11911).run();
66 | }
67 |
68 | @Test
69 | public void client2() throws InterruptedException {
70 | new GroupChatClient("127.0.0.1", 11911).run();
71 | }
72 |
73 | @Test
74 | public void client3() throws InterruptedException {
75 | new GroupChatClient("127.0.0.1", 11911).run();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/chats/GroupChatClientHandler.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.chats;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.SimpleChannelInboundHandler;
5 |
6 | /**
7 | * @author D瓜哥, https://www.diguage.com/
8 | * @since 2020-06-28 11:32
9 | */
10 | public class GroupChatClientHandler extends SimpleChannelInboundHandler {
11 | @Override
12 | protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
13 | System.out.println(msg);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/chats/GroupChatServer.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.chats;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.ChannelOption;
7 | import io.netty.channel.ChannelPipeline;
8 | import io.netty.channel.nio.NioEventLoopGroup;
9 | import io.netty.channel.socket.SocketChannel;
10 | import io.netty.channel.socket.nio.NioServerSocketChannel;
11 | import io.netty.handler.codec.string.StringDecoder;
12 | import io.netty.handler.codec.string.StringEncoder;
13 |
14 | /**
15 | * @author D瓜哥, https://www.diguage.com/
16 | * @since 2020-06-28 10:56
17 | */
18 | public class GroupChatServer {
19 | private int port;
20 |
21 | public GroupChatServer(int port) {
22 | this.port = port;
23 | }
24 |
25 | // 处理客户端请求
26 | public void run() throws InterruptedException {
27 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
28 | NioEventLoopGroup workerGroup = new NioEventLoopGroup();
29 | try {
30 | ServerBootstrap bootstrap = new ServerBootstrap();
31 | bootstrap.group(bossGroup, workerGroup)
32 | .channel(NioServerSocketChannel.class)
33 | .option(ChannelOption.SO_BACKLOG, 128)
34 | .childOption(ChannelOption.SO_KEEPALIVE, true)
35 | .childHandler(new ChannelInitializer() {
36 | @Override
37 | protected void initChannel(SocketChannel ch) throws Exception {
38 | // 获取 pipeline
39 | ChannelPipeline pipeline = ch.pipeline();
40 | // 向 pipeline 加入解码器
41 | pipeline.addLast("decoder", new StringDecoder());
42 | // 向 pipeline 加入编码器
43 | pipeline.addLast("encoder", new StringEncoder());
44 | // 加入自己的业务处理 handler TODO
45 | pipeline.addLast(new GroupChatServerHandler());
46 | }
47 | });
48 | System.out.println("Netty 服务器启动");
49 | ChannelFuture future = bootstrap.bind(port).sync();
50 | // 关闭监听
51 | future.channel().closeFuture().sync();
52 | } finally {
53 | bossGroup.shutdownGracefully();
54 | workerGroup.shutdownGracefully();
55 | }
56 | }
57 |
58 | public static void main(String[] args) throws InterruptedException {
59 | new GroupChatServer(11911).run();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/chats/GroupChatServerHandler.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.chats;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.SimpleChannelInboundHandler;
6 | import io.netty.channel.group.ChannelGroup;
7 | import io.netty.channel.group.DefaultChannelGroup;
8 | import io.netty.util.concurrent.GlobalEventExecutor;
9 |
10 | import java.time.LocalDateTime;
11 |
12 | /**
13 | * @author D瓜哥, https://www.diguage.com/
14 | * @since 2020-06-28 11:04
15 | */
16 | public class GroupChatServerHandler extends SimpleChannelInboundHandler {
17 | // 定义一个 channel 组,管理所有的 channel
18 | // GlobalEventExecutor.INSTANCE 是全局的事件执行器,是一个单例
19 | private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
20 |
21 | /**
22 | * 表示连接建立,一旦建立,第一个执行
23 | *
24 | * 将当前 channel 加入到 channelGroup
25 | */
26 | @Override
27 | public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
28 | Channel channel = ctx.channel();
29 | /**
30 | * 将该客户加入聊天的信息推送给其他在线的客户端
31 | *
32 | * 该方法会将 channelGroup 中所有的 channel 遍历,并发送消息。
33 | *
34 | * 我们不需要自己遍历。
35 | */
36 | channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 加入群聊 at" + LocalDateTime.now() + " \n");
37 | channelGroup.add(channel);
38 |
39 | super.handlerAdded(ctx);
40 | }
41 |
42 | /**
43 | * 表示 channel 处于活动状态,表示 xx 上线
44 | */
45 | @Override
46 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
47 | System.out.println(ctx.channel().remoteAddress() + " 上线了~");
48 | }
49 |
50 | /**
51 | * 表示 channel 处于不活动状态,表示 xx 离线
52 | */
53 | @Override
54 | public void channelInactive(ChannelHandlerContext ctx) throws Exception {
55 | Channel channel = ctx.channel();
56 | System.out.println(channel.remoteAddress() + " 下线了");
57 | }
58 |
59 | /**
60 | * 断开连接,将 xx客户离开信息推送给当前在线客户
61 | */
62 | @Override
63 | public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
64 | Channel channel = ctx.channel();
65 | channelGroup.writeAndFlush("[客户端]" + channel.remoteAddress() + " 离开了\n");
66 | // 触发这个方法后,不需要手动删除,Netty 会自动删除的
67 | // channelGroup.remove(channel);
68 | System.out.println("channelGroup size=" + channelGroup.size());
69 | }
70 |
71 | @Override
72 | protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
73 | // 获取当前 channel
74 | Channel channel = ctx.channel();
75 | // 这时我们遍历 channelGroup,根据不同的情况,回送不同的消息
76 | channelGroup.forEach(ch -> {
77 | if (channel != ch) {
78 | ch.writeAndFlush("[客户]" + channel.remoteAddress() + " 发送消息:" + msg + "\n");
79 |
80 | } else {
81 | ch.writeAndFlush("[自己]发送了消息:" + msg + "\n");
82 | }
83 | });
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/consumer/DubboClientBootstrap.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.consumer;
2 |
3 | import com.diguage.truman.netty.dubbo.interfaces.HelloService;
4 | import com.diguage.truman.netty.dubbo.netty.NettyClient;
5 |
6 | /**
7 | * @author D瓜哥, https://www.diguage.com/
8 | * @since 2020-06-29 17:44
9 | */
10 | public class DubboClientBootstrap {
11 | // 这里定义协议头
12 | public static final String providerName = "#Hello#";
13 |
14 | public static void main(String[] args) throws InterruptedException {
15 | NettyClient consumer = new NettyClient();
16 | HelloService helloService = (HelloService) consumer.getBean(HelloService.class, providerName);
17 | // 通过代理对象调用服务提供者的方法
18 | for (int i = 0; i < 100; i++) {
19 | Thread.sleep(1000);
20 | String result = helloService.hello("您好啊,Dubbo~~~" + i);
21 | System.out.println("调用结果 res=" + result);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/interfaces/HelloService.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.interfaces;
2 |
3 | /**
4 | * @author D瓜哥, https://www.diguage.com/
5 | * @since 2020-06-29 16:55
6 | */
7 | public interface HelloService {
8 | String hello(String msg);
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/netty/NettyClient.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.netty;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.ChannelInitializer;
5 | import io.netty.channel.ChannelOption;
6 | import io.netty.channel.ChannelPipeline;
7 | import io.netty.channel.nio.NioEventLoopGroup;
8 | import io.netty.channel.socket.SocketChannel;
9 | import io.netty.channel.socket.nio.NioSocketChannel;
10 | import io.netty.handler.codec.string.StringDecoder;
11 | import io.netty.handler.codec.string.StringEncoder;
12 |
13 | import java.lang.reflect.Proxy;
14 | import java.util.concurrent.ExecutorService;
15 | import java.util.concurrent.Executors;
16 |
17 | /**
18 | * @author D瓜哥, https://www.diguage.com/
19 | * @since 2020-06-29 17:32
20 | */
21 | public class NettyClient {
22 | // 创建线程池
23 | private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
24 |
25 | private static NettyClientHandler client;
26 |
27 | // 使用 代理模式,创建代理对象
28 | public Object getBean(final Class> serviceService, final String providerName) {
29 | return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
30 | new Class>[]{serviceService}, (proxy, method, args) -> {
31 | if (client == null) {
32 | initClient();
33 | }
34 | // 设置要发送给服务器的消息
35 | // providerName 协议头,args[0] 就是客户端调用 API hello(msg) 时,传的参数
36 | client.setParam(providerName + args[0]);
37 | return executor.submit(client).get();
38 | });
39 | }
40 |
41 | private static void initClient() {
42 | client = new NettyClientHandler();
43 | NioEventLoopGroup group = new NioEventLoopGroup();
44 | Bootstrap bootstrap = new Bootstrap();
45 | bootstrap.group(group)
46 | .channel(NioSocketChannel.class)
47 | .option(ChannelOption.TCP_NODELAY, true)
48 | .handler(new ChannelInitializer() {
49 | @Override
50 | protected void initChannel(SocketChannel ch) throws Exception {
51 | ChannelPipeline pipeline = ch.pipeline();
52 | pipeline.addLast(new StringDecoder());
53 | pipeline.addLast(new StringEncoder());
54 | pipeline.addLast(client);
55 | }
56 | });
57 | try {
58 | bootstrap.connect("127.0.0.1", 11911).sync();
59 | System.out.println("客户端建立链接……");
60 | } catch (Exception e) {
61 | e.printStackTrace();
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/netty/NettyClientHandler.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.netty;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.ChannelInboundHandlerAdapter;
5 |
6 | import java.util.concurrent.Callable;
7 |
8 | /**
9 | * @author D瓜哥, https://www.diguage.com/
10 | * @since 2020-06-29 17:18
11 | */
12 | public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {
13 |
14 | private ChannelHandlerContext context;
15 | private String result;
16 | private String param;
17 |
18 | /**
19 | * 与服务器的连接创建后,就会被调用
20 | */
21 | @Override
22 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
23 | this.context = ctx;
24 | }
25 |
26 | /**
27 | * 收到服务器的数据后,调用方法
28 | */
29 | @Override
30 | public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
31 | this.result = msg.toString();
32 | notify(); // 唤醒等待的线程
33 | }
34 |
35 | @Override
36 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
37 | ctx.close();
38 | }
39 |
40 | /**
41 | * 被代理对象调用,发送数据给服务器,-> wait -> 等待被唤醒(channel read) -> 返回结果
42 | */
43 | @Override
44 | public synchronized Object call() throws Exception {
45 | context.writeAndFlush(param);
46 | // 进行 wait
47 | wait(); // 等待 channel read 方法获取的到服务器的结果后,唤醒
48 | return result;
49 | }
50 |
51 | public void setParam(String param) {
52 | this.param = param;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/netty/NettyServer.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.netty;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.ChannelOption;
7 | import io.netty.channel.ChannelPipeline;
8 | import io.netty.channel.nio.NioEventLoopGroup;
9 | import io.netty.channel.socket.SocketChannel;
10 | import io.netty.channel.socket.nio.NioServerSocketChannel;
11 | import io.netty.handler.codec.string.StringDecoder;
12 | import io.netty.handler.codec.string.StringEncoder;
13 |
14 | /**
15 | * @author D瓜哥, https://www.diguage.com/
16 | * @since 2020-06-29 16:59
17 | */
18 | public class NettyServer {
19 | /**
20 | * 完成对 NettyServer 的初始化和启动
21 | */
22 | public static void startServer(String hostname, int port) {
23 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
24 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(8);
25 | try {
26 | ServerBootstrap bootstrap = new ServerBootstrap();
27 | bootstrap.group(bossGroup, workerGroup)
28 | .channel(NioServerSocketChannel.class)
29 | .option(ChannelOption.SO_BACKLOG, 128)
30 | .childHandler(new ChannelInitializer() {
31 | @Override
32 | protected void initChannel(SocketChannel ch) throws Exception {
33 | ChannelPipeline pipeline = ch.pipeline();
34 | pipeline.addLast(new StringDecoder());
35 | pipeline.addLast(new StringEncoder());
36 | pipeline.addLast(new NettyServerHandler());
37 | }
38 | });
39 | ChannelFuture future = bootstrap.bind(hostname, port).sync();
40 | System.out.println("服务器启动成功,服务端开始提供服务");
41 | future.channel().closeFuture().sync();
42 | } catch (Exception e) {
43 |
44 | } finally {
45 | bossGroup.shutdownGracefully();
46 | workerGroup.shutdownGracefully();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/netty/NettyServerHandler.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.netty;
2 |
3 | import com.diguage.truman.netty.dubbo.provider.HelloServiceImpl;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.ChannelInboundHandlerAdapter;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-06-29 17:03
10 | */
11 | public class NettyServerHandler extends ChannelInboundHandlerAdapter {
12 | @Override
13 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
14 | // 获取客户端发送的消息,并调用服务
15 | System.out.println("msg=" + msg);
16 | // 客户端在调用服务器的 API 时,需要定义一个协议
17 | // 比如每次发消息时,都必须以某个字符串开头 "#Hello#"
18 | String prefix = "#Hello#";
19 | if (msg.toString().startsWith(prefix)) {
20 | String result = new HelloServiceImpl().hello(msg.toString().substring(prefix.length()));
21 | ctx.writeAndFlush(result);
22 | }
23 | }
24 |
25 | @Override
26 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
27 | ctx.close();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/provider/DubboServerBootstrap.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.provider;
2 |
3 | import com.diguage.truman.netty.dubbo.netty.NettyServer;
4 |
5 | /**
6 | * @author D瓜哥, https://www.diguage.com/
7 | * @since 2020-06-29 16:58
8 | */
9 | public class DubboServerBootstrap {
10 | public static void main(String[] args) {
11 | NettyServer.startServer("127.0.0.1", 11911);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/dubbo/provider/HelloServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.dubbo.provider;
2 |
3 | import com.diguage.truman.netty.dubbo.interfaces.HelloService;
4 |
5 | /**
6 | * @author D瓜哥, https://www.diguage.com/
7 | * @since 2020-06-29 16:55
8 | */
9 | public class HelloServiceImpl implements HelloService {
10 |
11 | private static int count = 0;
12 |
13 | @Override
14 | public String hello(String msg) {
15 | System.out.println("接收到客户端消息=" + msg);
16 | if (msg != null) {
17 | return "您好,D瓜哥!我接收到了你的消息[" + msg + "] 第" + (++this.count) + "次";
18 | } else {
19 | return "对不起!没有收到你的消息!";
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/hearts/MyServer.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.hearts;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.ChannelPipeline;
7 | import io.netty.channel.nio.NioEventLoopGroup;
8 | import io.netty.channel.socket.SocketChannel;
9 | import io.netty.channel.socket.nio.NioServerSocketChannel;
10 | import io.netty.handler.logging.LogLevel;
11 | import io.netty.handler.logging.LoggingHandler;
12 | import io.netty.handler.timeout.IdleStateHandler;
13 |
14 | import java.util.concurrent.TimeUnit;
15 |
16 | /**
17 | * @author D瓜哥, https://www.diguage.com/
18 | * @since 2020-06-28 14:25
19 | */
20 | public class MyServer {
21 | public static void main(String[] args) throws InterruptedException {
22 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
23 | NioEventLoopGroup workerGroup = new NioEventLoopGroup();
24 | try {
25 | ServerBootstrap serverBootstrap = new ServerBootstrap();
26 | serverBootstrap.group(bossGroup, workerGroup)
27 | .channel(NioServerSocketChannel.class)
28 | .handler(new LoggingHandler(LogLevel.INFO)) // 在 bossGroup 增加一个日志处理器
29 | .childHandler(new ChannelInitializer() {
30 | /**
31 | * 加入一个 Netty 提供的 IdleStateHandler
32 | *
33 | * 说明:
34 | *
35 | * 1. IdleStateHandler 是 Netty 提供的处理空闲状态的处理器
36 | * 2. long readerIdleTime 表示多长时间没有读取,就会发送一个心跳检查包,检查是否是连接状态
37 | * 3. long writerIdleTime 表示多长时间没有写,就会发送一个心跳检查包,检查是否是连接状态
38 | * 4. long allIdleTime 表示多长时间没有读写,就会发送一个心跳检查包,检查是否是连接状态
39 | *
40 | * 当 IdleStateHandler 触发后,就会传递给管道的下一个 handler 去处理
41 | * 通过调用(触发)下一个 handler 的 userEventTriggered,在该方法中处理 IdleStateEvent(读空闲,写空闲,读写空闲)
42 | */
43 | @Override
44 | protected void initChannel(SocketChannel ch) throws Exception {
45 | ChannelPipeline pipeline = ch.pipeline();
46 | // 调整参数,显示不同的事件
47 | pipeline.addLast(new IdleStateHandler(13, 15, 7, TimeUnit.SECONDS));
48 | // 加入一个对空闲检测进一步处理的 handler
49 | pipeline.addLast(new MyServerHandler());
50 | }
51 | });
52 |
53 | ChannelFuture future = serverBootstrap.bind(11911).sync();
54 | future.channel().closeFuture().sync();
55 | } finally {
56 | bossGroup.shutdownGracefully();
57 | workerGroup.shutdownGracefully();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/hearts/MyServerHandler.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.hearts;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.ChannelInboundHandlerAdapter;
5 | import io.netty.handler.timeout.IdleStateEvent;
6 |
7 | /**
8 | * @author D瓜哥, https://www.diguage.com/
9 | * @since 2020-06-28 14:45
10 | */
11 | public class MyServerHandler extends ChannelInboundHandlerAdapter {
12 | @Override
13 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
14 | if (evt instanceof IdleStateEvent) {
15 | IdleStateEvent event = (IdleStateEvent) evt;
16 | String eventType = null;
17 | switch (event.state()) {
18 | case READER_IDLE:
19 | eventType = "读空闲";
20 | break;
21 | case WRITER_IDLE:
22 | eventType = "写空闲";
23 | break;
24 | case ALL_IDLE:
25 | eventType = "读写空闲";
26 | break;
27 | }
28 | System.out.println(ctx.channel().remoteAddress() + " --超时时间--" + eventType);
29 | System.out.println("服务器做相应处理");
30 | //比如:如果发生空闲,我们关闭通道
31 | ctx.channel().close();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/http/TestServer.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.http;
2 |
3 |
4 | import io.netty.bootstrap.ServerBootstrap;
5 | import io.netty.channel.ChannelFuture;
6 | import io.netty.channel.nio.NioEventLoopGroup;
7 | import io.netty.channel.socket.nio.NioServerSocketChannel;
8 | import org.junit.jupiter.api.Test;
9 |
10 | /**
11 | * @author D瓜哥, https://www.diguage.com/
12 | * @since 2020-06-27 23:33
13 | */
14 | public class TestServer {
15 |
16 | @Test
17 | public void test() throws InterruptedException {
18 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
19 | NioEventLoopGroup workerGroup = new NioEventLoopGroup();
20 | try {
21 | ServerBootstrap bootstrap = new ServerBootstrap();
22 | bootstrap.group(bossGroup, workerGroup)
23 | .channel(NioServerSocketChannel.class)
24 | .childHandler(new TestServerInitializer());
25 |
26 | ChannelFuture future = bootstrap.bind(11911).sync();
27 |
28 | future.channel().closeFuture().sync();
29 | } finally {
30 | bossGroup.shutdownGracefully();
31 | workerGroup.shutdownGracefully();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/http/TestServerHandler.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.http;
2 |
3 | import io.netty.buffer.ByteBuf;
4 | import io.netty.buffer.Unpooled;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.SimpleChannelInboundHandler;
7 | import io.netty.handler.codec.http.DefaultFullHttpResponse;
8 | import io.netty.handler.codec.http.FullHttpResponse;
9 | import io.netty.handler.codec.http.HttpHeaderNames;
10 | import io.netty.handler.codec.http.HttpObject;
11 | import io.netty.handler.codec.http.HttpRequest;
12 | import io.netty.handler.codec.http.HttpResponseStatus;
13 | import io.netty.handler.codec.http.HttpVersion;
14 |
15 | import java.net.URI;
16 |
17 | import static java.nio.charset.StandardCharsets.UTF_8;
18 |
19 | /**
20 | * 说明
21 | *
22 | * 1. SimpleChannelInboundHandler 就是 ChannelInboundHandlerAdapter 的子类
23 | * 2. HttpObject 客户端和服务器端相互同学的数据被封装成 HttpObject。
24 | *
25 | * @author D瓜哥, https://www.diguage.com/
26 | * @since 2020-06-27 23:33
27 | */
28 | public class TestServerHandler extends SimpleChannelInboundHandler {
29 | /**
30 | * 读取客户端数据
31 | */
32 | @Override
33 | protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
34 | // 判断 msg 是不是一个 HttpRequest 请求
35 | if (msg instanceof HttpRequest) {
36 |
37 | System.out.println("ctx 类型 " + ctx.getClass().getName());
38 |
39 | System.out.println("pipeline hashcode=" + ctx.pipeline().hashCode()
40 | + " TestServerHandler hash=" + this.hashCode());
41 |
42 | System.out.println("msg 类型 " + msg.getClass());
43 | System.out.println("客户端地址 " + ctx.channel().remoteAddress());
44 |
45 | HttpRequest httpRequest = (HttpRequest) msg;
46 | URI uri = new URI(httpRequest.uri());
47 | if ("/favicon.ico".equals(uri.getPath())) {
48 | System.out.println("请求了 favicon.ico,不做响应");
49 | return;
50 | }
51 |
52 | // 回复信息给浏览器(http协议)
53 | ByteBuf content = Unpooled.copiedBuffer("Hello, D瓜哥。我是服务器", UTF_8);
54 | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
55 | response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
56 | response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
57 |
58 | // 将构建好的 response 返回
59 | ctx.writeAndFlush(response);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/http/TestServerInitializer.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.http;
2 |
3 | import io.netty.channel.ChannelInitializer;
4 | import io.netty.channel.ChannelPipeline;
5 | import io.netty.channel.socket.SocketChannel;
6 | import io.netty.handler.codec.http.HttpServerCodec;
7 |
8 | /**
9 | * @author D瓜哥, https://www.diguage.com/
10 | * @since 2020-06-27 23:33
11 | */
12 | public class TestServerInitializer extends ChannelInitializer {
13 | @Override
14 | protected void initChannel(SocketChannel ch) throws Exception {
15 | // 向管道加入处理器
16 | // 得到管道
17 | ChannelPipeline pipeline = ch.pipeline();
18 | // 加入一个 Netty 提供的 HttpServerCodec
19 | // HttpServerCodec 的说明
20 | // 1. HttpServerCodec 是 Netty 提供的处理 HTTP 的编解码器
21 | pipeline.addLast("MyHttpServerCodec", new HttpServerCodec());
22 | // 2. 增加一个自定义的 Handler
23 | pipeline.addLast("MyTestServerHandler", new TestServerHandler());
24 |
25 | System.out.println(pipeline);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/diguage/truman/netty/iobound/ByteToLongDecoder.java:
--------------------------------------------------------------------------------
1 | package com.diguage.truman.netty.iobound;
2 |
3 | import io.netty.buffer.ByteBuf;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.handler.codec.ByteToMessageDecoder;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @author D瓜哥, https://www.diguage.com/
11 | * @since 2020-06-28 19:53
12 | */
13 | public class ByteToLongDecoder extends ByteToMessageDecoder {
14 | /**
15 | * decode 会根据接收的数据,被调用多次,直到确定没有新的元素被添加到 list 或者 `ByteBuf` 没有更多的可读字节为止。
16 | *
17 | * 如果 list out 不为空,就会将 List 的内容传递给下一个 ChannelInboundHandler 处理,该处理器方法也会被调用多次。
18 | *
19 | * @param ctx 上下文对象
20 | * @param in 入站的 ByteBuf
21 | * @param out List 集合,将解码后的数据传给下一个 handler
22 | * @throws Exception
23 | */
24 | @Override
25 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List