├── .gitattributes ├── .idea ├── $PRODUCT_WORKSPACE_FILE$ ├── .gitignore ├── encodings.xml ├── misc.xml └── modules.xml ├── README.md ├── Thread_jdk8-juc.iml ├── images ├── ThreadPoolExecutor底层原理.bmp ├── condition.bmp ├── function.bmp ├── jdk8_wait.bmp ├── juc_api.bmp ├── lock.bmp ├── queue.bmp ├── queue方法.bmp ├── reentrantLock.png ├── sync集合.bmp ├── 线程池原理图.bmp ├── 线程池工作流程图.bmp ├── 线程池架构图.bmp └── 队列方法介绍.png └── src └── org └── laiqilin └── juc ├── ArrayListDemo.java ├── BlockingQueueDemo.java ├── CallableDemo.java ├── CountDownLatchDemo.java ├── CyclicBarrierDemo.java ├── HashMapDemo.java ├── HashSetDemo.java ├── LambdaDemo.java ├── LockDemo.java ├── MyThreadPoolDemo1.java ├── MyThreadPoolDemo2.java ├── ProdConsumerDemo.java ├── ReadWriteLockDemo.java ├── SemaphoreDemo.java ├── StreamDemo.java └── VolatileDemo.java /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* linguist-language=Java -------------------------------------------------------------------------------- /.idea/$PRODUCT_WORKSPACE_FILE$: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 1.8 8 | 9 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 1. JUC是什么 2 | 3 | ### 1.1 概述 4 | 5 | >juc是jdk8提供的高并发工具类,都是线程安全的,在java.util.concurrent包下。api如下图: 6 | > 7 | >![](images/juc_api.bmp) 8 | 9 | ### 1.2 线程和进程 10 | 11 | #### 进程/线程是什么? 12 | 13 | **进程** 14 | 15 | >进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。 16 | 17 | **线程** 18 | 19 | >线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。 20 | 21 | 22 | 23 | #### 进程和线程的例子 24 | 25 | **进程案例** 26 | 27 | >比如你在玩游戏的过程又在使用网易云音乐听歌,这个两个俗称进程。 28 | 29 | **线程案例** 30 | 31 | > 比如你在写代码的时候,使用的工具是IDEA,你在编写代码过程,工具帮你检查语法错同时也在帮你保存,这俗称叫做多线程操作。 32 | 33 | #### 线程的状态 34 | 35 | ```java 36 | //线程包含6种状态 37 | public enum State { 38 | 39 | NEW,(新建) 40 | 41 | RUNNABLE,(准备就绪) 42 | 43 | BLOCKED,(阻塞) 44 | 45 | WAITING,(不见不散) 46 | 47 | TIMED_WAITING,(过时不候) 48 | 49 | TERMINATED;(终结) 50 | } 51 | ``` 52 | 53 | #### wait/sleep的区别? 54 | 55 | >线程调用这两个方法都是暂停方法作用,wait是Object类的方法,sleep是Thread静态类的方法 56 | > 57 | >wait是放开手去睡,同时也放开手中的锁。 58 | > 59 | >sleep紧握手中的锁去睡,醒了手中的锁还在。 60 | 61 | #### 什么是并发?什么是并行? 62 | 63 | **并发** 64 | 65 | > 同一个资源有多条线程抢用,多个线程对一个点。 66 | > 67 | > 案例:比如各大电商网站的 秒杀抢购活动。 68 | 69 | **并行** 70 | 71 | > 多项工作一起执行,之后再汇总。 72 | > 73 | > 案例:泡方便面,电水壶烧水,一边撕调料倒入桶中 74 | 75 | 76 | 77 | ## 2. JUC 之 Lock锁 78 | 79 | ### 2.1 Synchronized 80 | 81 | > > 1.synchronized修饰的方法,当创建实例调用它,锁是当前对象,只要有一个方法去调用了synchronized方法,其他的线程都只能等待,等待那条线程释放完。 82 | > > 2.synchronized锁实现同步的基础是:Java中的每一个对象都可以作为锁 83 | > > 2.1创建两个资源对象了,锁是不同一把锁。 84 | > > 3.表现为一下三种 85 | > > 3.1对于静态同步方法锁是当前类的Class对象 86 | > > 3.2对于普通同步方法,锁是当前对象,即为锁的是当前this 87 | > > 3.2对于同步代码块,锁是括号里面配置的对象。 88 | 89 | **Synchronized 案例代码** 90 | 91 | ```java 92 | package com.laiqilin.juc; 93 | 94 | import java.util.concurrent.TimeUnit; 95 | class Phone { 96 | public static synchronized void sendEmail() throws InterruptedException { 97 | TimeUnit.SECONDS.sleep(3); 98 | System.out.println(Thread.currentThread().getName() + "----Phone.sendEmail"); 99 | } 100 | 101 | public synchronized void sendSMS() { 102 | System.out.println(Thread.currentThread().getName() + "----Phone.sendSMS"); 103 | } 104 | 105 | //普通方法 106 | public String sayHello() { 107 | return Thread.currentThread().getName() + "----Hello Thread......"; 108 | } 109 | } 110 | 111 | public class LockDemo { 112 | 113 | public static void main(String[] args) throws Exception { 114 | //资源类 115 | Phone phone = new Phone(); 116 | Phone phone2 = new Phone(); 117 | //创建线程 118 | new Thread(() -> { 119 | try { 120 | phone.sendEmail(); 121 | } catch (InterruptedException e) { 122 | e.printStackTrace(); 123 | } 124 | }, "A").start(); 125 | 126 | //第二条线程 127 | new Thread(() -> { 128 | phone.sendSMS(); 129 | }, "B").start(); 130 | //第三条线程 131 | new Thread(() -> { 132 | System.out.println(phone.sayHello()); 133 | }, "C").start(); 134 | } 135 | } 136 | ``` 137 | 138 | ### 2.2 Lock锁 139 | 140 | **概述** 141 | 142 | > lock锁是jdk8提供的新的多线程锁,提供更多锁机制,api如图: 143 | > 144 | > ![](images/lock.bmp) 145 | > 146 | > 147 | 148 | **Lock接口的实现ReentrantLock可重入锁** 149 | 150 | ![](images/reentrantLock.png) 151 | 152 | 案例代码 153 | 154 | ```java 155 | class X { 156 | private final ReentrantLock lock = new ReentrantLock(); 157 | // ... 158 | 159 | public void m() { 160 | lock.lock(); // block until condition holds 161 | try { 162 | // ... method body 163 | } finally { 164 | lock.unlock() 165 | } 166 | } 167 | } 168 | ``` 169 | 170 | **synchronized与Lock的区别** 171 | 172 | > 两者区别: 173 | > 174 | > 1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类; 175 | > 2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁; 176 | > 3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁; 177 | > 4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了; 178 | > 5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可) 179 | > 6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。 180 | 181 | ### 2.3 创建线程方式 182 | 183 | **线程的创建方式有四种** 184 | 185 | > 第一种: 继承Thread类 186 | > 187 | > ```java 188 | > public class MyThread extends Thread{} 189 | > Thread thread = new Thread(); thread.start(); 190 | > ``` 191 | > 192 | > 第种二: 实现runnable接口 193 | > 194 | > ```java 195 | > class MyThread implements Runnable//新建类实现runnable接口 196 | > new Thread(new MyThread,...) 197 | > ``` 198 | > 199 | > 这种方法会新增类,有更新更好的方法 200 | > 201 | > 匿名内部类写法: 202 | > 203 | > new Thread(new Runnable() { 204 | > 205 | > ```java 206 | > @Override 207 | > public void run() { } 208 | > }, "your thread name").start(); 209 | > ``` 210 | > 这种方法不需要创建新的类,可以new接口 211 | > 212 | > lambda表达式写法(推荐): 213 | > 214 | > ```java 215 | > new Thread(()->{},"Thread Name").start(); 216 | > ``` 217 | > 218 | > 第三种:使用FutureTask类中实现 Callable接口创建线程 219 | > 220 | > ```java 221 | > FutureTask task = new FutureTask<>(new Callable() { 222 | > @Override 223 | > public String call() throws Exception { 224 | > return "Callable线程..."; 225 | > } 226 | > }); 227 | > new Thread(task,"A").start(); 228 | > String s = task.get(); 229 | > System.out.println("s = " + s); 230 | > ``` 231 | > 232 | > 第四种:使用自定义线程池创建线程(ThreadPoolExecutor), jdk自带的线程池(Executors.new....) 233 | 234 | > ```java 235 | > /** 236 | > * 自定义线程池 237 | > */ 238 | > ExecutorService threadPool = new ThreadPoolExecutor ( 239 | > 2, //常驻线程数 240 | > 5, //能够容纳的线程数 241 | > 2L,//多余线程存活时间 242 | > TimeUnit.SECONDS,//keepAliveTime单位 243 | > //任务队列(阻塞队列),被提交但是没有执行在队列等待中 244 | > new ArrayBlockingQueue<> (3), 245 | > Executors.defaultThreadFactory (),//用于生成线程池中工作线程的工厂,默认 246 | > //new ThreadPoolExecutor.AbortPolicy () 247 | > //new ThreadPoolExecutor.DiscardPolicy () 248 | > //new ThreadPoolExecutor.CallerRunsPolicy () 249 | > new ThreadPoolExecutor.DiscardOldestPolicy () 250 | > );//当队列满了,jdk自带的拒绝策略,四种拒绝策略 251 | > ``` 252 | > 253 | > ```java 254 | > //jdk自带的线程池(不推荐使用,详情请参考阿里巴巴开发手册) 255 | > ExecutorService threadPool = Executors.newCachedThreadPool (); 256 | > ``` 257 | 258 | 259 | 260 | 261 | 262 | ## 3. 线程间通信 263 | 264 | ### 3.1 线程怎么通信? 265 | 266 | > 1. 生产者+消费者 267 | > 2. 通知等待唤醒机制 268 | 269 | **多线程编程模板** 270 | 271 | > 判断 + 干活 + 通知 272 | 273 | ### 3.2 Java里面如何进行工程级别的多线程编写 274 | 275 | > 1 多线程变成模板(套路)-----上 276 | > 277 | > 1.1 线程 操作 资源类 278 | > 279 | > 1.2 高内聚 低耦合 280 | > 281 | > 2 多线程变成模板(套路)-----下 282 | > 283 | > 2.1 判断 284 | > 285 | > 2.2 干活 286 | > 287 | > 2.3 通知 288 | 289 | **synchronized实现多线程通信** 290 | 291 | > 案例: 292 | > 293 | > * 现在两个线程, 294 | > 295 | > 296 | > * 可以操作初始值为零的一个变量, 297 | > * 实现一个线程对该变量加1,一个线程对该变量减1, 298 | > * 交替,来10轮。 299 | > 300 | > ```java 301 | > //代码实现 302 | > //资源类 303 | > class ShareDataOne 304 | > { 305 | > private int number = 0;//初始值为零的一个变量 306 | > 307 | > public synchronized void increment() throws InterruptedException 308 | > { 309 | > //多线程中使用if判断会出现线程虚假唤醒现象,所以解决方案 使用while循环代替 310 | > //1判断 311 | > /* if(number !=0 ) { 312 | > this.wait(); 313 | > }*/ 314 | > while(number!=0){ 315 | > this.wait(); 316 | > } 317 | > //2干活 318 | > ++number; 319 | > System.out.println(Thread.currentThread().getName()+"\t"+number); 320 | > //3通知 321 | > this.notifyAll(); 322 | > } 323 | > 324 | > public synchronized void decrement() throws InterruptedException 325 | > { 326 | > //多线程中使用if判断会出现线程虚假唤醒现象,所以解决方案 使用while循环代替 327 | > // 1判断 328 | > /*if (number == 0) { 329 | > this.wait(); 330 | > }*/ 331 | > while(number!=0){ 332 | > this.wait(); 333 | > } 334 | > // 2干活 335 | > --number; 336 | > System.out.println(Thread.currentThread().getName() + "\t" + number); 337 | > // 3通知 338 | > this.notifyAll(); 339 | > } 340 | > } 341 | > 342 | > public class NotifyWaitDemoOne 343 | > { 344 | > public static void main(String[] args) 345 | > { 346 | > ShareDataOne sd = new ShareDataOne(); 347 | > new Thread(() -> { 348 | > for (int i = 1; i < 10; i++) { 349 | > try { 350 | > sd.increment(); 351 | > } catch (InterruptedException e) { 352 | > // TODO Auto-generated catch block 353 | > e.printStackTrace(); 354 | > } 355 | > } 356 | > }, "A").start(); 357 | > new Thread(() -> { 358 | > for (int i = 1; i < 10; i++) { 359 | > try { 360 | > sd.decrement(); 361 | > } catch (InterruptedException e) { 362 | > // TODO Auto-generated catch block 363 | > e.printStackTrace(); 364 | > } 365 | > } 366 | > }, "B").start(); 367 | > } 368 | > } 369 | > 370 | > ``` 371 | > 372 | > **注意** 373 | > 374 | > > ~~注意多线程之间的虚假唤醒~~ 375 | > > 376 | > > 解决方法:**多线中使用while代替if判断** 377 | 378 | **jdk8方式实现上面的案例** 379 | 380 | > 代替传统的实现线程wait()和noifyall() ;对标实现如图: 381 | > 382 | > ![](images/jdk8_wait.bmp) 383 | 384 | **Condition:查看API,java.util.concurrent** 385 | 386 | ![](images/condition.bmp) 387 | 388 | > 代码实现: 389 | > 390 | > ```java 391 | > //创建资源类 392 | > class Aircondition { 393 | > private int number = 0; 394 | > //获取锁对象 395 | > private Lock lock = new ReentrantLock(); 396 | > //返回用于此锁实例的条件实例 397 | > Condition condition = lock.newCondition(); 398 | > 399 | > public void decrement() { 400 | > lock.lock(); 401 | > try { 402 | > while (number == 0) { //使用while代替if解决线程虚假唤醒 403 | > //使当前线程等待,直到发出信号或中断为止 404 | > condition.await(); 405 | > } 406 | > //干活 407 | > number--; 408 | > System.out.println(Thread.currentThread().getName() + "\t" + number); 409 | > //通知 410 | > //唤醒所有等待的线程 411 | > condition.signalAll(); 412 | > } catch (InterruptedException e) { 413 | > e.printStackTrace(); 414 | > } finally { 415 | > lock.unlock(); 416 | > } 417 | > } 418 | > 419 | > public void increment() { 420 | > lock.lock(); 421 | > try { 422 | > while (number != 0) { //使用while代替if解决线程虚假唤醒 423 | > condition.await(); 424 | > } 425 | > //干活 426 | > number++; 427 | > System.out.println(Thread.currentThread().getName() + "\t" + number); 428 | > //通知 429 | > condition.signalAll(); 430 | > } catch (InterruptedException e) { 431 | > e.printStackTrace(); 432 | > } finally { 433 | > lock.unlock(); 434 | > } 435 | > } 436 | > } 437 | > 438 | > public class ProdConsumerDemo { 439 | > public static void main(String[] args) { 440 | > Aircondition aircondition = new Aircondition(); 441 | > new Thread(() -> { 442 | > for (int i = 1; i <= 10; i++) { 443 | > try { 444 | > TimeUnit.SECONDS.sleep(1); 445 | > aircondition.increment(); 446 | > } catch (InterruptedException e) { 447 | > e.printStackTrace(); 448 | > } 449 | > } 450 | > }, "A").start(); 451 | > 452 | > new Thread(() -> { 453 | > for (int i = 1; i <= 10; i++) { 454 | > try { 455 | > TimeUnit.SECONDS.sleep(1); 456 | > aircondition.decrement(); 457 | > } catch (InterruptedException e) { 458 | > e.printStackTrace(); 459 | > } 460 | > } 461 | > }, "B").start(); 462 | > 463 | > new Thread(() -> { 464 | > for (int i = 1; i <= 10; i++) { 465 | > try { 466 | > TimeUnit.SECONDS.sleep(1); 467 | > aircondition.increment(); 468 | > } catch (InterruptedException e) { 469 | > e.printStackTrace(); 470 | > } 471 | > } 472 | > }, "C").start(); 473 | > 474 | > new Thread(() -> { 475 | > for (int i = 1; i <= 10; i++) { 476 | > try { 477 | > TimeUnit.SECONDS.sleep(1); 478 | > aircondition.decrement(); 479 | > } catch (InterruptedException e) { 480 | > e.printStackTrace(); 481 | > } 482 | > } 483 | > }, "D").start(); 484 | > } 485 | > } 486 | > 487 | > ``` 488 | 489 | ## 4. 多线程锁 490 | 491 | ### 8锁分析 492 | 493 | > * 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法 494 | > * 加个普通方法后发现和同步锁无关 495 | > * 换成两个对象后,不是同一把锁了,情况立刻变化。 496 | > * synchronized实现同步的基础:Java中的每一个对象都可以作为锁。 497 | > + 具体表现为以下3种形式。 498 | > + 对于普通同步方法,锁是当前实例对象。 499 | > + 对于静态同步方法,锁是当前类的Class对象。 500 | > + 对于同步方法块,锁是Synchonized括号里配置的对象 501 | > * 当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。 502 | > * 所有的静态同步方法用的也是同一把锁——类对象本身, 503 | > * 这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象! 504 | 505 | ## 5. juc之集合 506 | 507 | **请列举集合中不安全的类** 508 | 509 | > List -> ArrayList 510 | > 511 | > Set -> HashSet 512 | > 513 | > map -> hashMap 514 | 515 | ### 5.1 集合不安全解决方案 516 | 517 | **List** 518 | 519 | > 问题: 520 | > 521 | > ArrayList在迭代的时候如果同时对其进行修改就会 522 | > 抛出java.util.ConcurrentModificationException异常 523 | > 并发修改异常 524 | > 525 | > 解决方案一:使用 Vector类 线程安全 (方法加同步 synchronized ) 526 | > 527 | > 解决方案二:Collections提供了方法synchronizedList保证list是同步线程安全的 528 | > 529 | > ![](images/sync集合.bmp) 530 | > 531 | > 解决方案三:使用juc提供写实复制 CopyOnWriteArrayList 532 | > 533 | > 底层源代码: 534 | > 535 | > ```java 536 | > 537 | > /** 538 | > * Appends the specified element to the end of this list. 539 | > * 540 | > * @param e element to be appended to this list 541 | > * @return {@code true} (as specified by {@link Collection#add}) 542 | > */ 543 | > public boolean add(E e) { 544 | > final ReentrantLock lock = this.lock; 545 | > lock.lock(); 546 | > try { 547 | > Object[] elements = getArray(); 548 | > int len = elements.length; 549 | > //使用Arrays.copyOf(); 550 | > Object[] newElements = Arrays.copyOf(elements, len + 1); 551 | > newElements[len] = e; 552 | > setArray(newElements); 553 | > return true; 554 | > } finally { 555 | > lock.unlock(); 556 | > } 557 | > } 558 | > ``` 559 | > 560 | > 原理: 561 | > 562 | > > CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加, 563 | > > 而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。 564 | > > 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。 565 | > > 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。 566 | > > 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。 567 | 568 | **Set** 569 | 570 | > Set set = new HashSet<>();//线程不安全 571 | > 572 | > 573 | > Set set = new CopyOnWriteArraySet<>();//线程安全 574 | > 575 | > HashSet底层数据结构是什么? 576 | > HashMap 但HashSet的add是放一个值,而HashMap是放K、V键值对 577 | > 578 | 579 | **Map** 580 | 581 | > Map map = new HashMap<>();//线程不安全 582 | > 583 | > Map map = new ConcurrentHashMap<>();//线程安全 584 | 585 | ## 6. Callable接口 586 | 587 | **面试题:获取线程的方法有几种?** 588 | 589 | > 有3+1种 ,继承Thread,实现runnable接口,和借助futrueTask实现Callable接口,+1种是使用线程池。 590 | 591 | **附:函数式接口** api详情图如下: 592 | 593 | ![](images/function.bmp) 594 | 595 | > 这是一个函数式接口,因此可以用作lambda表达式或方法引用的赋值对象。 596 | 597 | ### 6.1 Callable和runnable接口对比 598 | 599 | > ```java 600 | > //创建新类MyThread实现runnable接口 601 | > class MyThread implements Runnable{ 602 | > @Override 603 | > public void run() { 604 | > } 605 | > } 606 | > 607 | > //新类MyThread2实现callable接口 608 | > class MyThread2 implements Callable{ 609 | > @Override 610 | > public Integer call() throws Exception { 611 | > return 200; 612 | > } 613 | > } 614 | > ``` 615 | > 616 | > **面试题:callable接口与runnable接口的区别?** 617 | > 618 | > > 答:(1)是否有返回值 619 | > > 620 | > > ​ (2)是否抛异常 621 | > > ​ (3)落地方法不一样,一个是run,一个是call 622 | > 623 | > 注意 Callable不能代替runnable,因为Thread类的构造方法没有带Callable接口。 624 | 625 | ### 6.2 FutureTask类 626 | 627 | **FutureTask是什么?** 628 | 629 | > 说人话,在现实生活中,我们在上课听老师讲课,突然老师渴了,可以开一个新的线程叫班长去买,回来放桌上,需要的时候 task.get();获取 630 | 631 | **FutureTask原理** 632 | 633 | > 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时, 634 | > 635 | > 可以把这些作业交给Future对象在后台完成, 636 | > 当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。 637 | > 638 | > 一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。 639 | > 640 | > 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成, 641 | > 就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取, 642 | > 643 | > 否则会一直阻塞直到任务转入完成状态, 644 | > 然后会返回结果或者抛出异常。 645 | > 646 | > 只计算一次 647 | > get方法放到最后 648 | 649 | > **代码案例** 650 | > 651 | > ```java 652 | > import java.util.concurrent.Callable; 653 | > import java.util.concurrent.FutureTask; 654 | > import java.util.concurrent.TimeUnit; 655 | > //实现Runnable接口 656 | > class MyThread implements Runnable{ 657 | > @Override 658 | > public void run() { 659 | > } 660 | > } 661 | > //实现Callable接口 662 | > class MyThread2 implements Callable{ 663 | > @Override 664 | > public Integer call() throws Exception { 665 | > System.out.println(Thread.currentThread().getName()+"come in callable"); 666 | > return 200; 667 | > } 668 | > } 669 | > 670 | > 671 | > public class CallableDemo { 672 | > 673 | > 674 | > public static void main(String[] args) throws Exception { 675 | > 676 | > //FutureTask futureTask = new FutureTask(new MyThread2()); 677 | > FutureTask futureTask = new FutureTask(()->{ 678 | > System.out.println(Thread.currentThread().getName()+" come in callable"); 679 | > TimeUnit.SECONDS.sleep(4); 680 | > return 1024; 681 | > }); 682 | > FutureTask futureTask2 = new FutureTask(()->{ 683 | > System.out.println(Thread.currentThread().getName()+" come in callable"); 684 | > TimeUnit.SECONDS.sleep(4); 685 | > return 2048; 686 | > }); 687 | > 688 | > new Thread(futureTask,"zhang3").start(); 689 | > new Thread(futureTask2,"li4").start(); 690 | > 691 | > //System.out.println(futureTask.get()); 692 | > //System.out.println(futureTask2.get()); 693 | > //1、一般放在程序后面,直接获取结果 694 | > //2、只会计算结果一次 695 | > 696 | > while(!futureTask.isDone()){ 697 | > System.out.println("***wait"); 698 | > } 699 | > System.out.println(futureTask.get()); 700 | > System.out.println(Thread.currentThread().getName()+" come over"); 701 | > } 702 | > } 703 | > ``` 704 | 705 | ## 7. JUC强大的辅助类 706 | 707 | ### 7.1 CountDownLatch(减少计数) 708 | 709 | **原理** 710 | 711 | > * CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。 712 | > * 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞), 713 | > * 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。 714 | 715 | **代码案例实现** 716 | 717 | > ```java 718 | > public class CountDownLatchDemo 719 | > { 720 | > public static void main(String[] args) throws InterruptedException 721 | > { 722 | > CountDownLatch countDownLatch = new CountDownLatch(6); 723 | > 724 | > for (int i = 1; i <=6; i++) //6个上自习的同学,各自离开教室的时间不一致 725 | > { 726 | > new Thread(() -> { 727 | > System.out.println(Thread.currentThread().getName()+"\t 号同学离开教室"); 728 | > //计数器 729 | > countDownLatch.countDown(); 730 | > }, String.valueOf(i)).start(); 731 | > } 732 | > countDownLatch.await(); 733 | > System.out.println(Thread.currentThread().getName()+"\t****** 班长关门走人,main线程是班长"); 734 | > 735 | > } 736 | > } 737 | > ``` 738 | 739 | ### 7.2 CyclicBarrier(循环栅栏) 740 | 741 | **原理** 742 | 743 | > * CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是, 744 | > * 让一组线程到达一个屏障(也可以叫同步点)时被阻塞, 745 | > * 直到最后一个线程到达屏障时,屏障才会开门,所有 746 | > * 被屏障拦截的线程才会继续干活。 747 | > * 线程进入屏障通过CyclicBarrier的await()方法。 748 | 749 | **代码案例实现** 750 | 751 | > ```java 752 | > 753 | > import java.util.concurrent.BrokenBarrierException; 754 | > import java.util.concurrent.CyclicBarrier; 755 | > 756 | > /** 757 | > * 758 | > * CyclicBarrier 759 | > * 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是, 760 | > * 让一组线程到达一个屏障(也可以叫同步点)时被阻塞, 761 | > * 直到最后一个线程到达屏障时,屏障才会开门,所有 762 | > * 被屏障拦截的线程才会继续干活。 763 | > * 线程进入屏障通过CyclicBarrier的await()方法。 764 | > * 765 | > * 集齐7颗龙珠就可以召唤神龙 766 | > */ 767 | > public class CyclicBarrierDemo 768 | > { 769 | > private static final int NUMBER = 7; 770 | > public static void main(String[] args) 771 | > { 772 | > //CyclicBarrier(int parties, Runnable barrierAction) 773 | > CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ()->{System.out.println("*****集齐7颗龙珠就可以召唤神龙");}) ; 774 | > 775 | > for (int i = 1; i <= 7; i++) { 776 | > new Thread(() -> { 777 | > try { 778 | > System.out.println(Thread.currentThread().getName()+"\t 星龙珠被收集 "); 779 | > cyclicBarrier.await(); 780 | > } catch (InterruptedException | BrokenBarrierException e) { 781 | > // TODO Auto-generated catch block 782 | > e.printStackTrace(); 783 | > } 784 | > }, String.valueOf(i)).start(); 785 | > } 786 | > } 787 | > } 788 | > ``` 789 | 790 | ### 7.3 Semaphore(信号灯) 791 | 792 | **原理** 793 | 794 | > 在信号量上我们定义两种操作: 795 | > 796 | > * acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1), 797 | > * 要么一直等下去,直到有线程释放信号量,或超时。 798 | > * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。 799 | > * 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。 800 | 801 | **代码案例实现** 802 | 803 | > ```java 804 | > /** 805 | > * 在信号量上我们定义两种操作: 806 | > * acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1), 807 | > * 要么一直等下去,直到有线程释放信号量,或超时。 808 | > * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。 809 | > * 810 | > * 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。 811 | > */ 812 | > 813 | > public class SemaphoreDemo 814 | > { 815 | > public static void main(String[] args) 816 | > { 817 | > Semaphore semaphore = new Semaphore(3);//模拟3个停车位 818 | > 819 | > for (int i = 1; i <=6; i++) //模拟6部汽车 820 | > { 821 | > new Thread(() -> { 822 | > try 823 | > { 824 | > semaphore.acquire(); 825 | > System.out.println(Thread.currentThread().getName()+"\t 抢到了车位"); 826 | > TimeUnit.SECONDS.sleep(new Random().nextInt(5)); 827 | > System.out.println(Thread.currentThread().getName()+"\t------- 离开"); 828 | > } catch (InterruptedException e) { 829 | > e.printStackTrace(); 830 | > }finally { 831 | > semaphore.release(); 832 | > } 833 | > }, String.valueOf(i)).start(); 834 | > } 835 | > } 836 | > } 837 | > ``` 838 | 839 | ## 8. ReentrantReadWriteLock(读写锁) 840 | 841 | **实际案例** 842 | 843 | > 缓存框架 844 | 845 | **代码案例实** 846 | 847 | > ```java 848 | > 849 | > import java.util.HashMap; 850 | > import java.util.Map; 851 | > import java.util.concurrent.TimeUnit; 852 | > import java.util.concurrent.locks.ReadWriteLock; 853 | > import java.util.concurrent.locks.ReentrantReadWriteLock; 854 | > 855 | > class MyCache { 856 | > private volatile Map map = new HashMap<>(); 857 | > //创建读写锁对象 858 | > private ReadWriteLock rwLock = new ReentrantReadWriteLock(); 859 | > 860 | > public void put(String key, Object value) { 861 | > //开启锁 862 | > rwLock.writeLock().lock(); 863 | > try { 864 | > System.out.println(Thread.currentThread().getName() + "\t 正在写" + key); 865 | > //暂停一会儿线程 866 | > try { 867 | > TimeUnit.MILLISECONDS.sleep(300); 868 | > } catch (InterruptedException e) { 869 | > e.printStackTrace(); 870 | > } 871 | > map.put(key, value); 872 | > System.out.println(Thread.currentThread().getName() + "\t 写完了" + key); 873 | > System.out.println(); 874 | > } catch (Exception e) { 875 | > e.printStackTrace(); 876 | > } finally { 877 | > //释放锁 878 | > rwLock.writeLock().unlock(); 879 | > } 880 | > } 881 | > 882 | > public Object get(String key) { 883 | > rwLock.readLock().lock(); 884 | > Object result = null; 885 | > try { 886 | > System.out.println(Thread.currentThread().getName() + "\t 正在读" + key); 887 | > try { 888 | > TimeUnit.MILLISECONDS.sleep(300); 889 | > } catch (InterruptedException e) { 890 | > e.printStackTrace(); 891 | > } 892 | > result = map.get(key); 893 | > System.out.println(Thread.currentThread().getName() + "\t 读完了" + result); 894 | > } catch (Exception e) { 895 | > e.printStackTrace(); 896 | > } finally { 897 | > rwLock.readLock().unlock(); 898 | > } 899 | > return result; 900 | > } 901 | > } 902 | > 903 | > public class ReadWriteLockDemo { 904 | > public static void main(String[] args) { 905 | > MyCache myCache = new MyCache(); 906 | > for (int i = 1; i <= 5; i++) { 907 | > final int num = i; 908 | > new Thread(() -> { 909 | > myCache.put(num + "", num + ""); 910 | > }, String.valueOf(i)).start(); 911 | > } 912 | > for (int i = 1; i <= 5; i++) { 913 | > final int num = i; 914 | > new Thread(() -> { 915 | > myCache.get(num + ""); 916 | > }, String.valueOf(i)).start(); 917 | > } 918 | > } 919 | > } 920 | > ``` 921 | 922 | ## 9 .BlockingQueue(阻塞队列) 923 | 924 | ### 9.1 阻塞架构图 925 | 926 | ![](images/queue.bmp) 927 | 928 | ### 9.2 队列分析 929 | 930 | > **ArrayBlockingQueue:由数组结构组成的有界阻塞队列。** 931 | > **LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列。** 932 | > PriorityBlockingQueue:支持优先级排序的无界阻塞队列。 933 | > DelayQueue:使用优先级队列实现的延迟无界阻塞队列。 934 | > **SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。** 935 | > LinkedTransferQueue:由链表组成的无界阻塞队列。 936 | > LinkedBlockingDeque:由链表组成的双向阻塞队列。 937 | 938 | ### 9.3核心方法介绍 939 | 940 | ![](images/queue方法.bmp) 941 | 942 | ![](images/队列方法介绍.png) 943 | 944 | ## 10 .ThreadPool线程池 945 | 946 | ### 10.1 线程池的优点 947 | 948 | > 线程池的优势: 949 | > 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。 950 | > 951 | > 它的主要特点为:线程复用;控制最大并发数;管理线程。 952 | > 953 | > 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。 954 | > 第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。 955 | > 第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 956 | 957 | ### 10.2 线程池架构图 958 | 959 | > Java中的线程池是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类 960 | > 961 | > ![](images/线程池架构图.bmp) 962 | 963 | **JDK默认的实现线程池案例** 964 | 965 | + Executors.newFixedThreadPool(int) 966 | 967 | ```java 968 | public static ExecutorService newFixedThreadPool(int nThreads) { 969 | return new ThreadPoolExecutor(nThreads, nThreads, 970 | 0L, TimeUnit.MILLISECONDS, 971 | new LinkedBlockingQueue()); 972 | } 973 | //newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的是//LinkedBlockingQueue 974 | ``` 975 | 976 | > 执行长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定线程数的线程 977 | 978 | + Executors.newSingleThreadExecutor() 979 | 980 | ```java 981 | public static ExecutorService newSingleThreadExecutor() { 982 | return new FinalizableDelegatedExecutorService 983 | (new ThreadPoolExecutor(1, 1, 984 | 0L, TimeUnit.MILLISECONDS, 985 | new LinkedBlockingQueue())); 986 | } 987 | //newSingleThreadExecutor 创建的线程池corePoolSize和maximumPoolSize值都是1,它使用的是LinkedBlockingQueue 988 | ``` 989 | 990 | > 一个任务一个任务的执行,一池一线程 991 | 992 | + Executors.newCachedThreadPool() 993 | 994 | ```java 995 | public static ExecutorService newCachedThreadPool() { 996 | return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 997 | 60L, TimeUnit.SECONDS, 998 | new SynchronousQueue()); 999 | } 1000 | /*newCachedThreadPool创建的线程池将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。*/ 1001 | ``` 1002 | 1003 | > 执行很多短期异步任务,线程池根据需要创建新线程, 1004 | > 但在先前构建的线程可用时将重用它们。可扩容,遇强则强 1005 | 1006 | **三种线程池代码案例** 1007 | 1008 | ```java 1009 | import java.util.Arrays; 1010 | import java.util.List; 1011 | import java.util.concurrent.Executor; 1012 | import java.util.concurrent.ExecutorService; 1013 | import java.util.concurrent.Executors; 1014 | /** 1015 | * 线程池 1016 | * Arrays 1017 | * Collections 1018 | * Executors 1019 | */ 1020 | public class MyThreadPoolDemo { 1021 | 1022 | public static void main(String[] args) { 1023 | //List list = new ArrayList(); 1024 | //List list = Arrays.asList("a","b"); 1025 | //固定数的线程池,一池五线程 1026 | 1027 | // ExecutorService threadPool = Executors.newFixedThreadPool(5); //一个银行网点,5个受理业务的窗口 1028 | // ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一个银行网点,1个受理业务的窗口 1029 | ExecutorService threadPool = Executors.newCachedThreadPool(); //一个银行网点,可扩展受理业务的窗口 1030 | 1031 | //10个顾客请求 1032 | try { 1033 | for (int i = 1; i <=10; i++) { 1034 | threadPool.execute(()->{ 1035 | System.out.println(Thread.currentThread().getName()+"\t 办理业务"); 1036 | }); 1037 | } 1038 | } catch (Exception e) { 1039 | e.printStackTrace(); 1040 | } finally { 1041 | threadPool.shutdown(); 1042 | } 1043 | } 1044 | } 1045 | ``` 1046 | 1047 | ### 10.3 ThreadPoolExecutor底层原理 1048 | 1049 | > ![](images/ThreadPoolExecutor底层原理.bmp) 1050 | 1051 | ### 10.4 线程池几个重要参数(7大参数) 1052 | 1053 | > 1、corePoolSize:线程池中的常驻核心线程数 1054 | > 2、maximumPoolSize:线程池中能够容纳同时 执行的最大线程数,此值必须大于等于1 1055 | > 3、keepAliveTime:多余的空闲线程的存活时间 当前池中线程数量超过corePoolSize时,当空闲时间 达到keepAliveTime时,多余线程会被销毁直到 只剩下corePoolSize个线程为止 1056 | > 4、unit:keepAliveTime的单位 1057 | > 5、workQueue:任务队列,被提交但尚未被执行的任务 1058 | > 6、threadFactory:表示生成线程池中工作线程的线程工厂, 用于创建线程,一般默认的即可 1059 | > 7、handler:拒绝策略,表示当队列满了,并且工作线程大于 等于线程池的最大线程数(maximumPoolSize)时如何来拒绝 请求执行的runnable的策略 1060 | 1061 | **线程池工作原理图** 1062 | 1063 | ![](images/线程池原理图.bmp) 1064 | 1065 | **线程池工作流程图** 1066 | 1067 | ![](images/线程池工作流程图.bmp) 1068 | 1069 | > 1、在创建了线程池后,开始等待请求。 1070 | > 2、当调用execute()方法添加一个请求任务时,线程池会做出如下判断: 1071 | > 2.1如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务; 1072 | > 2.2如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列; 1073 | > 2.3如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务; 1074 | > 2.4如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。 1075 | > 3、当一个线程完成任务时,它会从队列中取下一个任务来执行。 1076 | > 4、当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断: 1077 | > 1078 | > 如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。 1079 | > 所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。 1080 | 1081 | ### 10.5 线程池的拒绝策略 1082 | 1083 | > **JDK内置的拒绝策略**(四种) 1084 | > 1085 | > + AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行 1086 | > + CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不 会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。 1087 | > + DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加人队列中 尝试再次提交当前任务。 1088 | > + DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。 如果允许任务丢失,这是最好的一种策略。 1089 | 1090 | **实际工作中不推荐Executors中JDK已经给你提供了线程池创建方法** 1091 | 1092 | ### 10.6 自定义线程池 1093 | 1094 | ```java 1095 | 1096 | import java.util.Arrays; 1097 | import java.util.List; 1098 | import java.util.concurrent.*; 1099 | 1100 | /** 1101 | * 线程池 1102 | * Arrays 1103 | * Collections 1104 | * Executors 1105 | */ 1106 | public class MyThreadPoolDemo { 1107 | 1108 | public static void main(String[] args) { 1109 | ExecutorService threadPool = new ThreadPoolExecutor( 1110 | 2, 1111 | 5, 1112 | 2L, 1113 | TimeUnit.SECONDS, 1114 | new ArrayBlockingQueue(3), 1115 | Executors.defaultThreadFactory(), 1116 | //new ThreadPoolExecutor.AbortPolicy() 1117 | //new ThreadPoolExecutor.CallerRunsPolicy() 1118 | //new ThreadPoolExecutor.DiscardOldestPolicy() 1119 | new ThreadPoolExecutor.DiscardOldestPolicy() 1120 | ); 1121 | //10个顾客请求 1122 | try { 1123 | for (int i = 1; i <= 10; i++) { 1124 | threadPool.execute(() -> { 1125 | System.out.println(Thread.currentThread().getName() + "\t 办理业务"); 1126 | }); 1127 | } 1128 | } catch (Exception e) { 1129 | e.printStackTrace(); 1130 | } finally { 1131 | threadPool.shutdown(); 1132 | } 1133 | } 1134 | 1135 | private static void threadPool() { 1136 | //List list = new ArrayList(); 1137 | //List list = Arrays.asList("a","b"); 1138 | //固定数的线程池,一池五线程 1139 | 1140 | // ExecutorService threadPool = Executors.newFixedThreadPool(5); //一个银行网点,5个受理业务的窗口 1141 | // ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一个银行网点,1个受理业务的窗口 1142 | ExecutorService threadPool = Executors.newCachedThreadPool(); //一个银行网点,可扩展受理业务的窗口 1143 | 1144 | //10个顾客请求 1145 | try { 1146 | for (int i = 1; i <= 10; i++) { 1147 | threadPool.execute(() -> { 1148 | System.out.println(Thread.currentThread().getName() + "\t 办理业务"); 1149 | }); 1150 | } 1151 | } catch (Exception e) { 1152 | e.printStackTrace(); 1153 | } finally { 1154 | threadPool.shutdown(); 1155 | } 1156 | } 1157 | } 1158 | ``` 1159 | 1160 | -------------------------------------------------------------------------------- /Thread_jdk8-juc.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/ThreadPoolExecutor底层原理.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/ThreadPoolExecutor底层原理.bmp -------------------------------------------------------------------------------- /images/condition.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/condition.bmp -------------------------------------------------------------------------------- /images/function.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/function.bmp -------------------------------------------------------------------------------- /images/jdk8_wait.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/jdk8_wait.bmp -------------------------------------------------------------------------------- /images/juc_api.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/juc_api.bmp -------------------------------------------------------------------------------- /images/lock.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/lock.bmp -------------------------------------------------------------------------------- /images/queue.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/queue.bmp -------------------------------------------------------------------------------- /images/queue方法.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/queue方法.bmp -------------------------------------------------------------------------------- /images/reentrantLock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/reentrantLock.png -------------------------------------------------------------------------------- /images/sync集合.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/sync集合.bmp -------------------------------------------------------------------------------- /images/线程池原理图.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/线程池原理图.bmp -------------------------------------------------------------------------------- /images/线程池工作流程图.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/线程池工作流程图.bmp -------------------------------------------------------------------------------- /images/线程池架构图.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/线程池架构图.bmp -------------------------------------------------------------------------------- /images/队列方法介绍.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springbees/Thread_jdk8-juc/2da3c4bc3522debb38a8e8558aaeeed2b8473a4b/images/队列方法介绍.png -------------------------------------------------------------------------------- /src/org/laiqilin/juc/ArrayListDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.List; 4 | import java.util.UUID; 5 | import java.util.concurrent.CopyOnWriteArrayList; 6 | 7 | /** 8 | * @author laiqilin 9 | * @create 2019-10-08 14:24 10 | */ 11 | public class ArrayListDemo { 12 | public static void main(String[] args) { 13 | 14 | /** 异常: 15 | * java.util.ConcurrentModificationException 16 | * ArrayList是线程不安全的 17 | *解决方案可以使用java.util.concurrent包下面的类进行解决 18 | * CopyOnWriteArrayList 解决线程安全 19 | */ 20 | List list = new CopyOnWriteArrayList<> (); 21 | 22 | for (int i = 0; i < 10; i++) { 23 | new Thread (() -> { 24 | list.add (UUID.randomUUID ().toString ().substring (0, 8)); 25 | System.out.println (list); 26 | }, String.valueOf (i)).start (); 27 | 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/BlockingQueueDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | 6 | /** 7 | * @author laiqilin 8 | * @create 2019-10-11 20:57 9 | */ 10 | public class BlockingQueueDemo { 11 | public static void main(String[] args) throws InterruptedException { 12 | 13 | // List list = new ArrayList(); 14 | 15 | BlockingQueue blockingQueue = new ArrayBlockingQueue<> (3); 16 | //第一组 17 | System.out.println (blockingQueue.add ("a")); 18 | System.out.println (blockingQueue.add ("b")); 19 | System.out.println (blockingQueue.add ("c")); 20 | //Exception in thread "main" java.lang.IllegalStateException: Queue full 21 | // System.out.println (blockingQueue.add ("d")); 22 | System.out.println (blockingQueue.element ()); 23 | 24 | //System.out.println(blockingQueue.add("x")); 25 | // System.out.println(blockingQueue.remove()); 26 | // System.out.println(blockingQueue.remove()); 27 | // System.out.println(blockingQueue.remove()); 28 | // System.out.println(blockingQueue.remove()); 29 | // 第二组 30 | // System.out.println(blockingQueue.offer("a")); 31 | // System.out.println(blockingQueue.offer("b")); 32 | // System.out.println(blockingQueue.offer("c")); 33 | // System.out.println(blockingQueue.offer("x")); 34 | // System.out.println(blockingQueue.poll()); 35 | // System.out.println(blockingQueue.poll()); 36 | // System.out.println(blockingQueue.poll()); 37 | // System.out.println(blockingQueue.poll()); 38 | // 第三组 39 | // blockingQueue.put("a"); 40 | // blockingQueue.put("b"); 41 | // blockingQueue.put("c"); 42 | // //blockingQueue.put("x"); 43 | // System.out.println(blockingQueue.take()); 44 | // System.out.println(blockingQueue.take()); 45 | // System.out.println(blockingQueue.take()); 46 | // System.out.println(blockingQueue.take()); 47 | 48 | // 第四组 49 | /*System.out.println (blockingQueue.offer ("a")); 50 | System.out.println (blockingQueue.offer ("b")); 51 | System.out.println (blockingQueue.offer ("c")); 52 | System.out.println (blockingQueue.offer ("a", 3L, TimeUnit.SECONDS));*/ 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/CallableDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.FutureTask; 6 | 7 | /** 8 | * 第三种线程池创建方式 9 | */ 10 | public class CallableDemo { 11 | 12 | public static void main(String[] args) throws ExecutionException, InterruptedException { 13 | 14 | FutureTask task = new FutureTask<>(new Callable() { 15 | @Override 16 | public String call() throws Exception { 17 | return "Callable线程..."; 18 | } 19 | }); 20 | new Thread(task,"A").start(); 21 | String s = task.get(); 22 | System.out.println("s = " + s); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/CountDownLatchDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | 4 | import java.util.concurrent.CountDownLatch; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * 案例:模拟六个学生在教室,班长值班,六个同学走了之后班长才能走。 9 | * 原理: 10 | * CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。 11 | * 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞), 12 | * 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。 13 | * 14 | * @author laiqilin 15 | * @create 2019-10-11 8:40 16 | */ 17 | public class CountDownLatchDemo { 18 | public static void main(String[] args) throws InterruptedException { 19 | 20 | CountDownLatch countDownLatch = new CountDownLatch (6); 21 | for (int i = 1; i <= 6; i++) { 22 | TimeUnit.SECONDS.sleep (2); 23 | new Thread (() -> { 24 | System.out.println (Thread.currentThread ().getName () + "\t号同学离开了教室"); 25 | countDownLatch.countDown (); //进入次数减一 26 | }, String.valueOf (i)).start (); 27 | } 28 | //主线程等待 29 | countDownLatch.await (); 30 | System.out.println (Thread.currentThread ().getName () + "\t班长离开了教室"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/CyclicBarrierDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.concurrent.BrokenBarrierException; 4 | import java.util.concurrent.CyclicBarrier; 5 | 6 | /** 7 | * @author laiqilin 8 | * @create 2019-10-11 8:55 9 | */ 10 | public class CyclicBarrierDemo { 11 | private static final int CONST_NUMBER = 7; 12 | 13 | /** 14 | * * CyclicBarrier 15 | * * 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是, 16 | * * 让一组线程到达一个屏障(也可以叫同步点)时被阻塞, 17 | * * 直到最后一个线程到达屏障时,屏障才会开门,所有 18 | * * 被屏障拦截的线程才会继续干活。 19 | * * 线程进入屏障通过CyclicBarrier的await()方法。 20 | * * 21 | * * 集齐7颗龙珠就可以召唤神龙 22 | */ 23 | 24 | public static void main(String[] args) { 25 | //CyclicBarrier(int parties, Runnable barrierAction) 26 | CyclicBarrier barrier = new CyclicBarrier (CONST_NUMBER, () -> { 27 | System.out.println ("*********七颗龙珠集齐准备召唤神龙*********"); 28 | }); 29 | 30 | for (int i = 1; i <= 7; i++) { 31 | new Thread (() -> { 32 | System.out.println (Thread.currentThread ().getName () + "\t 星龙珠被收集 "); 33 | try { 34 | barrier.await (); 35 | } catch (InterruptedException | BrokenBarrierException e) { 36 | e.printStackTrace (); 37 | } 38 | }, String.valueOf (i)).start (); 39 | 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/HashMapDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.Map; 4 | import java.util.UUID; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * @author laiqilin 9 | * @create 2019-10-08 14:43 10 | */ 11 | public class HashMapDemo { 12 | /** 13 | * 问题:java.util.ConcurrentModificationException 14 | * HashMap在多线程的情况下也是线程不安全的会发生并发修改异常 15 | * 解决方案: 16 | * java.util.concurrent.ConcurrentHashMap; 17 | */ 18 | 19 | public static void main(String[] args) { 20 | Map map = new ConcurrentHashMap<> (); 21 | for (int i = 0; i < 10; i++) { 22 | new Thread (() -> { 23 | map.put (UUID.randomUUID ().toString ().substring (0, 2), UUID.randomUUID ().toString ().substring (0,5)); 24 | System.out.println (map); 25 | }, String.valueOf (i)).start (); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/HashSetDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.Set; 4 | import java.util.UUID; 5 | import java.util.concurrent.CopyOnWriteArraySet; 6 | 7 | /** 8 | * @author laiqilin 9 | * @create 2019-10-08 14:37 10 | */ 11 | public class HashSetDemo { 12 | /** 13 | * 问题:java.util.ConcurrentModificationException 14 | * HashSet也是线程不安全的多线程情况下会发生并发修改异常。 15 | * 解决方案: 16 | * 使用java.util.concurrent.CopyOnWriteArraySet 17 | */ 18 | 19 | public static void main(String[] args) { 20 | Set set = new CopyOnWriteArraySet<> (); 21 | 22 | for (int i = 0; i < 10; i++) { 23 | new Thread (() -> { 24 | set.add (UUID.randomUUID ().toString ().substring (0, 5)); 25 | System.out.println (Thread.currentThread ().getName () + "---->" + set); 26 | }, String.valueOf (i)).start (); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/LambdaDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | //函数式接口注解 4 | @FunctionalInterface 5 | interface Foo { 6 | //没有参数 7 | //public void sayHello(); 8 | 9 | //带参数接口 10 | public int add(int x, int y); 11 | 12 | default int hello(int x, int y) { 13 | return x + y; 14 | } 15 | } 16 | 17 | public class LambdaDemo { 18 | 19 | public static void main(String[] args) { 20 | 21 | //new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start(); 22 | 23 | /* Foo foo = new Foo () { 24 | @Override 25 | public int add(int x, int y) { 26 | return x + y; 27 | } 28 | 29 | @Override 30 | public void sayHello() { 31 | System.out.println ("传统写法------>.sayHello"); 32 | } 33 | }; 34 | foo.sayHello (); 35 | */ 36 | Foo lambdaFoo = (int x, int y) -> { 37 | System.out.println ("Lambda表达式写法----->.main"); 38 | return x + y; 39 | }; 40 | System.out.println (lambdaFoo.hello (8, 8)); 41 | System.out.println (lambdaFoo.hello (8, 8)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/LockDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | 4 | import java.util.concurrent.TimeUnit; 5 | 6 | class Phone { 7 | 8 | public static synchronized void sendEmail() throws InterruptedException { 9 | TimeUnit.SECONDS.sleep(3); 10 | System.out.println(Thread.currentThread().getName() + "----Phone.sendEmail"); 11 | } 12 | 13 | public synchronized void sendSMS() { 14 | System.out.println(Thread.currentThread().getName() + "----Phone.sendSMS"); 15 | } 16 | 17 | //普通方法 18 | public String sayHello() { 19 | return Thread.currentThread().getName() + "----Hello Thread......"; 20 | } 21 | } 22 | 23 | /** 24 | * 1.synchronized修饰的方法,当创建实例调用它,锁是当前对象, 25 | * 只要有一个方法去调用了synchronized方法,其他的线程都只能等待,等待那条线程释放完。 26 | * 2.synchronized锁实现同步的基础是:Java中的每一个对象都可以作为锁 27 | * 2.1创建两个资源对象了,锁是不同一把锁。 28 | * 3.表现为一下三种 29 | * 3.1对于静态同步方法锁是当前类的Class对象 30 | * 3.2对于普通同步方法,锁是当前对象,即为锁的是当前this 31 | * 3.2对于同步代码块,锁是括号里面配置的对象。 32 | * 33 | */ 34 | public class LockDemo { 35 | 36 | public static void main(String[] args) throws Exception { 37 | //资源类 38 | Phone phone = new Phone(); 39 | Phone phone2 = new Phone(); 40 | //创建线程 41 | new Thread(() -> { 42 | try { 43 | phone.sendEmail(); 44 | } catch (InterruptedException e) { 45 | e.printStackTrace(); 46 | } 47 | }, "A").start(); 48 | 49 | //第二条线程 50 | new Thread(() -> { 51 | phone.sendSMS(); 52 | }, "B").start(); 53 | //第三条线程 54 | new Thread(() -> { 55 | System.out.println(phone.sayHello()); 56 | }, "C").start(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/MyThreadPoolDemo1.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | 4 | import java.util.concurrent.ExecutorService; 5 | import java.util.concurrent.Executors; 6 | 7 | /** 8 | * @author laiqilin 9 | * @create 2019-10-11 21:10 10 | */ 11 | public class MyThreadPoolDemo1 { 12 | /** 13 | * 编写三种jdk提供的线程池技术 14 | * Executor 15 | */ 16 | 17 | public static void main(String[] args) { 18 | 19 | // ExecutorService threadPool = Executors.newSingleThreadExecutor (); //比如一个银行网点,只有一个窗口服务,单线程 20 | //ExecutorService threadPool = Executors.newFixedThreadPool (5);//比如一个银行网点,开启了5个窗口 21 | ExecutorService threadPool = Executors.newCachedThreadPool ();//比如一个银行网点,可以拓展受理业务窗口 22 | try { 23 | //假设有10个顾客请求线程服务 24 | for (int i = 1; i <= 10; i++) { 25 | //TimeUnit.SECONDS.sleep (2); 26 | threadPool.execute (() -> { 27 | System.out.println (Thread.currentThread ().getName () + "\t办理业务!"); 28 | }); 29 | } 30 | } catch (Exception e) { 31 | e.printStackTrace (); 32 | } finally { 33 | threadPool.shutdown (); 34 | } 35 | 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/MyThreadPoolDemo2.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.concurrent.*; 4 | 5 | /** 6 | * @author laiqilin 7 | * @create 2019-10-11 22:05 8 | */ 9 | public class MyThreadPoolDemo2 { 10 | 11 | public static void main(String[] args) { 12 | /** 13 | * 自定义线程池 14 | */ 15 | ExecutorService threadPool = new ThreadPoolExecutor ( 16 | 2, //常驻线程数 17 | 5, //能够容纳的线程数 18 | 2L,//多余线程存活时间 19 | TimeUnit.SECONDS,//keepAliveTime单位 20 | new ArrayBlockingQueue<> (3),//任务队列(阻塞队列),被提交但是没有执行在队列等待中 21 | Executors.defaultThreadFactory (),//用于生成线程池中工作线程的工厂,默认 22 | //new ThreadPoolExecutor.AbortPolicy () 23 | //new ThreadPoolExecutor.DiscardPolicy () 24 | //new ThreadPoolExecutor.CallerRunsPolicy () 25 | new ThreadPoolExecutor.DiscardOldestPolicy () 26 | );//当队列满了,jdk自带的拒绝策略,四种拒绝策略 27 | 28 | 29 | try { 30 | //假设10个线程请求线程池处理业务 31 | for (int i = 1; i <= 9; i++) { 32 | threadPool.execute (() -> { 33 | System.out.println (Thread.currentThread ().getName () + "\t处理业务!"); 34 | }); 35 | } 36 | } catch (Exception e) { 37 | e.printStackTrace (); 38 | } finally { 39 | threadPool.shutdown (); 40 | } 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/ProdConsumerDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.concurrent.locks.Condition; 6 | import java.util.concurrent.locks.Lock; 7 | import java.util.concurrent.locks.ReentrantLock; 8 | 9 | //创建资源类 10 | class Aircondition { 11 | private int number = 0; 12 | //获取锁对象 13 | private Lock lock = new ReentrantLock(); 14 | //返回用于此锁实例的条件实例 15 | Condition condition = lock.newCondition(); 16 | 17 | public void decrement() { 18 | lock.lock(); 19 | try { 20 | while (number == 0) { 21 | //使当前线程等待,直到发出信号或中断为止 22 | condition.await(); 23 | } 24 | //干活 25 | number--; 26 | System.out.println(Thread.currentThread().getName() + "\t" + number); 27 | //通知 28 | //唤醒所有等待的线程 29 | condition.signalAll(); 30 | } catch (InterruptedException e) { 31 | e.printStackTrace(); 32 | } finally { 33 | lock.unlock(); 34 | } 35 | } 36 | 37 | public void increment() { 38 | lock.lock(); 39 | try { 40 | while (number != 0) { 41 | condition.await(); 42 | } 43 | //干活 44 | number++; 45 | System.out.println(Thread.currentThread().getName() + "\t" + number); 46 | //通知 47 | condition.signalAll(); 48 | } catch (InterruptedException e) { 49 | e.printStackTrace(); 50 | } finally { 51 | lock.unlock(); 52 | } 53 | } 54 | 55 | //==================================================== 56 | /* public synchronized void decrement() throws InterruptedException { 57 | //判断 58 | while (number == 0) { 59 | this.wait(); 60 | } 61 | //干活 62 | number--; 63 | System.out.println(Thread.currentThread().getName() + "\t" + number); 64 | //通知 65 | this.notifyAll(); 66 | } 67 | 68 | public synchronized void increment() throws InterruptedException { 69 | //判断 70 | while (number != 0) { 71 | this.wait(); 72 | } 73 | //干活 74 | number++; 75 | System.out.println(Thread.currentThread().getName() + "\t" + number); 76 | //通知 77 | this.notifyAll(); 78 | 79 | }*/ 80 | } 81 | 82 | /** 83 | * 使用线程模拟生产消费 84 | * 案例: 85 | * 现在两个线程,可以操作初始值为0的一个变量, 86 | * 实现一个线程对该变量加1,一个线程对该变量减1, 87 | * 实现交替,来回10轮,变量初始值为0. 88 | */ 89 | public class ProdConsumerDemo { 90 | 91 | public static void main(String[] args) { 92 | Aircondition aircondition = new Aircondition(); 93 | new Thread(() -> { 94 | for (int i = 1; i <= 10; i++) { 95 | try { 96 | TimeUnit.SECONDS.sleep(1); 97 | aircondition.increment(); 98 | } catch (InterruptedException e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | }, "A").start(); 103 | 104 | new Thread(() -> { 105 | for (int i = 1; i <= 10; i++) { 106 | try { 107 | TimeUnit.SECONDS.sleep(1); 108 | aircondition.decrement(); 109 | } catch (InterruptedException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | }, "B").start(); 114 | 115 | new Thread(() -> { 116 | for (int i = 1; i <= 10; i++) { 117 | try { 118 | TimeUnit.SECONDS.sleep(1); 119 | aircondition.increment(); 120 | } catch (InterruptedException e) { 121 | e.printStackTrace(); 122 | } 123 | } 124 | }, "C").start(); 125 | 126 | new Thread(() -> { 127 | for (int i = 1; i <= 10; i++) { 128 | try { 129 | TimeUnit.SECONDS.sleep(1); 130 | aircondition.decrement(); 131 | } catch (InterruptedException e) { 132 | e.printStackTrace(); 133 | } 134 | } 135 | }, "D").start(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/ReadWriteLockDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.locks.ReadWriteLock; 7 | import java.util.concurrent.locks.ReentrantReadWriteLock; 8 | 9 | class MyCache { 10 | private volatile Map map = new HashMap<> (); 11 | //创建读写锁对象 12 | ReadWriteLock readWriteLock = new ReentrantReadWriteLock (); 13 | 14 | //创建写操作 15 | public void put(String key, Object value) { 16 | try { 17 | //写入锁 18 | readWriteLock.writeLock ().lock (); 19 | System.out.println (Thread.currentThread ().getName () + "\t正在写入" + key); 20 | 21 | try { 22 | TimeUnit.SECONDS.sleep (2); 23 | } catch (InterruptedException e) { 24 | e.printStackTrace (); 25 | } 26 | map.put (key, value); 27 | System.out.println (Thread.currentThread ().getName () + "\t写入完成--" + key); 28 | } finally { 29 | //释放锁 30 | readWriteLock.writeLock ().unlock (); 31 | } 32 | 33 | } 34 | 35 | //创建读操作 36 | public Object get(String key) { 37 | Object result = null; 38 | try { 39 | readWriteLock.readLock ().lock (); 40 | System.out.println (Thread.currentThread ().getName () + "\t正在读出" + key); 41 | try { 42 | TimeUnit.SECONDS.sleep (2); 43 | } catch (InterruptedException e) { 44 | e.printStackTrace (); 45 | } 46 | result = map.get (key); 47 | System.out.println (Thread.currentThread ().getName () + "\t读出" + result); 48 | } finally { 49 | readWriteLock.readLock ().unlock (); 50 | } 51 | 52 | return result; 53 | } 54 | } 55 | 56 | /** 57 | * @author laiqilin 58 | * @create 2019-10-11 9:22 59 | */ 60 | 61 | public class ReadWriteLockDemo { 62 | /** 63 | * 使用读写锁模拟缓存机制 64 | */ 65 | public static void main(String[] args) { 66 | MyCache myCache = new MyCache (); 67 | for (int i = 1; i <= 5; i++) { 68 | final int num = i; 69 | new Thread (() -> { 70 | myCache.put (num + "", num + ""); 71 | }, String.valueOf (i)).start (); 72 | } 73 | for (int i = 1; i <= 5; i++) { 74 | final int num = i; 75 | new Thread (() -> { 76 | myCache.get (num + ""); 77 | }, String.valueOf (i)).start (); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/org/laiqilin/juc/SemaphoreDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.concurrent.Semaphore; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | /** 7 | * @author laiqilin 8 | * @create 2019-10-11 9:07 9 | */ 10 | public class SemaphoreDemo { 11 | private static final int COUNT_NUMBER = 3; 12 | 13 | /** 14 | * 在信号量上我们定义两种操作: 15 | * * acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1), 16 | * * 要么一直等下去,直到有线程释放信号量,或超时。 17 | * * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。 18 | * * 19 | * * 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。 20 | */ 21 | public static void main(String[] args) { 22 | Semaphore semaphore = new Semaphore (COUNT_NUMBER);//模拟三个停车位 23 | 24 | for (int i = 1; i <= 6; i++) { 25 | new Thread (() -> { 26 | try { 27 | semaphore.acquire (); 28 | System.out.println (Thread.currentThread ().getName () + "\t号抢到了停车位!"); 29 | TimeUnit.SECONDS.sleep (3); 30 | System.out.println (Thread.currentThread ().getName () + "\t号离开了!"); 31 | } catch (InterruptedException e) { 32 | e.printStackTrace (); 33 | } finally { 34 | semaphore.release (); 35 | } 36 | }, String.valueOf (i)).start (); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/StreamDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | class User { 7 | private int id; 8 | private String username; 9 | private int age; 10 | 11 | public User() { 12 | } 13 | 14 | public User(int id, String username, int age) { 15 | this.id = id; 16 | this.username = username; 17 | this.age = age; 18 | } 19 | 20 | 21 | public int getId() { 22 | return id; 23 | } 24 | 25 | public void setId(int id) { 26 | this.id = id; 27 | } 28 | 29 | public String getUsername() { 30 | return username; 31 | } 32 | 33 | public void setUsername(String username) { 34 | this.username = username; 35 | } 36 | 37 | public int getAge() { 38 | return age; 39 | } 40 | 41 | public void setAge(int age) { 42 | this.age = age; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "User{" + 48 | "id=" + id + 49 | ", username='" + username + '\'' + 50 | ", age=" + age + 51 | '}'; 52 | } 53 | } 54 | 55 | /** 56 | * @author laiqilin 57 | * @create 2019-10-11 22:32 58 | */ 59 | public class StreamDemo { 60 | public static void main(String[] args) { 61 | /** 62 | * 请按照给出数据, 63 | * 找出同时 64 | * 满足偶数ID 65 | * 且年龄大于24 66 | * 且用户名转为大写 67 | * 且用户名字母倒排序 68 | * 最后只输出一个用户名字 69 | */ 70 | User u1 = new User (11, "a", 23); 71 | User u2 = new User (12, "b", 24); 72 | User u3 = new User (13, "c", 22); 73 | User u4 = new User (14, "d", 28); 74 | User u5 = new User (16, "e", 26); 75 | 76 | List list = Arrays.asList (u1, u2, u3, u4, u5); 77 | list.stream ().filter (u -> { 78 | return u.getId () % 2 == 0; //过滤不是偶数 79 | }).filter (u -> { 80 | return u.getAge () > 24; //过滤小于年龄24 81 | }).map (u -> { 82 | return u.getUsername ().toUpperCase (); //返回一个对象 83 | }).sorted ((o1, o2) -> { 84 | return o2.compareTo (o1); 85 | }).limit (1).forEach (System.out::println); 86 | 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/org/laiqilin/juc/VolatileDemo.java: -------------------------------------------------------------------------------- 1 | package org.laiqilin.juc; 2 | 3 | /** 4 | * @author laiqilin 5 | * @create 2019-10-14 14:06 6 | *

7 | * 单例模式 8 | *

9 | * (推荐)DCL双端检查单例模式 10 | */ 11 | public class VolatileDemo { 12 | //禁止底层指令重排优化 加volatile 13 | public static volatile VolatileDemo instance = null; 14 | 15 | private VolatileDemo() { 16 | System.out.println ("私有构造方法....."); 17 | } 18 | 19 | /** 20 | * 单例模式 21 | * 问题: 22 | * 线程不安全 23 | * 解决方案: 24 | * 1. 在方法上加上synchronized关键字 25 | * 2. DCL双重检查机制 26 | */ 27 | 28 | public static VolatileDemo getInstance() { 29 | if (instance == null) { 30 | synchronized (VolatileDemo.class) { 31 | if (instance == null) { 32 | instance = new VolatileDemo (); 33 | } 34 | } 35 | } 36 | return instance; 37 | } 38 | 39 | 40 | public static void main(String[] args) { 41 | for (int i = 0; i <= 100; i++) { 42 | new Thread (() -> { 43 | VolatileDemo.getInstance (); 44 | }, String.valueOf (i)).start (); 45 | } 46 | } 47 | } 48 | --------------------------------------------------------------------------------