├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.adoc ├── cfg ├── fonts │ └── Symbola.ttf ├── gems │ └── .gitkeep ├── plantuml.cfg └── theme │ └── Source-theme.yml ├── docs ├── .asciidoctorconfig ├── diagram │ └── ack.puml ├── docinfo.html ├── images │ ├── AbstractQueuedSynchronizer-CLH-queue.png │ ├── BlockingQueue.png │ ├── ConcurrentHashMap-segment-lock.png │ ├── ConcurrentSkipListMap-search.jpg │ ├── CountDownLatch-await-park.png │ ├── CountDownLatch-await-unpark.png │ ├── CountDownLatch-countDown-1.png │ ├── CountDownLatch-countDown-2.png │ ├── DiagramForLinkedList.png │ ├── ForkJoinPool-ctl.png │ ├── ForkJoinPool-data-structures.png │ ├── ForkJoinPool-fork-join.webp │ ├── ForkJoinPool-invoke-link.png │ ├── ForkJoinPool-work-stealing.webp │ ├── Hashtable-lock.png │ ├── JDK1.8-ConcurrentHashMap-Structure.jpg │ ├── LinkedHashMap.png │ ├── PriorityQueue-offer.png │ ├── PriorityQueue-poll.png │ ├── PriorityQueue-remove2.png │ ├── Proxy-newProxyInstance-sequence-diagram.png │ ├── ThreadLocal-set-get.jpeg │ ├── ThreadPoolExecutor-process.jpeg │ ├── ThreadPoolExecutor-process.png │ ├── ThreadPoolExecutor-runWorker.jpeg │ ├── ThreadPoolExecutor-states.jpeg │ ├── alipay.png │ ├── compareListLoop.png │ ├── hashmap_put.png │ ├── java-collections-overview.png │ ├── java-concurrent-overview.png │ ├── java.util.Collection.png │ ├── java.util.Map.png │ ├── jdk-collection-classes.png │ ├── jdk1.8hashmap.png │ ├── jvm-thread-to-os-thread.jpeg │ ├── linkedlist-insert-node.png │ ├── linkedlist-remove-node.png │ ├── load-class-process.png │ ├── map_structure.png │ ├── redis │ │ ├── redis-quicklist-structure.png │ │ ├── redis-skiplist-insertions.png │ │ ├── redis-ziplist-sample.png │ │ ├── redis-ziplist-structure.jpg │ │ ├── redis_skiplist_example.png │ │ └── skiplist.png │ ├── simplex-half-duplex-full-duplex.jpg │ ├── thread-lifecycle.jpeg │ ├── thread-states.jpeg │ ├── time-wheel.png │ ├── wx-jikerizhi.png │ ├── wxpay.jpg │ └── wxpay.png ├── index.adoc ├── io.netty.adoc ├── java.collection.adoc ├── java.io.Serializable.adoc ├── java.lang.ClassLoader.adoc ├── java.lang.String.adoc ├── java.lang.Thread.adoc ├── java.lang.ThreadLocal.adoc ├── java.lang.reflect.Field.adoc ├── java.lang.reflect.Proxy.adoc ├── java.net.ServerSocket.adoc ├── java.util.AbstractCollection.adoc ├── java.util.AbstractList.adoc ├── java.util.AbstractMap.adoc ├── java.util.AbstractQueue.adoc ├── java.util.AbstractSequentialList.adoc ├── java.util.AbstractSet.adoc ├── java.util.ArrayDeque.adoc ├── java.util.ArrayList.adoc ├── java.util.Arrays.adoc ├── java.util.BitSet.adoc ├── java.util.Collection.adoc ├── java.util.Collections.adoc ├── java.util.Date.adoc ├── java.util.Deque.adoc ├── java.util.Dictionary.adoc ├── java.util.EnumMap.adoc ├── java.util.EnumSet.adoc ├── java.util.HashMap.adoc ├── java.util.HashSet.adoc ├── java.util.Hashtable.adoc ├── java.util.IdentityHashMap.adoc ├── java.util.Iterator.adoc ├── java.util.LinkedHashMap.adoc ├── java.util.LinkedHashSet.adoc ├── java.util.LinkedList.adoc ├── java.util.List.adoc ├── java.util.Map.adoc ├── java.util.NavigableMap.adoc ├── java.util.NavigableSet.adoc ├── java.util.PriorityQueue.adoc ├── java.util.Queue.adoc ├── java.util.ServiceLoader.adoc ├── java.util.Set.adoc ├── java.util.SortedMap.adoc ├── java.util.SortedSet.adoc ├── java.util.Stack.adoc ├── java.util.TreeMap.adoc ├── java.util.TreeSet.adoc ├── java.util.Vector.adoc ├── java.util.WeakHashMap.adoc ├── java.util.concurrent.ArrayBlockingQueue.adoc ├── java.util.concurrent.CompletableFuture.adoc ├── java.util.concurrent.ConcurrentHashMap.adoc ├── java.util.concurrent.ConcurrentLinkedQueue.adoc ├── java.util.concurrent.ConcurrentSkipListMap.adoc ├── java.util.concurrent.CopyOnWriteArrayList.adoc ├── java.util.concurrent.CountDownLatch.adoc ├── java.util.concurrent.CyclicBarrier.adoc ├── java.util.concurrent.DelayQueue.adoc ├── java.util.concurrent.Exchanger.adoc ├── java.util.concurrent.Flow.adoc ├── java.util.concurrent.ForkJoinPool.adoc ├── java.util.concurrent.ForkJoinTask.adoc ├── java.util.concurrent.Future.adoc ├── java.util.concurrent.FutureTask.adoc ├── java.util.concurrent.LinkedBlockingQueue.adoc ├── java.util.concurrent.Phaser.adoc ├── java.util.concurrent.PriorityBlockingQueue.adoc ├── java.util.concurrent.ScheduledThreadPoolExecutor.adoc ├── java.util.concurrent.Semaphore.adoc ├── java.util.concurrent.ThreadPoolExecutor.adoc ├── java.util.concurrent.adoc ├── java.util.concurrent.atomic.AtomicInteger.adoc ├── java.util.concurrent.atomic.LongAdder.adoc ├── java.util.concurrent.locks.AbstractQueuedSynchronizer.adoc ├── java.util.concurrent.locks.LockSupport.adoc ├── java.util.concurrent.locks.ReentrantLock.adoc ├── java.util.concurrent.locks.ReentrantReadWriteLock.adoc ├── java.util.concurrent.locks.StampedLock.adoc ├── java.util.regex.Pattern.adoc └── preface.adoc ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── diguage │ │ │ └── truman │ │ │ ├── ArrayListAddTest.java │ │ │ ├── ArrayListBaseTest.java │ │ │ ├── ArrayListIteratorSpeedTest.java │ │ │ ├── ArrayListRemoveTest.java │ │ │ ├── ArrayTest.java │ │ │ ├── BloomFilterTest.java │ │ │ ├── ClassLoaderTest.java │ │ │ ├── CollectionTest.java │ │ │ ├── ComparatorTest.java │ │ │ ├── ConcurrentSkipListMapTest.java │ │ │ ├── DateFormatTest.java │ │ │ ├── EmptyStringEquals.java │ │ │ ├── HashMapTest.java │ │ │ ├── LinkedListBaseTest.java │ │ │ ├── LinkedListTest.java │ │ │ ├── ListSortTest.java │ │ │ ├── LongTest.java │ │ │ ├── OuterClass.java │ │ │ ├── PriorityQueueTest.java │ │ │ ├── ServiceLoaderSay.java │ │ │ ├── ServiceLoaderSayGoodbye.java │ │ │ ├── ServiceLoaderSayHello.java │ │ │ ├── ServiceLoaderTest.java │ │ │ ├── StringTest.java │ │ │ ├── StringUtilTest.java │ │ │ ├── StringUtils.java │ │ │ ├── ToolMain.java │ │ │ ├── TreeMapTest.java │ │ │ ├── bytebuddy │ │ │ └── ByteBuddyTest.java │ │ │ ├── concurrent │ │ │ ├── AbstractQueuedSynchronizerTest.java │ │ │ ├── ArrayBlockingQueueTest.java │ │ │ ├── CallableTest.java │ │ │ ├── CompletableFutureTest.java │ │ │ ├── CompletedFutureTest.java │ │ │ ├── ConcurrentMapTest.java │ │ │ ├── CountDownLatchTest.java │ │ │ ├── CyclicBarrierTest.java │ │ │ ├── DelayQueueTest.java │ │ │ ├── FilesSpy.java │ │ │ ├── FlowTest.java │ │ │ ├── ForkJoinPoolTest.java │ │ │ ├── JolTest.java │ │ │ ├── LockSupportTest.java │ │ │ ├── LongAdderTest.java │ │ │ ├── Mutex.java │ │ │ ├── ReentrantLockTest.java │ │ │ ├── ReentrantReadWriteLockTest.java │ │ │ ├── ScheduledThreadPoolExecutorTest.java │ │ │ ├── SemaphoreTest.java │ │ │ ├── StampedLockTest.java │ │ │ ├── SynchronizedTest.java │ │ │ ├── ThreadLocalTest.java │ │ │ ├── ThreadMain.java │ │ │ ├── ThreadPoolExecutorTest.java │ │ │ └── ThreadTest.java │ │ │ ├── io │ │ │ └── FileTest.java │ │ │ ├── jgroups │ │ │ ├── ChatClient01.java │ │ │ ├── ChatClient02.java │ │ │ ├── SimpleChat.java │ │ │ ├── SimpleChat2.java │ │ │ └── SimpleChat4.java │ │ │ ├── math │ │ │ └── BigDecimalTest.java │ │ │ ├── net │ │ │ ├── ServerSockerChannelTest.java │ │ │ ├── SocketFullDuplexTest.java │ │ │ └── SocketHalfCloseTest.java │ │ │ ├── netty │ │ │ ├── NettyTest.java │ │ │ ├── ServerSocketTest.java │ │ │ ├── Test01.java │ │ │ ├── Test03.java │ │ │ ├── buf │ │ │ │ └── NettyByteBuf.java │ │ │ ├── chats │ │ │ │ ├── GroupChatClient.java │ │ │ │ ├── GroupChatClientHandler.java │ │ │ │ ├── GroupChatServer.java │ │ │ │ └── GroupChatServerHandler.java │ │ │ ├── dubbo │ │ │ │ ├── consumer │ │ │ │ │ └── DubboClientBootstrap.java │ │ │ │ ├── interfaces │ │ │ │ │ └── HelloService.java │ │ │ │ ├── netty │ │ │ │ │ ├── NettyClient.java │ │ │ │ │ ├── NettyClientHandler.java │ │ │ │ │ ├── NettyServer.java │ │ │ │ │ └── NettyServerHandler.java │ │ │ │ └── provider │ │ │ │ │ ├── DubboServerBootstrap.java │ │ │ │ │ └── HelloServiceImpl.java │ │ │ ├── hearts │ │ │ │ ├── MyServer.java │ │ │ │ └── MyServerHandler.java │ │ │ ├── http │ │ │ │ ├── TestServer.java │ │ │ │ ├── TestServerHandler.java │ │ │ │ └── TestServerInitializer.java │ │ │ ├── iobound │ │ │ │ ├── ByteToLongDecoder.java │ │ │ │ ├── ByteToLongDecoder2.java │ │ │ │ ├── IoClient.java │ │ │ │ ├── IoClientHandler.java │ │ │ │ ├── IoClientInitializer.java │ │ │ │ ├── IoServer.java │ │ │ │ ├── IoServerHandler.java │ │ │ │ ├── IoServerInitializer.java │ │ │ │ └── LongToByteEncoder.java │ │ │ ├── nia │ │ │ │ └── ch02 │ │ │ │ │ └── EchoTest.java │ │ │ ├── protobuf │ │ │ │ ├── ClientHandler.java │ │ │ │ ├── ProtobufClient.java │ │ │ │ ├── ProtobufServer.java │ │ │ │ ├── ServerHandler.java │ │ │ │ ├── Student.proto │ │ │ │ └── StudentPOJO.java │ │ │ ├── protobuf2 │ │ │ │ ├── ClientHandler.java │ │ │ │ ├── MyDataInfo.java │ │ │ │ ├── ProtobufClient2.java │ │ │ │ ├── ProtobufServer2.java │ │ │ │ ├── ServerHandler.java │ │ │ │ └── Student.proto │ │ │ ├── simple │ │ │ │ ├── NettyClient.java │ │ │ │ ├── NettyClientHandler.java │ │ │ │ ├── NettyServer.java │ │ │ │ └── NettyServerHandler.java │ │ │ ├── tcp │ │ │ │ ├── TcpClient.java │ │ │ │ ├── TcpClientHandler.java │ │ │ │ ├── TcpClientInitializer.java │ │ │ │ ├── TcpServer.java │ │ │ │ ├── TcpServerHandler.java │ │ │ │ └── TcpServerInitializer.java │ │ │ ├── tcprotocol │ │ │ │ ├── MessageDecoder.java │ │ │ │ ├── MessageEncoder.java │ │ │ │ ├── MessageProtocol.java │ │ │ │ ├── TcpClientHandler.java │ │ │ │ ├── TcpClientInitializer.java │ │ │ │ ├── TcpServerHandler.java │ │ │ │ ├── TcpServerInitializer.java │ │ │ │ ├── TcprotocolClient.java │ │ │ │ └── TcprotocolServer.java │ │ │ └── ws │ │ │ │ ├── Server.java │ │ │ │ ├── TextWebSocketFrameHandler.java │ │ │ │ └── hello.html │ │ │ ├── okhttp │ │ │ └── ConnPoolTest.java │ │ │ ├── reflect │ │ │ ├── ProxyAnnoTest.java │ │ │ └── ProxyTest.java │ │ │ ├── regex │ │ │ └── PatternTest.java │ │ │ ├── stream │ │ │ └── CollectorsTest.java │ │ │ └── time │ │ │ ├── FormatTest.java │ │ │ └── ZoneTest.java │ └── resources │ │ ├── META-INF │ │ ├── hessian │ │ │ └── serializers │ │ └── services │ │ │ └── com.diguage.truman.ServiceLoaderSay │ │ └── jgroups-chat-tcp.xml └── test │ └── java │ └── com │ └── diguage │ └── truman │ ├── GsonTest.java │ └── GsonUtils.java └── tools └── publish-to-pages.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{java,adoc,xml}] 14 | charset = utf-8 15 | 16 | # 2 space indentation 17 | [*.{java,xml}] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | # Matches the exact files either package.json or .travis.yml 22 | [.travis.yml] 23 | indent_style = space 24 | indent_size = 2 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | *.epub 4 | .classpath 5 | .project 6 | .settings/ 7 | .vscode/ 8 | .idea/ 9 | target 10 | *.iml 11 | .DS_Store 12 | *.svg 13 | *.html 14 | .factorypath 15 | .asciidoctor/ 16 | *.ttf 17 | *.otf 18 | /cfg/gems 19 | fonts.ttf.tgz 20 | 21 | *.icloud 22 | 23 | 24 | -------------------------------------------------------------------------------- /cfg/fonts/Symbola.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/cfg/fonts/Symbola.ttf -------------------------------------------------------------------------------- /cfg/gems/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/cfg/gems/.gitkeep -------------------------------------------------------------------------------- /cfg/plantuml.cfg: -------------------------------------------------------------------------------- 1 | skinparam defaultFontName Hiragino Sans GB -------------------------------------------------------------------------------- /docs/.asciidoctorconfig: -------------------------------------------------------------------------------- 1 | // this adds attributes to every preview being rendered in IntelliJ 2 | // for more information see: 3 | // https://intellij-asciidoc-plugin.ahus1.de/docs/users-guide/features/advanced/asciidoctorconfig-file.html 4 | 5 | :pdf-fontsdir: {asciidoctorconfigdir}/../cfg/fonts 6 | :pdf-stylesdir: {asciidoctorconfigdir}/../cfg/theme 7 | :pdf-theme: Source 8 | 9 | :icons: font 10 | 11 | :diagram_attr: format=svg,align="center",width=100% 12 | 13 | :sourcedir: {asciidoctorconfigdir}/../src/main/java/com/diguage/truman 14 | :source_attr: linenums,indent=0,subs="attributes,verbatim" 15 | :java_src_attr: source%nowrap,java,{source_attr} 16 | -------------------------------------------------------------------------------- /docs/diagram/ack.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | 4 | @enduml 5 | -------------------------------------------------------------------------------- /docs/docinfo.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/images/AbstractQueuedSynchronizer-CLH-queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/AbstractQueuedSynchronizer-CLH-queue.png -------------------------------------------------------------------------------- /docs/images/BlockingQueue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/BlockingQueue.png -------------------------------------------------------------------------------- /docs/images/ConcurrentHashMap-segment-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ConcurrentHashMap-segment-lock.png -------------------------------------------------------------------------------- /docs/images/ConcurrentSkipListMap-search.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ConcurrentSkipListMap-search.jpg -------------------------------------------------------------------------------- /docs/images/CountDownLatch-await-park.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/CountDownLatch-await-park.png -------------------------------------------------------------------------------- /docs/images/CountDownLatch-await-unpark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/CountDownLatch-await-unpark.png -------------------------------------------------------------------------------- /docs/images/CountDownLatch-countDown-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/CountDownLatch-countDown-1.png -------------------------------------------------------------------------------- /docs/images/CountDownLatch-countDown-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/CountDownLatch-countDown-2.png -------------------------------------------------------------------------------- /docs/images/DiagramForLinkedList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/DiagramForLinkedList.png -------------------------------------------------------------------------------- /docs/images/ForkJoinPool-ctl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ForkJoinPool-ctl.png -------------------------------------------------------------------------------- /docs/images/ForkJoinPool-data-structures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ForkJoinPool-data-structures.png -------------------------------------------------------------------------------- /docs/images/ForkJoinPool-fork-join.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ForkJoinPool-fork-join.webp -------------------------------------------------------------------------------- /docs/images/ForkJoinPool-invoke-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ForkJoinPool-invoke-link.png -------------------------------------------------------------------------------- /docs/images/ForkJoinPool-work-stealing.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ForkJoinPool-work-stealing.webp -------------------------------------------------------------------------------- /docs/images/Hashtable-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/Hashtable-lock.png -------------------------------------------------------------------------------- /docs/images/JDK1.8-ConcurrentHashMap-Structure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/JDK1.8-ConcurrentHashMap-Structure.jpg -------------------------------------------------------------------------------- /docs/images/LinkedHashMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/LinkedHashMap.png -------------------------------------------------------------------------------- /docs/images/PriorityQueue-offer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/PriorityQueue-offer.png -------------------------------------------------------------------------------- /docs/images/PriorityQueue-poll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/PriorityQueue-poll.png -------------------------------------------------------------------------------- /docs/images/PriorityQueue-remove2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/PriorityQueue-remove2.png -------------------------------------------------------------------------------- /docs/images/Proxy-newProxyInstance-sequence-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/Proxy-newProxyInstance-sequence-diagram.png -------------------------------------------------------------------------------- /docs/images/ThreadLocal-set-get.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ThreadLocal-set-get.jpeg -------------------------------------------------------------------------------- /docs/images/ThreadPoolExecutor-process.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ThreadPoolExecutor-process.jpeg -------------------------------------------------------------------------------- /docs/images/ThreadPoolExecutor-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ThreadPoolExecutor-process.png -------------------------------------------------------------------------------- /docs/images/ThreadPoolExecutor-runWorker.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ThreadPoolExecutor-runWorker.jpeg -------------------------------------------------------------------------------- /docs/images/ThreadPoolExecutor-states.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/ThreadPoolExecutor-states.jpeg -------------------------------------------------------------------------------- /docs/images/alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/alipay.png -------------------------------------------------------------------------------- /docs/images/compareListLoop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/compareListLoop.png -------------------------------------------------------------------------------- /docs/images/hashmap_put.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/hashmap_put.png -------------------------------------------------------------------------------- /docs/images/java-collections-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/java-collections-overview.png -------------------------------------------------------------------------------- /docs/images/java-concurrent-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/java-concurrent-overview.png -------------------------------------------------------------------------------- /docs/images/java.util.Collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/java.util.Collection.png -------------------------------------------------------------------------------- /docs/images/java.util.Map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/java.util.Map.png -------------------------------------------------------------------------------- /docs/images/jdk-collection-classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/jdk-collection-classes.png -------------------------------------------------------------------------------- /docs/images/jdk1.8hashmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/jdk1.8hashmap.png -------------------------------------------------------------------------------- /docs/images/jvm-thread-to-os-thread.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/jvm-thread-to-os-thread.jpeg -------------------------------------------------------------------------------- /docs/images/linkedlist-insert-node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/linkedlist-insert-node.png -------------------------------------------------------------------------------- /docs/images/linkedlist-remove-node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/linkedlist-remove-node.png -------------------------------------------------------------------------------- /docs/images/load-class-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/load-class-process.png -------------------------------------------------------------------------------- /docs/images/map_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/map_structure.png -------------------------------------------------------------------------------- /docs/images/redis/redis-quicklist-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/redis/redis-quicklist-structure.png -------------------------------------------------------------------------------- /docs/images/redis/redis-skiplist-insertions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/redis/redis-skiplist-insertions.png -------------------------------------------------------------------------------- /docs/images/redis/redis-ziplist-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/redis/redis-ziplist-sample.png -------------------------------------------------------------------------------- /docs/images/redis/redis-ziplist-structure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/redis/redis-ziplist-structure.jpg -------------------------------------------------------------------------------- /docs/images/redis/redis_skiplist_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/redis/redis_skiplist_example.png -------------------------------------------------------------------------------- /docs/images/redis/skiplist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/redis/skiplist.png -------------------------------------------------------------------------------- /docs/images/simplex-half-duplex-full-duplex.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/simplex-half-duplex-full-duplex.jpg -------------------------------------------------------------------------------- /docs/images/thread-lifecycle.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/thread-lifecycle.jpeg -------------------------------------------------------------------------------- /docs/images/thread-states.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/thread-states.jpeg -------------------------------------------------------------------------------- /docs/images/time-wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/time-wheel.png -------------------------------------------------------------------------------- /docs/images/wx-jikerizhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/wx-jikerizhi.png -------------------------------------------------------------------------------- /docs/images/wxpay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/wxpay.jpg -------------------------------------------------------------------------------- /docs/images/wxpay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diguage/jdk-source-analysis/89c17b721b12f06db718c5def4977935409bf5ba/docs/images/wxpay.png -------------------------------------------------------------------------------- /docs/java.collection.adoc: -------------------------------------------------------------------------------- 1 | [#overview] 2 | = 集合类概述 3 | 4 | image::images/java-collections-overview.png[] 5 | -------------------------------------------------------------------------------- /docs/java.io.Serializable.adoc: -------------------------------------------------------------------------------- 1 | [#io-Serializable] 2 | = `Serializable` 3 | 4 | [{java_src_attr}] 5 | ---- 6 | include::{sourcedir}/OuterClass.java[] 7 | ---- 8 | -------------------------------------------------------------------------------- /docs/java.lang.String.adoc: -------------------------------------------------------------------------------- 1 | [#lang-String] 2 | = `String` 3 | 4 | [{java_src_attr}] 5 | ---- 6 | include::{sourcedir}/StringUtils.java[] 7 | ---- 8 | 9 | 10 | [{java_src_attr}] 11 | ---- 12 | include::{sourcedir}/StringUtilTest.java[] 13 | ---- 14 | 15 | 16 | [{java_src_attr}] 17 | ---- 18 | include::{sourcedir}/StringTest.java[] 19 | ---- 20 | -------------------------------------------------------------------------------- /docs/java.lang.reflect.Field.adoc: -------------------------------------------------------------------------------- 1 | [#reflect-Field] 2 | = Field 3 | 4 | 属性操作方法 `Field#set(Object obj, Object value)` 和 `Field#get(Object obj)` 底层都是委托到 `jdk.internal.reflect.FieldAccessor` 实现。 5 | 6 | `FieldAccessor` 接口有很多的实现,`FieldAccessor` 接口实例是通过 `jdk.internal.reflect.ReflectionFactory` 这个工厂构造的: 7 | 8 | .jdk.internal.reflect.ReflectionFactory 9 | [{java_src_attr}] 10 | ---- 11 | public FieldAccessor newFieldAccessor(Field field, boolean override) { 12 | checkInitted(); 13 | 14 | Field root = langReflectAccess.getRoot(field); 15 | if (root != null) { 16 | // FieldAccessor will use the root unless the modifiers have 17 | // been overrridden 18 | if (root.getModifiers() == field.getModifiers() || !override) { 19 | field = root; 20 | } 21 | } 22 | return UnsafeFieldAccessorFactory.newFieldAccessor(field, override); 23 | } 24 | ---- 25 | 26 | 最终委托到 `UnsafeFieldAccessorFactory#newFieldAccessor()`。 27 | 28 | `UnsafeObjectFieldAccessorImpl` 中除了 `get(Object obj)` 和 `set(Object obj, Object value)` 方法,其他方法都是直接抛出 `IllegalArgumentException`。而 `get(Object obj)` 和 `set(Object obj, Object value)` 底层分别依赖于 `jdk.internal.misc.Unsafe` 的 `putObject(obj, fieldOffset, value)` 和 `getObject(obj, fieldOffset)` 方法。而属性的内存偏移地址是在 `UnsafeObjectFieldAccessorImpl` 的父类 `UnsafeFieldAccessorImpl` 的构造函数中计算出来的。 29 | 30 | 属性反射操作 `Field` 的 `setXX` 和 `getXX` 方法最终委托到 `jdk.internal.misc.Unsafe` 的 `putXX` 和 `getXX` 方法,而属性的内存偏移地址是通过 `jdk.internal.misc.Unsafe` 的 `staticFieldBase()`、`staticFieldOffset` 和 `objectFieldOffset` 几个方法计算的。 31 | 32 | == 参考资料 33 | 34 | . http://www.throwable.club/2018/12/16/java-reflection-implementance/[深入分析Java反射(七)-简述反射调用的底层实现 - Throwable's Blog] 35 | -------------------------------------------------------------------------------- /docs/java.net.ServerSocket.adoc: -------------------------------------------------------------------------------- 1 | [#net-ServerSocket] 2 | = ServerSocket 3 | 4 | 在纠结了 N 久之后,终于动手写程序完成了 Socket 全双工实验。实验证实,Socket 在接受的同时,还可以发送报文。 5 | 6 | image::images/simplex-half-duplex-full-duplex.jpg[] 7 | 8 | [{java_src_attr}] 9 | ---- 10 | include::{sourcedir}/net/SocketFullDuplexTest.java[] 11 | ---- 12 | 13 | image::image-2022-02-15-17-56-18-693.png[] 14 | 15 | image::image-2022-02-15-17-57-07-482.png[] 16 | 17 | == 参考资料 18 | 19 | . http://hafizahabdullah.blogspot.com/2013/08/fp303-cn-simplex-half-duplex-and-full.html[P2E for Students: FP303 CN: Simplex, Half-Duplex and Full-Duplex] 20 | -------------------------------------------------------------------------------- /docs/java.util.AbstractCollection.adoc: -------------------------------------------------------------------------------- 1 | [#util-AbstractCollection] 2 | = AbstractCollection 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.AbstractList.adoc: -------------------------------------------------------------------------------- 1 | [#util-AbstractList] 2 | = AbstractList 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.AbstractMap.adoc: -------------------------------------------------------------------------------- 1 | [#util-AbstractMap] 2 | = AbstractMap 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/java.util.AbstractQueue.adoc: -------------------------------------------------------------------------------- 1 | [#util-AbstractQueue] 2 | = AbstractQueue 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.AbstractSequentialList.adoc: -------------------------------------------------------------------------------- 1 | [#util-AbstractSequentialList] 2 | = AbstractSequentialList 3 | 4 | == 类图 5 | 6 | 先来看一下 `AbstractSequentialList` 的类图: 7 | 8 | [plantuml,{diagram_attr}] 9 | .... 10 | @startuml 11 | skinparam nodesep 100 12 | 'skinparam ranksep 60 13 | 14 | title AbstractSequentialList 15 | 16 | interface Iterable 17 | 18 | interface Collection extends Iterable 19 | 20 | abstract class AbstractCollection implements Collection 21 | 22 | interface List extends Collection 23 | 24 | interface Queue extends Collection 25 | 26 | interface Deque extends Queue 27 | 28 | abstract class AbstractList extends AbstractCollection implements List 29 | 30 | abstract class AbstractSequentialList extends AbstractList 31 | 32 | @enduml 33 | .... 34 | 35 | `AbstractSequentialList` 是 xref:java.util.LinkedList.adoc[`java.util.LinkedList`] 的父类,主要是基于 xref:java.util.Iterator.adoc#ListIterator[`java.util.ListIterator`] 实现了 36 | 37 | * `get(int index)` 38 | * `set(int index, E element)` 39 | * `add(int index, E element)` 40 | * `remove(int index)` 41 | * `addAll(int index, Collection c)` 42 | 43 | 等与具体坐标相关的随机访问 List 的方法。 44 | -------------------------------------------------------------------------------- /docs/java.util.AbstractSet.adoc: -------------------------------------------------------------------------------- 1 | [#util-AbstractSet] 2 | = AbstractSet 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.ArrayDeque.adoc: -------------------------------------------------------------------------------- 1 | [#util-ArrayDeque] 2 | = ArrayDeque 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.Arrays.adoc: -------------------------------------------------------------------------------- 1 | [#util-Arrays] 2 | = 工具类 `Arrays` 3 | -------------------------------------------------------------------------------- /docs/java.util.BitSet.adoc: -------------------------------------------------------------------------------- 1 | [#util-BitSet] 2 | = BitSet 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.Collection.adoc: -------------------------------------------------------------------------------- 1 | [#util-Collection] 2 | = Collection 3 | 4 | 有一个问题,我们思考一下:如果让你设计 JDK 集合框架,你会怎么设计?说的更具体一些,现在需要一个可以包含重复对象的 `Aggregation` 集合,请问怎么设计? 5 | 6 | 可以先设想一下,有哪些操作? 7 | 8 | . 添加元素 `add()` 9 | . 删除元素 `remove()` 10 | . 是否包含元素 `boolean contain(Element e)` 11 | . 列表大小 `int size()` 12 | . 添加整个 `Bag` 元素 `addAll(Aggregation aggregation)` 13 | . 清空 `clear()` 14 | . 迭代器 `Iterator iterator()` 15 | . 和数组互操作: `toArray()` 和 `addAll(T[] array)` 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | .java.util.Collection 38 | [source,java,linenums] 39 | ---- 40 | public interface Collection extends Iterable { 41 | // Query Operations 42 | 43 | int size(); 44 | 45 | boolean isEmpty(); 46 | 47 | boolean contains(Object o); 48 | 49 | Iterator iterator(); 50 | 51 | Object[] toArray(); 52 | 53 | T[] toArray(T[] a); 54 | 55 | // Modification Operations 56 | 57 | boolean add(E e); 58 | 59 | boolean remove(Object o); 60 | 61 | 62 | // Bulk Operations 63 | 64 | boolean containsAll(Collection c); 65 | 66 | boolean addAll(Collection c); 67 | 68 | boolean removeAll(Collection c); 69 | 70 | /** 71 | * @since 1.8 72 | */ 73 | default boolean removeIf(Predicate filter) { 74 | Objects.requireNonNull(filter); 75 | boolean removed = false; 76 | final Iterator each = iterator(); 77 | while (each.hasNext()) { 78 | if (filter.test(each.next())) { 79 | each.remove(); 80 | removed = true; 81 | } 82 | } 83 | return removed; 84 | } 85 | 86 | boolean retainAll(Collection c); 87 | 88 | void clear(); 89 | 90 | 91 | // Comparison and hashing 92 | 93 | boolean equals(Object o); 94 | 95 | int hashCode(); 96 | 97 | /** 98 | * @since 1.8 99 | */ 100 | @Override 101 | default Spliterator spliterator() { 102 | return Spliterators.spliterator(this, 0); 103 | } 104 | 105 | /** 106 | * @since 1.8 107 | */ 108 | default Stream stream() { 109 | return StreamSupport.stream(spliterator(), false); 110 | } 111 | 112 | /** 113 | * @since 1.8 114 | */ 115 | default Stream parallelStream() { 116 | return StreamSupport.stream(spliterator(), true); 117 | } 118 | } 119 | ---- 120 | -------------------------------------------------------------------------------- /docs/java.util.Collections.adoc: -------------------------------------------------------------------------------- 1 | [#util-Collections] 2 | = 工具类 `Collections` 3 | -------------------------------------------------------------------------------- /docs/java.util.Date.adoc: -------------------------------------------------------------------------------- 1 | [#util-Date] 2 | = `Date` 3 | 4 | [{java_src_attr}] 5 | ---- 6 | include::{sourcedir}/DateFormatTest.java[] 7 | ---- 8 | -------------------------------------------------------------------------------- /docs/java.util.Deque.adoc: -------------------------------------------------------------------------------- 1 | [#util-Deque] 2 | = Deque 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.Dictionary.adoc: -------------------------------------------------------------------------------- 1 | [#util-Dictionary] 2 | = Dictionary 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.EnumMap.adoc: -------------------------------------------------------------------------------- 1 | [#util-EnumMap] 2 | = EnumMap 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.EnumSet.adoc: -------------------------------------------------------------------------------- 1 | [#util-EnumSet] 2 | = EnumSet 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.HashSet.adoc: -------------------------------------------------------------------------------- 1 | [#util-HashSet] 2 | = HashSet 3 | 4 | == Redis 中的 Set 5 | 6 | Redis 中的集合对象编码可以是: 7 | 8 | . intset 9 | . hashtable 10 | 11 | 转换的条件是: 12 | 13 | . 集合对象保存的所有元素都是整数值; 14 | . 集合对象保存的元素个数不超过 512 个;(通过参数 `set-max-intset-entries` 来调整,默认是 512) 15 | 16 | [source,bash,{source_attr}] 17 | ---- 18 | 127.0.0.1:6379> SADD num 1 3 5 19 | (integer) 3 20 | 127.0.0.1:6379> OBJECT encoding num 21 | "intset" 22 | 23 | 127.0.0.1:6379> sadd num "seven" 24 | (integer) 1 25 | 127.0.0.1:6379> OBJECT encoding num 26 | "hashtable" 27 | ---- 28 | 29 | 在 `t_set.c/setTypeConvert` 中执行转换操作。 30 | -------------------------------------------------------------------------------- /docs/java.util.Hashtable.adoc: -------------------------------------------------------------------------------- 1 | [#util-Hashtable] 2 | = Hashtable 3 | 4 | image::images/Hashtable-lock.png[] 5 | 6 | == 参考资料 7 | 8 | * https://www.cnblogs.com/chengxiao/p/6842045.html[ConcurrentHashMap实现原理及源码分析 - dreamcatcher-cx - 博客园] 9 | -------------------------------------------------------------------------------- /docs/java.util.IdentityHashMap.adoc: -------------------------------------------------------------------------------- 1 | [#util-IdentityHashMap] 2 | = IdentityHashMap 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.LinkedHashMap.adoc: -------------------------------------------------------------------------------- 1 | [#util-LinkedHashMap] 2 | = LinkedHashMap 3 | 4 | 5 | image::images/LinkedHashMap.png[] 6 | 7 | 问题: 8 | 9 | . `new LinkedHashMap<>(10, 0.75F, true)` 和 `new LinkedHashMap<>()` 除了容量之外,还有什么差别吗? 10 | -------------------------------------------------------------------------------- /docs/java.util.LinkedHashSet.adoc: -------------------------------------------------------------------------------- 1 | [#util-LinkedHashSet] 2 | = LinkedHashSet 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.List.adoc: -------------------------------------------------------------------------------- 1 | [#util-List] 2 | = List 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.Map.adoc: -------------------------------------------------------------------------------- 1 | [#util-Map] 2 | = Map 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.NavigableMap.adoc: -------------------------------------------------------------------------------- 1 | [#util-NavigableMap] 2 | = NavigableMap 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.NavigableSet.adoc: -------------------------------------------------------------------------------- 1 | [#util-NavigableSet] 2 | = NavigableSet 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.PriorityQueue.adoc: -------------------------------------------------------------------------------- 1 | [#util-PriorityQueue] 2 | = PriorityQueue 3 | 4 | 对于 `PriorityQueue` 来说,最重要的一点就是要清楚他是基于堆结构实现,可以用它来实现优先队列。 5 | 6 | 7 | image::images/PriorityQueue-offer.png[] 8 | 9 | image::images/PriorityQueue-poll.png[] 10 | 11 | image::images/PriorityQueue-remove2.png[] 12 | 13 | 14 | [{java_src_attr}] 15 | ---- 16 | include::{sourcedir}/PriorityQueueTest.java[tag=size] 17 | ---- 18 | 19 | 从上述例子中可以看出,`PriorityQueue` 的长度是回增长的。所以,如果需要定长的优先队列,则需要将多余数据"弹出"。 20 | 21 | == 参考资料 22 | 23 | . https://www.pdai.tech/md/java/collection/java-collection-PriorityQueue.html[Collection - PriorityQueue源码解析 | Java 全栈知识体系] 24 | -------------------------------------------------------------------------------- /docs/java.util.Queue.adoc: -------------------------------------------------------------------------------- 1 | [#util-Queue] 2 | = Queue 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.ServiceLoader.adoc: -------------------------------------------------------------------------------- 1 | [#util-ServiceLoader] 2 | = `ServiceLoader` 3 | 4 | [{java_src_attr}] 5 | ---- 6 | include::{sourcedir}/ServiceLoaderSay.java[] 7 | ---- 8 | 9 | [{java_src_attr}] 10 | ---- 11 | include::{sourcedir}/ServiceLoaderSayHello.java[] 12 | ---- 13 | 14 | [{java_src_attr}] 15 | ---- 16 | include::{sourcedir}/ServiceLoaderSayGoodbye.java[] 17 | ---- 18 | 19 | [{java_src_attr}] 20 | ---- 21 | include::{sourcedir}/ServiceLoaderTest.java[] 22 | ---- 23 | 24 | 经过测试,有几点需要注意: 25 | 26 | . 只支持 `public` 的类。D瓜哥测试,这是因为内部需要创建对象,其他访问控制不能在 `ServiceLoader` 类中创建对象。 27 | . 不支持 `Outter.Inner` 这样的内部类 ,即使是 `public static class`。 28 | 29 | == 参考资料 30 | 31 | . http://www.throwable.club/2018/11/30/java-service-loader/[浅析JDK中ServiceLoader的源码 - Throwable's Blog] -- 这里的代码是基于 JDK 8 的,和 JDK 11 的代码已经相差很大。 32 | . https://www.cnblogs.com/vivotech/p/16381937.html[剖析 SPI 在 Spring 中的应用^] -- 讲解了 Java、Spring、Dubbo 等三方面的实现,细致入微。 33 | . https://www.cnblogs.com/better-farther-world2099/articles/17092788.html[SpringBoot的SPI机制^] 34 | -------------------------------------------------------------------------------- /docs/java.util.Set.adoc: -------------------------------------------------------------------------------- 1 | [#util-Set] 2 | = Set 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.SortedMap.adoc: -------------------------------------------------------------------------------- 1 | [#util-SortedMap] 2 | = SortedMap 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.Stack.adoc: -------------------------------------------------------------------------------- 1 | [#util-Stack] 2 | = Stack 3 | 4 | [plantuml,{diagram_attr}] 5 | .... 6 | @startuml 7 | 'skinparam nodesep 70 8 | skinparam defaultFontName Hiragino Sans GB 9 | 10 | title Stack 11 | 12 | interface Iterable 13 | 14 | interface Collection extends Iterable 15 | 16 | interface List extends Collection 17 | 18 | abstract class AbstractCollection implements Collection 19 | 20 | abstract class AbstractList extends AbstractCollection implements List 21 | 22 | class Vector extends AbstractList implements RandomAccess, Cloneable, java.io.Serializable 23 | 24 | class Stack extends Vector 25 | 26 | @enduml 27 | .... 28 | 29 | `Stack` 的实现极其简单。可以用几句话概括完: 30 | 31 | . `Stack` 直接继承至 `Vector`,在其基础之上,只是增加了栈相关的操作; 32 | . 在方法上使用 `synchronized` 来实现线程安全; 33 | 34 | -------------------------------------------------------------------------------- /docs/java.util.TreeMap.adoc: -------------------------------------------------------------------------------- 1 | [#util-TreeMap] 2 | = TreeMap 3 | 4 | 先看一个问题: 5 | 6 | [{java_src_attr}] 7 | ---- 8 | include::{sourcedir}/TreeMapTest.java[tag=question] 9 | ---- 10 | 11 | `TreeMap` 底层是一个红黑树。 12 | 13 | 先定义一个测试实体类: 14 | 15 | [{java_src_attr}] 16 | ---- 17 | include::{sourcedir}/TreeMapTest.java[tag=classPerson] 18 | ---- 19 | 20 | [{java_src_attr}] 21 | ---- 22 | include::{sourcedir}/TreeMapTest.java[tag=resort] 23 | ---- 24 | 25 | 修改排序字段,打印时,依然可以保持有序性。__这个实现是怎么回事?__ 26 | 27 | [{java_src_attr}] 28 | ---- 29 | include::{sourcedir}/TreeMapTest.java[tag=duplicateSortFactor] 30 | ---- 31 | 32 | 由此看出,`TreeMap` 不能接受排序因子相同的值。如果存在,则后来者把前者的 `Value` 覆盖掉。 33 | 34 | [{java_src_attr}] 35 | ---- 36 | include::{sourcedir}/TreeMapTest.java[tag=commonCase] 37 | ---- 38 | 39 | -------------------------------------------------------------------------------- /docs/java.util.TreeSet.adoc: -------------------------------------------------------------------------------- 1 | [#util-TreeSet] 2 | = TreeSet 3 | 4 | -------------------------------------------------------------------------------- /docs/java.util.Vector.adoc: -------------------------------------------------------------------------------- 1 | [#util-Vector] 2 | = Vector 3 | 4 | [plantuml,{diagram_attr}] 5 | .... 6 | @startuml 7 | 'skinparam nodesep 70 8 | skinparam defaultFontName Hiragino Sans GB 9 | 10 | title Vector 11 | 12 | interface Iterable 13 | 14 | interface Collection extends Iterable 15 | 16 | interface List extends Collection 17 | 18 | abstract class AbstractCollection implements Collection 19 | 20 | abstract class AbstractList extends AbstractCollection implements List 21 | 22 | class Vector 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 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 out) throws Exception { 26 | System.out.println("ByteToLongDecoder decode 被调用"); 27 | if (in.readableBytes() >= 8) { 28 | out.add(in.readLong()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/ByteToLongDecoder2.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.ReplayingDecoder; 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 ByteToLongDecoder2 extends ReplayingDecoder { 14 | 15 | @Override 16 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 17 | // 在 ReplayingDecoder 不需要判断数据是否足够读取,内部会进行处理判断。 18 | out.add(in.readLong()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/IoClient.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.iobound; 2 | 3 | import com.diguage.truman.netty.protobuf.ClientHandler; 4 | import io.netty.bootstrap.Bootstrap; 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 | 12 | /** 13 | * @author D瓜哥, https://www.diguage.com/ 14 | * @since 2020-06-28 19:57 15 | */ 16 | public class IoClient { 17 | public static void main(String[] args) throws InterruptedException { 18 | // 客户端只需要一个事件循环组即可 19 | NioEventLoopGroup group = new NioEventLoopGroup(); 20 | 21 | try { 22 | // 创建客户端启动对象 23 | Bootstrap bootstrap = new Bootstrap(); 24 | // 设置相关参数 25 | bootstrap.group(group) // 设置线程组 26 | .channel(NioSocketChannel.class) // 设置客户端通讯通道的实现类 27 | .handler(new IoClientInitializer()); 28 | System.out.println("....客户端 OK ..."); 29 | 30 | // 启动客户端去连接服务器端 31 | // 关于 ChannelFuture 还要分析,涉及到 Netty 的异步模型 32 | ChannelFuture future = bootstrap.connect("127.0.0.1", 11911).sync(); 33 | 34 | // 给关闭通道进行监听 35 | future.channel().closeFuture().sync(); 36 | } finally { 37 | group.shutdownGracefully(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/IoClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.iobound; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.SimpleChannelInboundHandler; 6 | 7 | import static java.nio.charset.StandardCharsets.UTF_8; 8 | 9 | /** 10 | * @author D瓜哥, https://www.diguage.com/ 11 | * @since 2020-06-28 20:05 12 | */ 13 | public class IoClientHandler extends SimpleChannelInboundHandler { 14 | @Override 15 | protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception { 16 | System.out.println("从服务器接受消息, msg=" + msg); 17 | } 18 | 19 | @Override 20 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 21 | System.out.println("IoClientHandler 发送数据"); 22 | // ctx.writeAndFlush(119L); // 发送一个 long 23 | // 注意:这个实验数据!!! 24 | // 分析 25 | // 1. "abcdabcdabcdabcd" 是 16 个字节 26 | // 2. 该处理器的前一个 handler 是 LongToByteEncoder 27 | // 3. LongToByteEncoder 父类是 MessageToByteEncoder 28 | // 4. 父类的 write 调用 acceptOutboundMessage 方法来检查是不是可以接受的数据, 29 | // 是则执行子类 encode 的方法,否则直接传递到下一个 handler 30 | // 因此,在编写 Enoder 时,要注意传入的数据类型和处理的数据类型一致。否则就跳过由下一个handler处理了。 31 | ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcdabcd", UTF_8)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/IoClientInitializer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.iobound; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-28 20:00 10 | */ 11 | public class IoClientInitializer extends ChannelInitializer { 12 | @Override 13 | protected void initChannel(SocketChannel ch) throws Exception { 14 | ChannelPipeline pipeline = ch.pipeline(); 15 | // 加入一个出站的 handler,对数据进行一个编码 16 | pipeline.addLast(new LongToByteEncoder()); 17 | // 入站解码器 18 | // pipeline.addLast(new ByteToLongDecoder()); 19 | pipeline.addLast(new ByteToLongDecoder2()); 20 | pipeline.addLast(new IoClientHandler()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/IoServer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.iobound; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.nio.NioEventLoopGroup; 6 | import io.netty.channel.socket.nio.NioServerSocketChannel; 7 | import io.netty.handler.logging.LogLevel; 8 | import io.netty.handler.logging.LoggingHandler; 9 | 10 | /** 11 | * @author D瓜哥, https://www.diguage.com/ 12 | * @since 2020-06-28 19:46 13 | */ 14 | public class IoServer { 15 | public static void main(String[] args) throws InterruptedException { 16 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); 17 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 18 | try { 19 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 20 | serverBootstrap.group(bossGroup, workerGroup) 21 | .channel(NioServerSocketChannel.class) 22 | .handler(new LoggingHandler(LogLevel.INFO)) // 在 bossGroup 增加一个日志处理器 23 | .childHandler(new IoServerInitializer()); 24 | 25 | ChannelFuture future = serverBootstrap.bind(11911).sync(); 26 | future.channel().closeFuture().sync(); 27 | } finally { 28 | bossGroup.shutdownGracefully(); 29 | workerGroup.shutdownGracefully(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/IoServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.iobound; 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 19:55 9 | */ 10 | public class IoServerHandler extends SimpleChannelInboundHandler { 11 | @Override 12 | protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception { 13 | System.out.println("IoServerHandler 被调用"); 14 | System.out.println("从客户的" + ctx.channel().remoteAddress() + " 读取long=" + msg); 15 | } 16 | 17 | @Override 18 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 19 | ctx.writeAndFlush(120L); 20 | } 21 | 22 | @Override 23 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 24 | cause.printStackTrace(); 25 | ctx.channel().close(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/IoServerInitializer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.iobound; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-28 19:47 10 | */ 11 | public class IoServerInitializer extends ChannelInitializer { 12 | @Override 13 | protected void initChannel(SocketChannel ch) throws Exception { 14 | ChannelPipeline pipeline = ch.pipeline(); 15 | // 入站的 handler 进行编码 16 | // pipeline.addLast(new ByteToLongDecoder()); 17 | pipeline.addLast(new ByteToLongDecoder2()); 18 | // 出站编码器 19 | pipeline.addLast(new LongToByteEncoder()); 20 | pipeline.addLast(new IoServerHandler()); 21 | System.out.println(ch); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/iobound/LongToByteEncoder.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.MessageToByteEncoder; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-28 20:02 10 | */ 11 | public class LongToByteEncoder extends MessageToByteEncoder { 12 | @Override 13 | protected void encode(ChannelHandlerContext ctx, Long msg, ByteBuf out) throws Exception { 14 | System.out.println("LongToByteEncoder encode 被调用"); 15 | System.out.println("msg=" + msg); 16 | out.writeLong(msg); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | 10 | /** 11 | * @author D瓜哥, https://www.diguage.com/ 12 | * @since 2020-06-27 19:41 13 | */ 14 | public class ClientHandler extends ChannelInboundHandlerAdapter { 15 | /** 16 | * 当通道就绪就会触发该方法 17 | */ 18 | @Override 19 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 20 | System.out.println("client " + ctx); 21 | 22 | StudentPOJO.Student student = StudentPOJO.Student 23 | .newBuilder() 24 | .setId(119) 25 | .setName("D瓜哥") 26 | .build(); 27 | 28 | ctx.writeAndFlush(student); 29 | } 30 | 31 | /** 32 | * 当通道有读取事件时,会触发 33 | */ 34 | @Override 35 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 36 | System.out.println(""); 37 | ByteBuf buf = (ByteBuf) msg; 38 | System.out.println("服务器回复的消息:" + buf.toString(UTF_8)); 39 | System.out.println("服务器的地址:" + ctx.channel().remoteAddress()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf/ProtobufClient.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf; 2 | 3 | import io.netty.bootstrap.Bootstrap; 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.NioSocketChannel; 10 | import io.netty.handler.codec.protobuf.ProtobufEncoder; 11 | 12 | /** 13 | * @author D瓜哥, https://www.diguage.com/ 14 | * @since 2020-06-27 19:35 15 | */ 16 | public class ProtobufClient { 17 | public static void main(String[] args) throws InterruptedException { 18 | // 客户端只需要一个事件循环组即可 19 | NioEventLoopGroup group = new NioEventLoopGroup(); 20 | 21 | try { 22 | // 创建客户端启动对象 23 | Bootstrap bootstrap = new Bootstrap(); 24 | // 设置相关参数 25 | bootstrap.group(group) // 设置线程组 26 | .channel(NioSocketChannel.class) // 设置客户端通讯通道的实现类 27 | .handler(new ChannelInitializer() { 28 | @Override 29 | protected void initChannel(SocketChannel ch) throws Exception { 30 | ChannelPipeline pipeline = ch.pipeline(); 31 | // 加入 protobuf handler 32 | pipeline.addLast("encoder", new ProtobufEncoder()); 33 | pipeline.addLast(new ClientHandler()); // 加入自己的处理器 34 | } 35 | }); 36 | System.out.println("....客户端 OK ..."); 37 | 38 | // 启动客户端去连接服务器端 39 | // 关于 ChannelFuture 还要分析,涉及到 Netty 的异步模型 40 | ChannelFuture future = bootstrap.connect("127.0.0.1", 11911).sync(); 41 | 42 | // 给关闭通道进行监听 43 | future.channel().closeFuture().sync(); 44 | } finally { 45 | group.shutdownGracefully(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf/ProtobufServer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf; 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.codec.protobuf.ProtobufDecoder; 11 | import io.netty.handler.logging.LogLevel; 12 | import io.netty.handler.logging.LoggingHandler; 13 | 14 | /** 15 | * @author D瓜哥, https://www.diguage.com/ 16 | * @since 2020-06-28 16:02 17 | */ 18 | public class ProtobufServer { 19 | public static void main(String[] args) throws InterruptedException { 20 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); 21 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 22 | try { 23 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 24 | serverBootstrap.group(bossGroup, workerGroup) 25 | .channel(NioServerSocketChannel.class) 26 | .handler(new LoggingHandler(LogLevel.INFO)) // 在 bossGroup 增加一个日志处理器 27 | .childHandler(new ChannelInitializer() { 28 | @Override 29 | protected void initChannel(SocketChannel ch) throws Exception { 30 | ChannelPipeline pipeline = ch.pipeline(); 31 | pipeline.addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance())); 32 | pipeline.addLast(new ServerHandler()); 33 | } 34 | }); 35 | 36 | ChannelFuture future = serverBootstrap.bind(11911).sync(); 37 | future.channel().closeFuture().sync(); 38 | } finally { 39 | bossGroup.shutdownGracefully(); 40 | workerGroup.shutdownGracefully(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf/ServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.SimpleChannelInboundHandler; 6 | 7 | import static java.nio.charset.StandardCharsets.UTF_8; 8 | 9 | /** 10 | * 说明: 11 | * 我们自定义一个 Handler 需要继承 netty 规定好的某个 HandlerAdapter 12 | * 13 | * @author D瓜哥, https://www.diguage.com/ 14 | * @since 2020-06-27 18:54 15 | */ 16 | public class ServerHandler extends SimpleChannelInboundHandler { 17 | /** 18 | * 读取数据实际(这里我们可以读取客户端发送的消息) 19 | */ 20 | @Override 21 | public void channelRead0(ChannelHandlerContext ctx, StudentPOJO.Student student) throws Exception { 22 | System.out.println("客户端发送的数据 id=" + student.getId() + ",name=" + student.getName()); 23 | } 24 | 25 | /** 26 | * 数据读取完毕 27 | */ 28 | @Override 29 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 30 | // 将数据写入到缓存,并刷新 31 | // 一般讲,我们对这个发送的数据进行编码 32 | ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, D瓜哥~, pong -> O(∩_∩)O哈哈~", UTF_8)); 33 | } 34 | 35 | /** 36 | * 处理异常,一般需要关闭通道 37 | */ 38 | @Override 39 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 40 | ctx.close(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf/Student.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; // 版本号 2 | 3 | option java_outer_classname = "StudentPOJO"; // 生成的外部类名,同时也是文件名 4 | 5 | // protobuf 使用 message 管理数据 6 | // 会在 StudentPOJO 外部类生成一个内部类 Student, 7 | message Student { 8 | int32 id = 1; // 在 Student 类中有一个属性名称为 id 类型为 int32,1表示属性序号,不是值 9 | string name = 2; 10 | } 11 | 12 | // 在这个文件所在目录执行如下命令,生成Java类: 13 | // protoc --java_out=. Student.proto 14 | // WARNING: 需要给生成的类,加上 package 名。 15 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf2/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf2; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | 7 | import java.util.Random; 8 | 9 | import static java.nio.charset.StandardCharsets.UTF_8; 10 | 11 | /** 12 | * @author D瓜哥, https://www.diguage.com/ 13 | * @since 2020-06-27 19:41 14 | */ 15 | public class ClientHandler extends ChannelInboundHandlerAdapter { 16 | /** 17 | * 当通道就绪就会触发该方法 18 | */ 19 | @Override 20 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 21 | System.out.println("client " + ctx); 22 | 23 | // 随机发送 Student 或 Worker 24 | int random = new Random().nextInt(3); 25 | MyDataInfo.MyMessage message = null; 26 | if (0 == random) { 27 | // 发送学生 28 | message = MyDataInfo.MyMessage.newBuilder() 29 | .setDataType(MyDataInfo.MyMessage.DataType.StudentType) 30 | .setStudent( 31 | MyDataInfo.Student 32 | .newBuilder() 33 | .setId(119) 34 | .setName("瓜哥") 35 | .build()) 36 | .build(); 37 | } else { 38 | // 发送 worker 39 | message = MyDataInfo.MyMessage.newBuilder() 40 | .setDataType(MyDataInfo.MyMessage.DataType.WorkerType) 41 | .setWorker( 42 | MyDataInfo.Worker 43 | .newBuilder() 44 | .setName("瓜哥") 45 | .setAge(119) 46 | .build()) 47 | .build(); 48 | } 49 | 50 | ctx.writeAndFlush(message); 51 | } 52 | 53 | /** 54 | * 当通道有读取事件时,会触发 55 | */ 56 | @Override 57 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 58 | System.out.println(""); 59 | ByteBuf buf = (ByteBuf) msg; 60 | System.out.println("服务器回复的消息:" + buf.toString(UTF_8)); 61 | System.out.println("服务器的地址:" + ctx.channel().remoteAddress()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf2/ProtobufClient2.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf2; 2 | 3 | import io.netty.bootstrap.Bootstrap; 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.NioSocketChannel; 10 | import io.netty.handler.codec.protobuf.ProtobufEncoder; 11 | 12 | /** 13 | * @author D瓜哥, https://www.diguage.com/ 14 | * @since 2020-06-27 19:35 15 | */ 16 | public class ProtobufClient2 { 17 | public static void main(String[] args) throws InterruptedException { 18 | // 客户端只需要一个事件循环组即可 19 | NioEventLoopGroup group = new NioEventLoopGroup(); 20 | 21 | try { 22 | // 创建客户端启动对象 23 | Bootstrap bootstrap = new Bootstrap(); 24 | // 设置相关参数 25 | bootstrap.group(group) // 设置线程组 26 | .channel(NioSocketChannel.class) // 设置客户端通讯通道的实现类 27 | .handler(new ChannelInitializer() { 28 | @Override 29 | protected void initChannel(SocketChannel ch) throws Exception { 30 | ChannelPipeline pipeline = ch.pipeline(); 31 | // 加入 protobuf handler 32 | pipeline.addLast("encoder", new ProtobufEncoder()); 33 | pipeline.addLast(new ClientHandler()); // 加入自己的处理器 34 | } 35 | }); 36 | System.out.println("....客户端 OK ..."); 37 | 38 | // 启动客户端去连接服务器端 39 | // 关于 ChannelFuture 还要分析,涉及到 Netty 的异步模型 40 | ChannelFuture future = bootstrap.connect("127.0.0.1", 11911).sync(); 41 | 42 | // 给关闭通道进行监听 43 | future.channel().closeFuture().sync(); 44 | } finally { 45 | group.shutdownGracefully(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf2/ProtobufServer2.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf2; 2 | 3 | import com.diguage.truman.netty.protobuf.StudentPOJO; 4 | import io.netty.bootstrap.ServerBootstrap; 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.NioServerSocketChannel; 11 | import io.netty.handler.codec.protobuf.ProtobufDecoder; 12 | import io.netty.handler.logging.LogLevel; 13 | import io.netty.handler.logging.LoggingHandler; 14 | 15 | /** 16 | * @author D瓜哥, https://www.diguage.com/ 17 | * @since 2020-06-28 16:02 18 | */ 19 | public class ProtobufServer2 { 20 | public static void main(String[] args) throws InterruptedException { 21 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); 22 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 23 | try { 24 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 25 | serverBootstrap.group(bossGroup, workerGroup) 26 | .channel(NioServerSocketChannel.class) 27 | .handler(new LoggingHandler(LogLevel.INFO)) // 在 bossGroup 增加一个日志处理器 28 | .childHandler(new ChannelInitializer() { 29 | @Override 30 | protected void initChannel(SocketChannel ch) throws Exception { 31 | ChannelPipeline pipeline = ch.pipeline(); 32 | pipeline.addLast("decoder", new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance())); 33 | pipeline.addLast(new ServerHandler()); 34 | } 35 | }); 36 | 37 | ChannelFuture future = serverBootstrap.bind(11911).sync(); 38 | future.channel().closeFuture().sync(); 39 | } finally { 40 | bossGroup.shutdownGracefully(); 41 | workerGroup.shutdownGracefully(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf2/ServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.protobuf2; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.SimpleChannelInboundHandler; 6 | 7 | import static java.nio.charset.StandardCharsets.UTF_8; 8 | 9 | /** 10 | * 说明: 11 | * 我们自定义一个 Handler 需要继承 netty 规定好的某个 HandlerAdapter 12 | * 13 | * @author D瓜哥, https://www.diguage.com/ 14 | * @since 2020-06-27 18:54 15 | */ 16 | public class ServerHandler extends SimpleChannelInboundHandler { 17 | /** 18 | * 读取数据实际(这里我们可以读取客户端发送的消息) 19 | */ 20 | @Override 21 | public void channelRead0(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg) throws Exception { 22 | MyDataInfo.MyMessage.DataType dataType = msg.getDataType(); 23 | if (dataType == MyDataInfo.MyMessage.DataType.StudentType) { 24 | MyDataInfo.Student student = msg.getStudent(); 25 | System.out.println("学生 id=" + student.getId() + ", name=" + student.getName()); 26 | } else if (dataType == MyDataInfo.MyMessage.DataType.WorkerType) { 27 | MyDataInfo.Worker worker = msg.getWorker(); 28 | System.out.println("工人 name=" + worker.getName() + ", age=" + worker.getAge()); 29 | } else { 30 | System.out.println("传输类型不正确!"); 31 | } 32 | } 33 | 34 | /** 35 | * 数据读取完毕 36 | */ 37 | @Override 38 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 39 | // 将数据写入到缓存,并刷新 40 | // 一般讲,我们对这个发送的数据进行编码 41 | ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, D瓜哥~, pong -> O(∩_∩)O哈哈~", UTF_8)); 42 | } 43 | 44 | /** 45 | * 处理异常,一般需要关闭通道 46 | */ 47 | @Override 48 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 49 | ctx.close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/protobuf2/Student.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; // 版本号 2 | option optimize_for = SPEED; 3 | option java_package = "com.diguage.truman.netty.protobuf2"; 4 | 5 | option java_outer_classname = "MyDataInfo"; // 生成的外部类名,同时也是文件名 6 | 7 | // protobuf 使用 message 管理数据 8 | // protobuf 可以使用 message 管理其他的 message 9 | 10 | message MyMessage { 11 | // 定义一个枚举 12 | enum DataType { 13 | StudentType = 0; // 在 proto3 中,要求 enum 的编号从 0 开始 14 | WorkerType = 1; 15 | } 16 | 17 | // 用 data_type 来标识传的是哪一个枚举类型? 18 | DataType data_type = 1; 19 | 20 | // 表示每次枚举类型最多只能出现其中的一个,节省空间 21 | oneof dataBody { 22 | Student student = 2; 23 | Worker worker = 3; 24 | } 25 | } 26 | 27 | // 会在 StudentPOJO 外部类生成一个内部类 Student, 28 | message Student { 29 | int32 id = 1; // 在 Student 类中有一个属性名称为 id 类型为 int32,1表示属性序号,不是值 30 | string name = 2; 31 | } 32 | 33 | message Worker { 34 | string name = 1; 35 | int32 age = 2; 36 | } 37 | 38 | // 在这个文件所在目录执行如下命令,生成Java类: 39 | // protoc --java_out=. Student.proto 40 | // WARNING: 需要给生成的类,加上 package 名。 41 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/simple/NettyClient.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.simple; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.SocketChannel; 8 | import io.netty.channel.socket.nio.NioSocketChannel; 9 | 10 | /** 11 | * @author D瓜哥, https://www.diguage.com/ 12 | * @since 2020-06-27 19:35 13 | */ 14 | public class NettyClient { 15 | public static void main(String[] args) throws InterruptedException { 16 | // 客户端只需要一个事件循环组即可 17 | NioEventLoopGroup group = new NioEventLoopGroup(); 18 | 19 | try { 20 | // 创建客户端启动对象 21 | Bootstrap bootstrap = new Bootstrap(); 22 | // 设置相关参数 23 | bootstrap.group(group) // 设置线程组 24 | .channel(NioSocketChannel.class) // 设置客户端通讯通道的实现类 25 | .handler(new ChannelInitializer() { 26 | @Override 27 | protected void initChannel(SocketChannel ch) throws Exception { 28 | ch.pipeline().addLast(new NettyClientHandler()); // 加入自己的处理器 29 | } 30 | }); 31 | System.out.println("....客户端 OK ..."); 32 | 33 | // 启动客户端去连接服务器端 34 | // 关于 ChannelFuture 还要分析,涉及到 Netty 的异步模型 35 | ChannelFuture future = bootstrap.connect("127.0.0.1", 11911).sync(); 36 | 37 | // 给关闭通道进行监听 38 | future.channel().closeFuture().sync(); 39 | } finally { 40 | group.shutdownGracefully(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/simple/NettyClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.simple; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | 10 | /** 11 | * @author D瓜哥, https://www.diguage.com/ 12 | * @since 2020-06-27 19:41 13 | */ 14 | public class NettyClientHandler extends ChannelInboundHandlerAdapter { 15 | /** 16 | * 当通道就绪就会触发该方法 17 | */ 18 | @Override 19 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 20 | System.out.println("client " + ctx); 21 | ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, D瓜哥,ping -> \\(^o^)/", UTF_8)); 22 | } 23 | 24 | /** 25 | * 当通道有读取事件时,会触发 26 | */ 27 | @Override 28 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 29 | System.out.println(""); 30 | ByteBuf buf = (ByteBuf) msg; 31 | System.out.println("服务器回复的消息:" + buf.toString(UTF_8)); 32 | System.out.println("服务器的地址:" + ctx.channel().remoteAddress()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/simple/NettyServer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.simple; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelFutureListener; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import org.junit.jupiter.api.Test; 12 | 13 | /** 14 | * @author D瓜哥, https://www.diguage.com/ 15 | * @since 2020-06-27 17:28 16 | */ 17 | public class NettyServer { 18 | /** 19 | * 在 JDK 11 下启动错误: https://stackoverflow.com/a/57892679/951836 20 | */ 21 | @Test 22 | public void server() throws InterruptedException { 23 | // 创建 bossGroup 和 workerGroup 24 | // 1. 创建两个线程组 bossGroup 和 workerGroup 25 | // 2. bossGroup 只是处理连接请求,真正的客户端业务处理,会交给 workerGroup 完成 26 | // 3. 两个都是无限循环 27 | // 4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数 28 | // 默认 CPU 内核数 * 2,在 io.netty.channel.MultithreadEventLoopGroup.DEFAULT_EVENT_LOOP_THREADS 常量中定义 29 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); 30 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 31 | 32 | try { 33 | // 创建服务器端的启动对象,配置参数 34 | ServerBootstrap bootstrap = new ServerBootstrap(); 35 | // 使用链式编程进行配置 36 | bootstrap.group(bossGroup, workerGroup) // 设置两个 线程组 37 | .channel(NioServerSocketChannel.class) // 使用 NioSocketChannel 作为服务器的通道实现 38 | .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数 39 | .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态 40 | .childHandler(new ChannelInitializer() { // 创建一个通道测试对象 41 | // 给 pipeline 设置处理器 42 | @Override 43 | protected void initChannel(SocketChannel ch) throws Exception { 44 | ch.pipeline().addLast(new NettyServerHandler()); 45 | } 46 | }); // 给 workerGroup 的 EventLoop 对应的管道设置处理器 47 | System.out.println("....服务器 is ready..."); 48 | 49 | // 绑定一个端口并且同步,生成一个 ChannelFuture 对象 50 | // 启动服务器(并绑定端口) 51 | ChannelFuture future = bootstrap.bind(11911).sync(); 52 | 53 | // 给 future 注册监听器 54 | future.addListener(new ChannelFutureListener() { 55 | @Override 56 | public void operationComplete(ChannelFuture future) throws Exception { 57 | if (future.isSuccess()) { 58 | System.out.println("监听端口 11911 成功"); 59 | } else { 60 | System.out.println("监听端口 11911 失败"); 61 | } 62 | } 63 | }); 64 | 65 | 66 | // 对关闭通道进行监听 67 | future.channel().closeFuture().sync(); 68 | 69 | // TODO Netty 的异步模型 70 | } finally { 71 | bossGroup.shutdownGracefully(); 72 | workerGroup.shutdownGracefully(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcp/TcpClient.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcp; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.nio.NioEventLoopGroup; 6 | import io.netty.channel.socket.nio.NioSocketChannel; 7 | 8 | /** 9 | * @author D瓜哥, https://www.diguage.com/ 10 | * @since 2020-06-28 19:57 11 | */ 12 | public class TcpClient { 13 | public static void main(String[] args) throws InterruptedException { 14 | // 客户端只需要一个事件循环组即可 15 | NioEventLoopGroup group = new NioEventLoopGroup(); 16 | 17 | try { 18 | // 创建客户端启动对象 19 | Bootstrap bootstrap = new Bootstrap(); 20 | // 设置相关参数 21 | bootstrap.group(group) // 设置线程组 22 | .channel(NioSocketChannel.class) // 设置客户端通讯通道的实现类 23 | .handler(new TcpClientInitializer()); 24 | System.out.println("....客户端 OK ..."); 25 | 26 | // 启动客户端去连接服务器端 27 | // 关于 ChannelFuture 还要分析,涉及到 Netty 的异步模型 28 | ChannelFuture future = bootstrap.connect("127.0.0.1", 11911).sync(); 29 | 30 | // 给关闭通道进行监听 31 | future.channel().closeFuture().sync(); 32 | } finally { 33 | group.shutdownGracefully(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcp/TcpClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcp; 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 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | 10 | /** 11 | * @author D瓜哥, https://www.diguage.com/ 12 | * @since 2020-06-28 20:05 13 | */ 14 | public class TcpClientHandler extends SimpleChannelInboundHandler { 15 | private int count; 16 | 17 | @Override 18 | protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { 19 | byte[] bytes = new byte[msg.readableBytes()]; 20 | msg.readBytes(bytes); 21 | String message = new String(bytes, UTF_8); 22 | System.out.println("客户端接收到消息=" + message); 23 | System.out.println("客户端接收到消息量=" + (++this.count)); 24 | } 25 | 26 | @Override 27 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 28 | // 使用客户端发送10条数据 29 | for (int i = 0; i < 10; i++) { 30 | ByteBuf buffer = Unpooled.copiedBuffer("Hello,D瓜哥!~~" + i, UTF_8); 31 | ctx.writeAndFlush(buffer); 32 | } 33 | } 34 | 35 | @Override 36 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 37 | cause.printStackTrace(); 38 | ctx.channel().close(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcp/TcpClientInitializer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcp; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-28 20:00 10 | */ 11 | public class TcpClientInitializer extends ChannelInitializer { 12 | @Override 13 | protected void initChannel(SocketChannel ch) throws Exception { 14 | ChannelPipeline pipeline = ch.pipeline(); 15 | pipeline.addLast(new TcpClientHandler()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcp/TcpServer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcp; 2 | 3 | import com.diguage.truman.netty.iobound.IoServerInitializer; 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 io.netty.handler.logging.LogLevel; 9 | import io.netty.handler.logging.LoggingHandler; 10 | 11 | /** 12 | * @author D瓜哥, https://www.diguage.com/ 13 | * @since 2020-06-28 19:46 14 | */ 15 | public class TcpServer { 16 | public static void main(String[] args) throws InterruptedException { 17 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); 18 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 19 | try { 20 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 21 | serverBootstrap.group(bossGroup, workerGroup) 22 | .channel(NioServerSocketChannel.class) 23 | .handler(new LoggingHandler(LogLevel.INFO)) // 在 bossGroup 增加一个日志处理器 24 | .childHandler(new TcpServerInitializer()); 25 | 26 | ChannelFuture future = serverBootstrap.bind(11911).sync(); 27 | future.channel().closeFuture().sync(); 28 | } finally { 29 | bossGroup.shutdownGracefully(); 30 | workerGroup.shutdownGracefully(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcp/TcpServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcp; 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 | 8 | import java.util.UUID; 9 | 10 | import static java.nio.charset.StandardCharsets.UTF_8; 11 | 12 | /** 13 | * @author D瓜哥, https://www.diguage.com/ 14 | * @since 2020-06-29 10:42 15 | */ 16 | public class TcpServerHandler extends SimpleChannelInboundHandler { 17 | private int count; 18 | 19 | @Override 20 | protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { 21 | byte[] bytes = new byte[msg.readableBytes()]; 22 | msg.readBytes(bytes); 23 | 24 | // 将 bytes 转成字符串 25 | String message = new String(bytes, UTF_8); 26 | System.out.println("服务器接收到数据=" + message); 27 | System.out.println("服务器接收到消息量=" + (++this.count)); 28 | 29 | // 服务器回送数据给客户端,回送一个随机ID 30 | ByteBuf buffer = Unpooled.copiedBuffer(UUID.randomUUID().toString() + " ", UTF_8); 31 | ctx.writeAndFlush(buffer); 32 | } 33 | 34 | @Override 35 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 36 | cause.printStackTrace(); 37 | ctx.channel().close(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcp/TcpServerInitializer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcp; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-28 19:47 10 | */ 11 | public class TcpServerInitializer extends ChannelInitializer { 12 | @Override 13 | protected void initChannel(SocketChannel ch) throws Exception { 14 | ChannelPipeline pipeline = ch.pipeline(); 15 | pipeline.addLast(new TcpServerHandler()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/MessageDecoder.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.ReplayingDecoder; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author D瓜哥, https://www.diguage.com/ 11 | * @since 2020-06-29 11:18 12 | */ 13 | public class MessageDecoder extends ReplayingDecoder { 14 | @Override 15 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 16 | System.out.println("\n\nMessageDecoder decode 被调用"); 17 | // 需要将得到的二进制字节码 -> MessageProtocol 数据包 18 | int len = in.readInt(); 19 | byte[] bytes = new byte[len]; 20 | in.readBytes(bytes); 21 | // 封装成 MessageProtocol 对象,放入out,传递给下一个 handler 业务处理 22 | MessageProtocol msp = new MessageProtocol(); 23 | msp.setLen(len); 24 | msp.setContent(bytes); 25 | out.add(msp); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/MessageEncoder.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToByteEncoder; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-29 11:15 10 | */ 11 | public class MessageEncoder extends MessageToByteEncoder { 12 | @Override 13 | protected void encode(ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out) throws Exception { 14 | System.out.println("MessageEncoder encode 方法被调用"); 15 | out.writeInt(msg.getLen()); 16 | out.writeBytes(msg.getContent()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/MessageProtocol.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | /** 4 | * @author D瓜哥, https://www.diguage.com/ 5 | * @since 2020-06-29 11:09 6 | */ 7 | public class MessageProtocol { 8 | private int len; 9 | private byte[] content; 10 | 11 | public int getLen() { 12 | return len; 13 | } 14 | 15 | public void setLen(int len) { 16 | this.len = len; 17 | } 18 | 19 | public byte[] getContent() { 20 | return content; 21 | } 22 | 23 | public void setContent(byte[] content) { 24 | this.content = content; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/TcpClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.SimpleChannelInboundHandler; 5 | 6 | import static java.nio.charset.StandardCharsets.UTF_8; 7 | 8 | /** 9 | * @author D瓜哥, https://www.diguage.com/ 10 | * @since 2020-06-28 20:05 11 | */ 12 | public class TcpClientHandler extends SimpleChannelInboundHandler { 13 | private int count; 14 | 15 | @Override 16 | protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { 17 | // 接收到数据,并处理 18 | int len = msg.getLen(); 19 | byte[] bytes = msg.getContent(); 20 | System.out.println("客户端接收到信息如下:"); 21 | System.out.println("长度=" + len); 22 | System.out.println("消息=" + new String(bytes, UTF_8)); 23 | System.out.println("客户端接收到消息包数量=" + (++this.count)); 24 | } 25 | 26 | @Override 27 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 28 | // 使用客户端发送10条数据 29 | for (int i = 0; i < 10; i++) { 30 | String msg = "Hello,D瓜哥!~~" + i; 31 | byte[] content = msg.getBytes(UTF_8); 32 | // 创建协议包 33 | MessageProtocol msp = new MessageProtocol(); 34 | msp.setContent(content); 35 | msp.setLen(content.length); 36 | 37 | ctx.writeAndFlush(msp); 38 | } 39 | } 40 | 41 | @Override 42 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 43 | cause.printStackTrace(); 44 | ctx.channel().close(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/TcpClientInitializer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-28 20:00 10 | */ 11 | public class TcpClientInitializer extends ChannelInitializer { 12 | @Override 13 | protected void initChannel(SocketChannel ch) throws Exception { 14 | ChannelPipeline pipeline = ch.pipeline(); 15 | pipeline.addLast(new MessageEncoder());// TODO 必须放在 handler 上面吗? 16 | pipeline.addLast(new MessageDecoder()); 17 | pipeline.addLast(new TcpClientHandler()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/TcpServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.SimpleChannelInboundHandler; 5 | 6 | import java.util.UUID; 7 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | 10 | /** 11 | * @author D瓜哥, https://www.diguage.com/ 12 | * @since 2020-06-29 10:42 13 | */ 14 | public class TcpServerHandler extends SimpleChannelInboundHandler { 15 | private int count; 16 | 17 | @Override 18 | protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { 19 | // 接收到数据,并处理 20 | int len = msg.getLen(); 21 | byte[] bytes = msg.getContent(); 22 | System.out.println("服务器接收到信息如下:"); 23 | System.out.println("长度=" + len); 24 | System.out.println("消息=" + new String(bytes, UTF_8)); 25 | System.out.println("服务器接收到消息包数量=" + (++this.count)); 26 | 27 | String responseContent = UUID.randomUUID().toString(); 28 | byte[] responseContentBytes = responseContent.getBytes(UTF_8); 29 | MessageProtocol messageProtocol = new MessageProtocol(); 30 | messageProtocol.setContent(responseContentBytes); 31 | messageProtocol.setLen(responseContentBytes.length); 32 | 33 | ctx.writeAndFlush(messageProtocol); 34 | } 35 | 36 | @Override 37 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 38 | cause.printStackTrace(); 39 | ctx.channel().close(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/TcpServerInitializer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | 7 | /** 8 | * @author D瓜哥, https://www.diguage.com/ 9 | * @since 2020-06-28 19:47 10 | */ 11 | public class TcpServerInitializer extends ChannelInitializer { 12 | @Override 13 | protected void initChannel(SocketChannel ch) throws Exception { 14 | ChannelPipeline pipeline = ch.pipeline(); 15 | pipeline.addLast(new MessageDecoder()); 16 | pipeline.addLast(new MessageEncoder()); 17 | pipeline.addLast(new TcpServerHandler()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/TcprotocolClient.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.nio.NioEventLoopGroup; 6 | import io.netty.channel.socket.nio.NioSocketChannel; 7 | 8 | /** 9 | * @author D瓜哥, https://www.diguage.com/ 10 | * @since 2020-06-28 19:57 11 | */ 12 | public class TcprotocolClient { 13 | public static void main(String[] args) throws InterruptedException { 14 | // 客户端只需要一个事件循环组即可 15 | NioEventLoopGroup group = new NioEventLoopGroup(); 16 | 17 | try { 18 | // 创建客户端启动对象 19 | Bootstrap bootstrap = new Bootstrap(); 20 | // 设置相关参数 21 | bootstrap.group(group) // 设置线程组 22 | .channel(NioSocketChannel.class) // 设置客户端通讯通道的实现类 23 | .handler(new TcpClientInitializer()); 24 | System.out.println("....客户端 OK ..."); 25 | 26 | // 启动客户端去连接服务器端 27 | // 关于 ChannelFuture 还要分析,涉及到 Netty 的异步模型 28 | ChannelFuture future = bootstrap.connect("127.0.0.1", 11911).sync(); 29 | 30 | // 给关闭通道进行监听 31 | future.channel().closeFuture().sync(); 32 | } finally { 33 | group.shutdownGracefully(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/tcprotocol/TcprotocolServer.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.tcprotocol; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelOption; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.nio.NioServerSocketChannel; 8 | import io.netty.handler.logging.LogLevel; 9 | import io.netty.handler.logging.LoggingHandler; 10 | 11 | /** 12 | * @author D瓜哥, https://www.diguage.com/ 13 | * @since 2020-06-28 19:46 14 | */ 15 | public class TcprotocolServer { 16 | public static void main(String[] args) throws InterruptedException { 17 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); 18 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 19 | try { 20 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 21 | serverBootstrap.group(bossGroup, workerGroup) 22 | .channel(NioServerSocketChannel.class) 23 | .option(ChannelOption.SO_BACKLOG, 128) 24 | .handler(new LoggingHandler(LogLevel.INFO)) // 在 bossGroup 增加一个日志处理器 25 | .childHandler(new TcpServerInitializer()); 26 | 27 | ChannelFuture future = serverBootstrap.bind(11911).sync(); 28 | future.channel().closeFuture().sync(); 29 | } finally { 30 | bossGroup.shutdownGracefully(); 31 | workerGroup.shutdownGracefully(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/ws/Server.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.ws; 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.codec.http.HttpObjectAggregator; 11 | import io.netty.handler.codec.http.HttpServerCodec; 12 | import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 13 | import io.netty.handler.logging.LogLevel; 14 | import io.netty.handler.logging.LoggingHandler; 15 | import io.netty.handler.stream.ChunkedWriteHandler; 16 | 17 | /** 18 | * @author D瓜哥, https://www.diguage.com/ 19 | * @since 2020-06-28 14:56 20 | */ 21 | public class Server { 22 | public static void main(String[] args) throws InterruptedException { 23 | NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); 24 | NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 25 | try { 26 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 27 | serverBootstrap.group(bossGroup, workerGroup) 28 | .channel(NioServerSocketChannel.class) 29 | .handler(new LoggingHandler(LogLevel.INFO)) // 在 bossGroup 增加一个日志处理器 30 | .childHandler(new ChannelInitializer() { 31 | @Override 32 | protected void initChannel(SocketChannel ch) throws Exception { 33 | ChannelPipeline pipeline = ch.pipeline(); 34 | // 因为基于 HTTP 协议,使用 HTTP 的编解码器 35 | pipeline.addLast(new HttpServerCodec()); 36 | // 是以块方法写,加 ChunkedWriteHandler 处理器 37 | pipeline.addLast(new ChunkedWriteHandler()); 38 | /** 39 | * 说明 40 | * 1. HTTP 数据在传输过程中是分段的,HttpObjectAggregator 就可以将多个段聚合 41 | * 2. 这就是为什么,当浏览器发送大量数据时,就会发出多次 HTTP 请求 42 | */ 43 | pipeline.addLast(new HttpObjectAggregator(8192)); 44 | /** 45 | * 说明 46 | * 1. 对应 WebSocket,它的数据是以帧(frame)形式传递 47 | * 2. 可以看到 WebSocketFrame 下面有六个子类 48 | * 3. 浏览器请求时: ws://localhost:11911/hello 49 | * 4. WebSocketServerProtocolHandler 核心功能是将 HTTP 协议升级为 WS 协议,保持长连接 50 | * 这一点可以观察浏览器的链接信息,可以看到协议升级的过程。 51 | */ 52 | pipeline.addLast(new WebSocketServerProtocolHandler("/hello")); 53 | 54 | // 自定义 Handler,处理业务逻辑 55 | pipeline.addLast(new TextWebSocketFrameHandler()); 56 | } 57 | }); 58 | 59 | ChannelFuture future = serverBootstrap.bind(11911).sync(); 60 | future.channel().closeFuture().sync(); 61 | } finally { 62 | bossGroup.shutdownGracefully(); 63 | workerGroup.shutdownGracefully(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/ws/TextWebSocketFrameHandler.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.netty.ws; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.SimpleChannelInboundHandler; 5 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * TextWebSocketFrame 类型,表示一个文本帧 11 | * 12 | * @author D瓜哥, https://www.diguage.com/ 13 | * @since 2020-06-28 15:05 14 | */ 15 | public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler { 16 | @Override 17 | protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { 18 | System.out.println("服务器收到消息:" + msg.text()); 19 | // 回复消息 20 | ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间:" 21 | + LocalDateTime.now() + " " + msg.text())); 22 | } 23 | 24 | /** 25 | * 当 Web 客户端连接后,出发方法 26 | */ 27 | @Override 28 | public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 29 | System.out.println("handlerAdded 被调用" + ctx.channel().id().asLongText()); 30 | System.out.println("handlerAdded 被调用" + ctx.channel().id().asShortText()); 31 | } 32 | 33 | @Override 34 | public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { 35 | System.out.println("handlerRemoved 被调用" + ctx.channel().id().asLongText()); 36 | } 37 | 38 | @Override 39 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 40 | System.out.println("发生异常:" + cause.getMessage()); 41 | ctx.channel().close(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/netty/ws/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 37 |
38 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/okhttp/ConnPoolTest.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.okhttp; 2 | 3 | import okhttp3.ConnectionPool; 4 | import okhttp3.OkHttpClient; 5 | import okhttp3.Request; 6 | import okhttp3.Response; 7 | 8 | import java.io.IOException; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | public class ConnPoolTest { 12 | public static void main(String[] args) { 13 | // 创建自定义的连接池 14 | ConnectionPool connectionPool = new ConnectionPool(10, 5, TimeUnit.MINUTES); 15 | 16 | OkHttpClient client = new OkHttpClient.Builder() 17 | .connectionPool(connectionPool) 18 | .build(); 19 | 20 | // 创建请求 21 | Request request = new Request.Builder() 22 | .url("https://www.baidu.com") 23 | .build(); 24 | 25 | // 发送请求并获取响应 26 | try (Response response = client.newCall(request).execute()) { 27 | if (response.isSuccessful()) { 28 | System.out.println(response.body().string()); 29 | } else { 30 | System.err.println("Request failed: " + response); 31 | } 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/reflect/ProxyAnnoTest.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.reflect; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | import java.util.Arrays; 9 | 10 | /** 11 | * @author D瓜哥, https://www.diguage.com/ 12 | * @since 2020-04-08 23:34 13 | */ 14 | public class ProxyAnnoTest { 15 | @Diguage 16 | public static class AnnoTest { 17 | } 18 | 19 | @Diguage("https://github.com/diguage") 20 | public static class AnnoTest2 { 21 | } 22 | 23 | @Retention(RetentionPolicy.RUNTIME) 24 | @Documented 25 | @Target(ElementType.TYPE) 26 | static @interface Diguage { 27 | String value() default "https://www.diguage.com"; 28 | 29 | String name() default "D瓜哥"; 30 | } 31 | 32 | public static void main(String[] args) { 33 | System.getProperties() 34 | .put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); 35 | 36 | Class clazz = AnnoTest.class; 37 | Diguage annotation = clazz.getAnnotation(Diguage.class); 38 | System.out.println(annotation + " : " + annotation.hashCode()); 39 | System.out.println("Name: " + annotation.name()); 40 | System.out.println("Value: " + annotation.value()); 41 | 42 | Class annoClass = annotation.getClass(); 43 | 44 | 45 | System.out.println("\n----Class----"); 46 | String className = annoClass.getName(); 47 | 48 | System.out.println("\n----SuperClass----"); 49 | System.out.println(annoClass.getSuperclass().getName()); 50 | 51 | System.out.println("\n----Interfaces----"); 52 | System.out.println(Arrays.toString(annoClass.getInterfaces())); 53 | 54 | System.out.println("\n----Methods----"); 55 | System.out.println(Arrays.toString(annoClass.getDeclaredMethods()) 56 | .replaceAll(", p", ",\n p")); 57 | 58 | System.out.println("\n\n=============="); 59 | Diguage anno2 = AnnoTest2.class.getAnnotation(Diguage.class); 60 | System.out.println(anno2 + " : " + anno2.hashCode()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/regex/PatternTest.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.regex; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.format.DateTimeFormatter; 6 | import java.time.temporal.TemporalAccessor; 7 | import java.util.Map; 8 | import java.util.Properties; 9 | import java.util.TreeMap; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | public class PatternTest { 14 | @Test 15 | public void test() { 16 | Map p2fMap = new TreeMap<>(); 17 | 18 | Properties properties = new Properties(); 19 | // timeFormatter=[patter=format,patter=format,patter=format,patter=format,] 20 | 21 | p2fMap.put(Pattern.compile("\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d"), 22 | DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss")); 23 | 24 | Pattern pattern = Pattern.compile("\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d"); 25 | String input = "2022-03-03 23:59:59"; 26 | Matcher matcher = pattern.matcher(input); 27 | if (matcher.find()) { 28 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"); 29 | TemporalAccessor parse = formatter.parse(input); 30 | } 31 | 32 | Pattern p = Pattern.compile("(\\d\\d\\d\\d)[-|/](\\d\\d)[-|/](\\d\\d) (\\d\\d:\\d\\d:\\d\\d)"); 33 | Matcher m = p.matcher("2022-03-03 23:59:59"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/stream/CollectorsTest.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.stream; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Objects; 8 | import java.util.stream.Collectors; 9 | 10 | public class CollectorsTest { 11 | @Test 12 | public void testToList() { 13 | List ints = Arrays.asList(1, 2, 3); 14 | List filtered = ints.stream() 15 | .filter(i -> i > 5) 16 | .collect(Collectors.toList()); 17 | System.out.println(Objects.isNull(filtered)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/time/FormatTest.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.time; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.Duration; 6 | import java.time.LocalDateTime; 7 | import java.time.ZoneId; 8 | import java.time.ZonedDateTime; 9 | import java.time.format.DateTimeFormatter; 10 | 11 | public class FormatTest { 12 | 13 | @Test 14 | public void test() { 15 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); 16 | // ZonedDateTime time = ZonedDateTime.parse("20230815114735092", formatter); 17 | LocalDateTime t3 = LocalDateTime.parse("20230609143033001", formatter); 18 | LocalDateTime t1 = LocalDateTime.parse("20230815114735092", formatter); 19 | LocalDateTime t2 = LocalDateTime.parse("20230815114735099", formatter); 20 | System.out.println(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/diguage/truman/time/ZoneTest.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman.time; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.ZoneId; 6 | import java.time.ZonedDateTime; 7 | import java.time.temporal.ChronoUnit; 8 | 9 | public class ZoneTest { 10 | @Test 11 | public void test() { 12 | ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); 13 | ZonedDateTime tomorrow = now.plusDays(1L).truncatedTo(ChronoUnit.DAYS).plusHours(5L); 14 | System.out.println(now); 15 | System.out.println(tomorrow); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/hessian/serializers: -------------------------------------------------------------------------------- 1 | com.diguage.truman.Gua=com.diguage.truman.TestSerializer 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.diguage.truman.ServiceLoaderSay: -------------------------------------------------------------------------------- 1 | com.diguage.truman.ServiceLoaderSayHello 2 | com.diguage.truman.ServiceLoaderSayGoodbye -------------------------------------------------------------------------------- /src/main/resources/jgroups-chat-tcp.xml: -------------------------------------------------------------------------------- 1 | 8 | 12 | 19 | 20 | 21 | 22 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 39 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/com/diguage/truman/GsonTest.java: -------------------------------------------------------------------------------- 1 | package com.diguage.truman; 2 | 3 | public class GsonTest { 4 | } 5 | -------------------------------------------------------------------------------- /tools/publish-to-pages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd .. 4 | 5 | mvn clean package 6 | 7 | git stash 8 | 9 | git switch gh-pages 10 | 11 | rm -rf images assets *.html *.svg 12 | 13 | mv target/docs/html/* . 14 | 15 | git add -A 16 | 17 | git commit -am "`date +"%Y-%m-%d %H:%M:%S"`" 18 | 19 | git push -f origin gh-pages 20 | 21 | git switch master 22 | 23 | --------------------------------------------------------------------------------