第11章 进程与多线程
1 /***************** 2 ***第11章 进程与多线程 3 *******知识点: 4 **************1.进程和线程的概念与原理 5 ******************1.1 进程 6 ******************1.2 线程 7 **************2.线程的创建和启动 8 ******************2.1 定义线程 9 ******************2.2 实例化线程 10 ******************2.3 应用 11 **************3.线程栈模型与线程的变量 12 **************4.状态转换 13 ******************4.1 五大状态 14 ******************4.2 生命周期 15 ******************4.3 状态关键字 16 **************5.同步和锁 17 ******************5.1 同步方法 18 ******************5.2 同步块 19 ******************5.3 生产者消费者模型 20 ******************5.4 死锁 21 ******************5.5 volatile关键字 22 **************6.交互 23 ******************6.1 交互关键字 24 **************7.调度 25 ******************7.1 休眠 26 ******************7.2 优先级 27 ******************7.3 让步 28 ******************7.4 合并 29 ******************7.5 守护线程 30 **************8.新特征 31 ******************8.1 线程池 32 ******************8.2 有返回值的线程 33 ******************8.3 锁调度 34 ******************8.4 信号量 35 ******************8.5 阻塞队列 36 ******************8.6 阻塞栈 37 ******************8.7 条件变量 38 ******************8.8 原子量 39 ******************8.9 障碍器 40 */ 41 42 import java.util.*; 43 import java.util.concurrent.*; //新特性包 44 45 /* 46 *继承Thread 演示线程类 47 */ 48 class MyThreadTest1 extends Thread{ 49 private String name; 50 public MyThreadTest1(){ 51 super("无名氏"); 52 this.name = "无名氏"; 53 } 54 public MyThreadTest1(String name){ 55 super(name); 56 this.name = name; 57 58 } 59 public void run(){ 60 for(int i = 0 ; i < 10; i++){ 61 System.out.println("线程类:" + this.getClass() + "——线程对象:" + name + "——打印出来:" + i); 62 if(i % 3 == 0){ 63 try{ 64 System.out.println("能整除3,我要休息一下!"); 65 Thread.sleep(1000);//睡眠1000毫秒 即1秒 66 }catch(InterruptedException e){ 67 System.out.println("线程异常!"); 68 } 69 } 70 } 71 } 72 73 } 74 75 /* 76 *实现Runnable接口 演示线程类 77 */ 78 class MyThreadTest2 implements Runnable{ 79 private String name; 80 public MyThreadTest2(){ 81 this.name = "无名氏"; 82 } 83 84 public MyThreadTest2(String name){ 85 this.name = name; 86 } 87 88 public void run(){ 89 for(int i = 0 ; i < 10; i++){ 90 System.out.println("线程类:" + this.getClass() + "——线程对象:" + name + "——打印出来:" + i); 91 } 92 } 93 } 94 95 /* 96 *演示线程池类 97 */ 98 class testPool extends Thread{ 99 public void run(){ 100 System.out.println(Thread.currentThread().getName() + "正在运行....."); 101 } 102 } 103 104 /* 105 *主任务类 106 */ 107 class MainTask implements Runnable{ 108 public void run(){ 109 System.out.println("主任务运行....."); 110 } 111 } 112 113 /* 114 *子任务类 115 */ 116 class SubTask implements Runnable{ 117 private String name; 118 private CyclicBarrier cb; //主任务障碍器 119 120 public SubTask(String name,CyclicBarrier cb){ 121 this.name = name; 122 this.cb = cb; 123 } 124 125 public void run(){ 126 System.out.println("子任务" + name + "开始运行....."); 127 for(int i = 0; i < 500000; i++){ 128 129 } 130 System.out.println("子任务" + name + "完成运行"); 131 try{ 132 cb.await(); //设置障碍器等待状态 133 }catch(InterruptedException e){ 134 e.printStackTrace(); 135 }catch(BrokenBarrierException e){ 136 e.printStackTrace(); 137 } 138 } 139 140 } 141 142 public class test11{ 143 public static void main(String[] args){ 144 demoBaseInfo();//演示1.进程和线程的概念与原理 145 demoCreateThread();//演示2.线程的创建和启动 146 demoThreadStack();//演示3.线程栈模型与线程的变量 147 demoThreadState();//演示4.状态转换 148 demoSynchronizeAndLock();//演示5.同步和锁 149 demoThreadInteraction();//演示6.交互 150 demoThreadDispatch();//演示7.调度 151 demoThreadNewFeatures();//演示8.新特征 152 } 153 154 /* 155 *1.进程和线程的概念与原理 156 */ 157 public static void demoBaseInfo(){ 158 /**1.1进程**/ 159 //进程:一个内存中运行的应用程序,拥有自己独立的一块内存空间,一个进程中可以有多个线程 160 161 /**1.2线程**/ 162 //线程:进程中的一段执行流程。一个进程中的多个线程共享进程中的内存数据(所以可以理解线程是轻量级的进程) 163 } 164 165 /* 166 *2.线程的创建和启动 167 */ 168 public static void demoCreateThread(){ 169 //两种方式创建进程 170 //1.继承Thread类 重写run方法 171 //2.实现Runnable接口 重写run方法 172 Thread mythread1 = new MyThreadTest1();//通过继承线程类创建实例 173 174 Thread mythread2 = new Thread(new MyThreadTest2("ciade"));//通过实现接口创建实例 注意与第一种方式的区别 175 176 System.out.println(Thread.currentThread());//Thread.currentThread()返回当前执行的线程对象 177 178 mythread1.start(); //只有调用了start()方法后,线程才能从新建状态移动到可运行状态 179 mythread2.start(); //只有调用了start()方法后,线程才能从新建状态移动到可运行状态 180 181 182 } 183 184 /* 185 *3.线程栈模型与线程的变量 186 */ 187 public static void demoThreadStack(){ 188 //单不使用线程时,虚拟机中的调用栈应该是main()->main()的调用的方法1->main()的调用的方法2 189 190 //使用线程时,当程序执行到start()方法时,会在原来栈上多出一个分支(可称为栈B 之前的栈为栈A),两条线并行执行 191 192 //理解即可 可配合画图说明 193 } 194 195 /* 196 *4.状态转换 197 */ 198 public static void demoThreadState(){ 199 /**4.1 五大状态**/ 200 //1.新建状态:线程对象已经定义声明并初始化,还没调用start()之前 201 //2.可运行状态: 调用start() 202 //3.运行状态:选择一个线程作为当前运行线程 203 //4.等待/阻塞/睡眠状态:线程还是活的,只是没有条件运行而已 204 //5.死亡状态:调用run()方法完成后的状态。注意如果在一个死去的线程上调用start()方法会出现异常 205 206 /**4.2 生命周期**/ 207 //新建(new)->可运行(start)->运行(run)->死亡(run结束) 208 //(其中运行状态可以变成等待/阻塞/睡眠状态(sleep),再变成可运行状态 ,如此循环) 209 //可配合画图理解 210 211 /**4.3 状态关键字**/ 212 //1.sleep关键字——————强制当前正在执行的线程休眠 213 // 使用方式:Thread.sleep(long millis)和Thread.sleep(long millis, int nanos) 214 // 注意事项: 215 // 1.线程睡眠是帮助所有线程获得运行机会的最好办法 216 // 2.线程睡眠到期自动苏醒,并返回到可运行状态,注意不是运行状态。 217 // 3.sleep()中指定的是时间是线程不会运行的最小时间,所以不能保证该线程睡眠到期后就开始运行 218 // 4.sleep()是静态方法,只能控制当前正在运行的线程 219 // 5.sleep()时有可能出现InterruptedException异常 注意try..catch 220 221 //2.yield关键字———————让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会 222 // 使用方式:Thread.yield() 223 // 注意事项: 224 // 1.yield()从未导致线程转到等待/睡眠/阻塞状态 225 // 2.在大多数情况下,yield()将导致线程从运行状态转到可运行状态 226 // 3.实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中 227 //所以这个关键字是然并卵 228 229 //3.线程的优先级 230 //线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。 231 //在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。 232 233 //设置优先级————————线程对象调用setPriority(int Priority)方法 其中Priority参数取值范围:1~10 234 //获取线程优先级——————线程对象调用getPriority()方法 235 //线程类优先级静态常量——————MAX_PRIORITY(最大) 、MIN_PRIORITY(最小)、NORM_PRIORITY(默认为5) 236 237 //4.join关键字——————让一个B线程加入到另一个A线程的尾部。即A执行完毕之前,B不能工作. 238 // 线程的加入join()对线程栈导致的结果是线程栈发生了变化,当然这些变化都是瞬时的 239 //5.interrupt关键字——————通知该线程中止,当改线程进行到阻塞状态时(sleep等),才会退出线程 240 //6.stop关键字——————强行退出线程,有可能导致异常发生,所以不建议使用该方法 241 242 } 243 244 /* 245 *5.同步和锁 246 */ 247 public static void demoSynchronizeAndLock(){ 248 //同步:是保证多线程安全访问竞争资源的一种手段 249 //同步方法: 250 /* 251 (1)wait() / notify()方法 252 (2)await() / signal()方法 253 (3)BlockingQueue阻塞队列方法 254 (4)PipedInputStream / PipedOutputStream 255 第四种暂不说明 256 */ 257 //锁:Java中每个对象都有一个内置锁 258 /* 259 关于锁和同步,有一下几个要点: 260 1)、只能同步方法,而不能同步变量和类; 261 2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步? 262 3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。 263 4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。 264 也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。 265 5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。 266 6)、线程睡眠时,它所持的任何锁都不会释放。 267 7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。 268 8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。 269 9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。 270 */ 271 272 /* 273 线程同步小结 274 1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。 275 2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。 276 3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。 277 4、对于同步,要时刻清醒在哪个对象上同步,这是关键。 278 5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。 279 6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。 280 7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。 281 */ 282 283 284 /**5.1 同步方法**/ 285 /* 286 class TicketSource implements Runnable{ 287 private int ticket = 50; 288 public void run(){ 289 for(int i = 1 ; i < 50; i++){ 290 try{ 291 Thread.sleep(100); 292 }catch(InterruptedException e){ 293 e.printStackTrace(); 294 } 295 sale(); 296 } 297 } 298 299 public synchronized void sale(){ //同步方法 300 if(ticket > 0){ 301 System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票"); 302 } 303 } 304 public static void main(String[] args){ 305 TicketSource mt = new TicketSource(); 306 new Thread(mt,"000").start(); 307 new Thread(mt,"001").start(); 308 new Thread(mt,"002").start(); 309 310 311 }*/ 312 /**5.2 同步块**/ 313 /* 314 class TicketSouce implements Runnable 315 { 316 //票的总数 317 private int ticket=10; 318 public void run() 319 { 320 for(int i=1;i<50;i++) 321 { 322 synchronized(this){//同步块 注意位置 不能放在if后面 323 if(ticket>0) 324 { 325 //休眠1s秒中,为了使效果更明显,否则可能出不了效果 326 try { 327 Thread.sleep(1000); 328 } catch (InterruptedException e) { 329 e.printStackTrace(); 330 } 331 System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票"); 332 } 333 } 334 } 335 } 336 } 337 */ 338 339 //总结:在使用同步块的时候一定要清楚同步块的位置,因此推荐使用同步方法 340 341 /**5.3 生产者消费者模型**/ 342 /*对于此模型,应该明确一下几点: 343 1、生产者仅仅在仓储未满时候生产,仓满则停止生产。 344 2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。 345 3、当消费者发现仓储没产品可消费时候会通知生产者生产。 346 4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。 347 */ 348 349 /*三种实现方式*/ 350 351 /*1.wait()/notifyAll()方式 352 //仓库类 353 class Storge{ 354 private final int MAX_SIZE = 100; 355 private LinkedList<Object> list = new LinkedList<Object>(); 356 357 public int getMAX_SIZE(){ 358 return MAX_SIZE; 359 } 360 361 public void setList(LinkedList<Object> list){ 362 this.list = list; 363 } 364 365 public LinkedList<Object> getList(){ 366 return list; 367 } 368 public Storge(){ 369 370 } 371 372 //生产方法 373 public void produce(int num,String name){ 374 synchronized(list){ 375 while(list.size() + num > MAX_SIZE ){ //判断会不会满仓溢出 376 System.out.println("要生产的产品数量:" + num + " 库存量:" 377 + list.size() + " " + name +"暂时不能执行生产任务"); 378 try{ 379 list.wait();//设置阻塞状态 380 }catch(InterruptedException e){ 381 e.printStackTrace(); 382 } 383 } 384 for(int i = 1; i <= num ;i++){ 385 list.add(new Object());//生产 386 } 387 System.out.println(name + "已经生产了产品数量:" + num + " 库存量现在为:" + list.size()); 388 list.notifyAll();//唤醒其他等待线程 389 } 390 391 } 392 393 //消费方法 394 public void consume(int num,String name){ 395 synchronized(list){ 396 while(num > list.size() ){//判断有没可消费的库存 397 System.out.println("要消费的产品数量:" + num + " 库存量:" 398 + list.size() + " " + name +"暂时不能执行消费任务"); 399 try{ 400 list.wait();//设置阻塞状态 401 }catch(InterruptedException e){ 402 e.printStackTrace(); 403 } 404 405 } 406 for(int i = 1; i <= num ;i++){ 407 list.remove();//移除消费项 408 } 409 System.out.println(name + "已经消费了产品数量:" + num + " 库存量现在为:" + list.size()); 410 list.notifyAll();//唤醒其他等待线程 411 } 412 413 } 414 415 } 416 417 //生产类 418 class Producer extends Thread{ 419 private int num; //每次生产的数量 420 private Storge storge; 421 private String name; 422 423 public Producer(int num,Storge storge,String name){ 424 this.num = num; 425 this.storge = storge; 426 this.name = name; 427 } 428 429 public void run(){ 430 produce(num,name); 431 } 432 public void produce(int num ,String name){ 433 storge.produce(num,name); 434 } 435 } 436 437 438 //消费类 439 class Consumer extends Thread{ 440 private int num ; //设置每次消费数目 441 private Storge storge; //设置仓库 442 private String name; 443 444 public Consumer(int num,Storge storge,String name){ 445 this.num = num; 446 this.storge = storge; 447 this.name = name; 448 } 449 450 451 public void run(){ 452 consume(num,name); 453 } 454 455 public void consume(int num,String name){ 456 storge.consume(num,name); 457 } 458 459 main()方法中 460 //仓库对象 461 Storge storge = new Storge(); 462 463 //生产对象 464 Producer p1 = new Producer(20,storge,"p1"); 465 Producer p2 = new Producer(30,storge,"p2"); 466 Producer p3 = new Producer(50,storge,"p3"); 467 Producer p4 = new Producer(10,storge,"p4"); 468 Producer p5 = new Producer(10,storge,"p5"); 469 Producer p6 = new Producer(80,storge,"p6"); 470 471 472 //消费对象 473 Consumer c1 = new Consumer(50,storge,"c1"); 474 Consumer c2 = new Consumer(20,storge,"c2"); 475 Consumer c3 = new Consumer(30,storge,"c3"); 476 477 }*/ 478 479 /*2.await() / signal()方式(可取代第一种方式) 480 第一步:引入相关包 481 import java.util.concurrent.locks.Condition; 482 import java.util.concurrent.locks.Lock; 483 import java.util.concurrent.locks.ReentrantLock; 484 485 第二步:重写仓库类 486 class Storge{ 487 private final int MAX_SIZE = 100; 488 private LinkedList<Object> list = new LinkedList<Object>(); 489 private final Lock lock = new ReentrantLock(); //添加锁对象 490 private final Condition full = lock.newCondition();//添加条件1 491 private final Condition empty = lock.newCondition();//添加条件2 492 493 public int getMAX_SIZE(){ 494 return MAX_SIZE; 495 } 496 497 public void setList(LinkedList<Object> list){ 498 this.list = list; 499 } 500 501 public LinkedList<Object> getList(){ 502 return list; 503 } 504 public Storge(){ 505 506 } 507 508 //生产 509 public void produce(int num,String name){ 510 lock.lock();//加锁 511 while(list.size() + num > MAX_SIZE ){ //判断会不会满仓溢出 512 System.out.println("要生产的产品数量:" + num + " 库存量:" 513 + list.size() + " " + name +"暂时不能执行生产任务"); 514 try{ 515 full.await();//设置生产阻塞状态 516 }catch(InterruptedException e){ 517 e.printStackTrace(); 518 } 519 } 520 for(int i = 1; i <= num ;i++){ 521 list.add(new Object());//生产 522 } 523 System.out.println(name + "已经生产了产品数量:" + num + " 库存量现在为:" + list.size()); 524 full.signalAll();//唤醒其他等待线程 525 empty.signalAll(); 526 lock.unlock(); //解锁 527 528 } 529 530 //消费 531 public void consume(int num,String name){ 532 lock.lock();//加锁 533 while(num > list.size() ){//判断有没可消费的库存 534 System.out.println("要消费的产品数量:" + num + " 库存量:" 535 + list.size() + " " + name +"暂时不能执行消费任务"); 536 try{ 537 empty.await();//设置消费阻塞状态 538 }catch(InterruptedException e){ 539 e.printStackTrace(); 540 } 541 542 } 543 for(int i = 1; i <= num ;i++){ 544 list.remove();//移除消费项 545 } 546 System.out.println(name + "已经消费了产品数量:" + num + " 库存量现在为:" + list.size()); 547 full.signalAll();//唤醒其他等待线程 548 empty.signalAll(); 549 lock.unlock(); //解锁 550 551 } 552 553 } 554 555 其他跟第一种方式一样 (达到无需修改main()方法中相关代码和消费类、生产类即可实现) 556 //推荐以后使用此方式实现同步 557 */ 558 559 /*3.BlockingQueue阻塞队列方式 560 第一步:引入相关包 561 import java.util.concurrent.LinkedBlockingQueue; 562 第二步:重写仓库类 563 class Storge{ 564 private final int MAX_SIZE = 100; 565 private LinkedBlockingQueue<Object> list = new LinkedBlockingQueue<Object>();//定义一个可同步的list 566 567 public int getMAX_SIZE(){ 568 return MAX_SIZE; 569 } 570 571 public void setList(LinkedBlockingQueue<Object> list){ 572 this.list = list; 573 } 574 575 public LinkedBlockingQueue<Object> getList(){ 576 return list; 577 } 578 public Storge(){ 579 580 } 581 582 //生产 583 public void produce(int num,String name){ 584 if(list.size() + num > MAX_SIZE ){ //判断会不会满仓溢出 585 System.out.println("要生产的产品数量:" + num + " 库存量:" 586 + list.size() + " " + name +"暂时不能执行生产任务"); 587 588 } 589 for(int i = 1; i <= num ;i++){ 590 try{ 591 list.put(new Object());//生产 592 }catch(InterruptedException e){ 593 e.printStackTrace(); 594 } 595 System.out.println(name + "已经生产了产品数量:" + num + " 库存量现在为:" + list.size()); 596 } 597 598 599 } 600 601 //消费 602 public void consume(int num,String name){ 603 604 if(num > list.size() ){//判断有没可消费的库存 605 System.out.println("要消费的产品数量:" + num + " 库存量:" 606 + list.size() + " " + name +"暂时不能执行消费任务"); 607 } 608 for(int i = 1; i <= num ;i++){ 609 try{ 610 list.take();//移除消费项 611 }catch(InterruptedException e){ 612 e.printStackTrace(); 613 } 614 System.out.println(name + "已经消费了产品数量:" + num + " 库存量现在为:" + list.size()); 615 } 616 } 617 618 } 619 其他跟第一种方式一样 (达到无需修改main()方法中相关代码和消费类、生产类即可实现) 620 但可能会出现put跟打印出来的结果不一样。 621 */ 622 623 //总结:推荐使用第二种方式实现同步 624 625 /**5.4 死锁**/ 626 //发生死锁的原因一般是两个对象的锁相互等待造成的 627 628 /***5.5 volatile关键字**/ 629 //Java包含两种内在的同步机制:同步块(或方法)和 volatile变量。这两种机制的提出都是为了实现代码线程的安全性。 630 //其中 Volatile变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。所以你可以忘了它了 631 } 632 633 /* 634 *6.交互 635 */ 636 public static void demoThreadInteraction(){ 637 /**6.1 交互关键字**/ 638 //notify关键字:唤醒在此对象监视器上等待的单个线程 639 //notifyAll关键字:唤醒在此对象监视器上等待的所有线程 640 //wait关键字:导致当前的线程等待,直到其他线程调用此对象的notify()方法或者notifyAll()方法 641 //其中wait()方法有两个重载的方法: 642 // 1.void wait(long timeout)————导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法,或者超过指定的时间量。 643 // 2.void wait(long timeout, int nanos) —————导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。 644 645 } 646 647 /* 648 *7.调度 649 */ 650 public static void demoThreadDispatch(){ 651 /**7.1 休眠**/ 652 //休眠目的————————使线程让出CPU的最简单的做法之一,线程休眠时候,会将CPU资源交给其他线程,以便能轮换执行,当休眠一定时间后,线程会苏醒,进入准备状态等待执行 653 //实现方式:Thread.sleep(long millis)和Thread.sleep(long millis, int nanos),均为静态方法,谁调用就休眠谁 654 655 /**7.2 优先级**/ 656 //优先级高的线程获取CPU资源的概率较大 657 //优先级用1-10之间的整数表示,默认为5 658 //一般情况下,子线程优先级跟父线程一致 659 660 /**7.3 让步**/ 661 //让步——————使当前运行着线程让出CPU资源,但是然给谁不知道,仅仅是让出,线程状态回到可运行状态。 662 //实现方式:Thread.yield()方法,yield()为静态方法,功能是暂停当前正在执行的线程对象,并执行其他线程。 663 664 /**7.4 合并**/ 665 //合并——————将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行 666 //实现方式:调用join()方法 非静态方法 667 //其中join方法重载方法有: 668 // 1.void join() 等待该线程终止。 669 // 2.void join(long millis) 等待该线程终止的时间最长为 millis毫秒。 670 // 3.void join(long millis,int nanos) 等待该线程终止的时间最长为 millis毫秒 + nanos 纳秒。 671 672 /**7.5 守护线程**/ 673 //守护线程———————为其他线程的运行提供服务 674 //实现方式:线程对象的方法setDaemon(true),则可以将其设置为守护线程 675 //注意: 676 // 1.调用setDaemon()必须在start()方法前 677 // 2.Daemon线程中产生的新线程也是Daemon的 678 // 3.不是所有的应用都可以分配给Daemon线程来进行服务(因为有可能线程还没来得及进行操作时,虚拟机就已经退出了) 679 680 } 681 682 /* 683 *8.新特征 684 */ 685 public static void demoThreadNewFeatures(){ 686 //新特性指的是在Java6及之后版本所拥有线程相关的新特性 687 /**8.1 线程池**/ 688 //线程池分好多种:固定尺寸的线程池、单任务线程池、可变尺寸连接池、延迟连接池、自定义线程池等 689 ExecutorService pool_1 = Executors.newFixedThreadPool(3);//创建一个固定可重用的线程池 容量为3 690 ExecutorService pool_2 = Executors.newSingleThreadExecutor();//创建一个单任务线程池 691 ExecutorService pool_3 = Executors.newCachedThreadPool();//创建一个可变尺寸的线程池 692 ScheduledExecutorService pool_4 = Executors.newScheduledThreadPool(4);//创建一个延迟线程池 693 694 Thread t1 = new testPool(); 695 Thread t2 = new testPool(); 696 Thread t3 = new testPool(); 697 Thread t4 = new testPool(); 698 699 700 System.out.println("固定可重用的线程池演示!"); 701 //将线程放入线程池中执行 702 pool_1.execute(t1); 703 pool_1.execute(t2); 704 pool_1.execute(t3); 705 pool_1.execute(t4); 706 pool_1.shutdown();//关闭 707 708 System.out.println("单任务线程池演示!"); 709 pool_2.execute(t1); 710 pool_2.execute(t2); 711 pool_2.execute(t3); 712 pool_2.shutdown();//关闭 713 714 System.out.println("可变尺寸线程池演示!"); 715 pool_3.execute(t1); 716 pool_3.execute(t2); 717 pool_3.execute(t3); 718 pool_3.shutdown();//关闭*/ 719 720 System.out.println("延迟线程池演示!"); 721 pool_4.execute(t1); 722 pool_4.execute(t2); 723 724 //使用延迟执行风格的方法 725 //第一个参数是延迟执行线程对象,第二个参数是延迟数量,第三个参数是延迟的单位 726 pool_4.schedule(t3, 1000, TimeUnit.MILLISECONDS); //t3 延迟 1000毫秒 727 pool_4.schedule(t4, 10, TimeUnit.SECONDS); //t4 延迟 10秒 728 pool_4.shutdown();//关闭 729 730 /**8.2 有返回值的线程**/ 731 //可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口(Thread类也实现Runnable接口) 732 //实现方式:执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了 733 /* 734 735 class MyCallable implements Callable{ 736 private String oid; 737 738 MyCallable(String oid) { 739 this.oid = oid; 740 } 741 742 @Override 743 public Object call()throws Exception { 744 return oid+"任务返回的内容"; 745 } 746 } 747 748 public static void main(String[] args)throws ExecutionException, InterruptedException { 749 //创建一个线程池 750 ExecutorService pool = Executors.newFixedThreadPool(2); 751 752 //创建两个有返回值的任务 753 Callable c1 = new MyCallable("A"); 754 Callable c2 = new MyCallable("B"); 755 756 //执行任务并获取Future对象 757 Future f1 = pool.submit(c1); 758 Future f2 = pool.submit(c2); 759 760 //从Future对象上获取任务的返回值,并输出到控制台 761 System.out.println(">>>"+f1.get().toString()); 762 System.out.println(">>>"+f2.get().toString()); 763 764 //关闭线程池 765 pool.shutdown(); 766 } 767 768 //更多使用详见API文档 769 */ 770 771 /**8.3 锁**/ 772 /**具体使用参考本文上面的生产者和消费者模型的第二种实现方式**/ 773 774 /**8.4 信号量**/ 775 /* 776 信号量实际上是一个功能完毕的计数器,对控制一定资源的消费与回收有着很重要的意义,信号量常常用于多线程的代码中, 777 并能监控有多少数目的线程等待获取资源,并且通过信号量可以得知可用资源的数目等等,这里总是在强调“数目”二字, 778 但不能指出来有哪些在等待,哪些资源可用。 779 貌似用处不大,使用的时候再查看相关文档即可 780 */ 781 782 /**8.5 阻塞队列**/ 783 /**具体使用参考本文上面的生产者和消费者模型的第三种实现方式**/ 784 785 /**8.6 阻塞栈**/ 786 /**用法与阻塞队列相似,不同的是栈是后入先出的结构,每次操作都是栈顶元素,而队列则是先进先出结构,每次操作都是队头**/ 787 788 /**8.7 条件变量**/ 789 /**具体使用参考本文上面的生产者和消费者模型的第二种实现方式**/ 790 791 /**8.8 原子量**/ 792 //原子量:操作变量的操作是原子的,不可再分 793 /* 794 class MyRunnable implements Runnable { 795 privatestatic AtomicLong aLong =new AtomicLong(10000); //原子量,每个线程都可以自由操作 796 private String name; //操作人 797 privateint x; //操作数额 798 private Lock lock; 799 800 MyRunnable(String name, int x,Lock lock) { 801 this.name = name; 802 this.x = x; 803 this.lock = lock; 804 } 805 806 publicvoid run() { 807 lock.lock(); 808 System.out.println(name + "执行了" + x +",当前余额:" + aLong.addAndGet(x)); 809 lock.unlock(); 810 } 811 812 public static void main(String[] args) { 813 ExecutorService pool = Executors.newFixedThreadPool(2); 814 Lock lock = new ReentrantLock(false); 815 Runnable t1 = new MyRunnable("张三", 2000,lock); 816 Runnable t2 = new MyRunnable("李四", 3600,lock); 817 Runnable t3 = new MyRunnable("王五", 2700,lock); 818 Runnable t4 = new MyRunnable("老张", 600,lock); 819 Runnable t5 = new MyRunnable("老牛", 1300,lock); 820 Runnable t6 = new MyRunnable("胖子", 800,lock); 821 //执行各个线程 822 pool.execute(t1); 823 pool.execute(t2); 824 pool.execute(t3); 825 pool.execute(t4); 826 pool.execute(t5); 827 pool.execute(t6); 828 //关闭线程池 829 pool.shutdown(); 830 } 831 832 */ 833 /**8.9 障碍器**/ 834 //障碍器是多线程并发控制的一种手段。 835 //应用场景:如果一个主干任何中有许多子任务需要执行,那么只有在所有子任务执行完毕后,主任务才能执行 836 837 //构造主任务障碍器 第一个参数子任务个数,第二参数主任务对象 838 CyclicBarrier cb = new CyclicBarrier(7,new MainTask()); 839 840 new Thread(new SubTask("A",cb)).start();//创建子任务线程 841 new Thread(new SubTask("B",cb)).start(); 842 new Thread(new SubTask("C",cb)).start(); 843 new Thread(new SubTask("D",cb)).start(); 844 new Thread(new SubTask("E",cb)).start(); 845 new Thread(new SubTask("F",cb)).start(); 846 new Thread(new SubTask("G",cb)).start(); 847 848 } 849 850 /**部分文档参考出处:http://blog.csdn.net/shimiso**/ 851 }