黑马程序员:我对多线程的总结以及练习(重点)
来源:互联网 发布:软件敏捷开发模式 编辑:程序博客网 时间:2024/05/16 17:51
1.概述进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。一个进程中至少有一个线程。线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。多线程存在的意义:充分利用cpu的空闲时间,提高进程的整体运行效率。 2.创建线程的两种方式第一种:继承Thread类步骤: 1.子类覆盖父类中的run方法,将线程运行的代码存放在run中。 2.建立子类对象的同时线程也被创建。 3.通过调用start方法开启线程。示例代码:[java] view plaincopy1.class Demo extends Thread 2.{ 3. public void run() 4. { 5. for(int i = 0;i<60;i++) 6. { 7. System.out.println(“demo run---+”i); 8. } 9. } 10.} 11. 12.class ThreadDemo 13.{ 14. public static void main(String[] args) 15. { 16. Demo d = new Demo();//创建一个线程 17. d.start();//调用start方法执行该线程的run方法 18. } 19.} 第二种:实现Runnable接口步骤: 1.子类覆盖接口中的run方法 2.通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。 3.Thread类对象调用start方法开启线程。代码示例:[java] view plaincopy1.class Test implement Runnable 2.{ 3. public void run()//子类覆盖接口中的run方法 4. { 5. for(int x=0; x<60; x++) 6. { 7. System.out.println(“run..."+x); 8. } 9. } 10. 11.} 12. 13. 14.class ThreadTest 15.{ 16. public static void main(String[] args) 17. { 18. Test te = new Test();实现Runnable接口的对象 19. Thread t1 = new Thread(te);将那个对象作为参数传递到Thread构造函数 20. Thread t2 = new Thread(te); 21. t1.start();//调用Thread类的start方法开启线程 22. t2.start(); 23. } 24.} 两种线程创建方式的区别: 继承Thread类创建对象: 1.Thread子类无法再从其它类继承(java语言单继承)。 2.编写简单,run()方法的当前对象就是线程对象,可直接操作。 使用Runnable接口创建线程: 1.可以将CPU,代码和数据分开,形成清晰的模型。 2.线程体run()方法所在的类可以从其它类中继承一些有用的属性和方法。 3.有利于保持程序的设计风格一致。 继承Thread类线程代码存放于Thread子类run方法中; 实现Runnable接口线程代码存在于接口的子类run方法中,而且这种方式避免了单继承的局限性,在实际应用中,几乎都采取第二种方式。 问题:为什么要将Runnable接口的子类对象传递给Thread的构造函数?答:因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run方法,就必须明确该run方法所属的对象。 3.线程的四种状态sleep方法需要指定睡眠时间,单位是毫秒。冻结与运行之间的状态:准备就绪。具备了执行资格,但是没有执行权。 4.线程的对象获取与名称的操作线程都有自己的默认名称,格式为:Thread-编号 该编号从0开始。操作方法:static Thread currentThread():获取当前线程对象。getName(): 获取线程名称。setName或者构造函数:设置线程名称。示例代码: [java] view plaincopy1.class Test extends Thread 2.{ 3. Test(String name)//构造方法设置线程名称 4. { 5. super(name);//继承父类方法 6. } 7. public void run()//执行代码 8. { 9. for(int x=0; x<60; x++) 10. { 11. System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x); 12. } 13. } 14. 15.} 16. 17.class ThreadTest 18.{ 19. public static void main(String[] args) 20. { 21. Test t1 = new Test("one---");//调用构造函数设置线程名称 22. Test t2 = new Test("two+++"); 23. t1.start(); 24. t2.start(); 25. 26. for(int x=0; x<60; x++) 27. { 28. System.out.println("main....."+x); 29. } 30. } 31.} 5.线程的安全导致安全问题的出现的原因: 1.多个线程访问出现延迟 2.线程随机性线程安全问题在理想状态下,不容易出现,但是一旦出现对软件的影响是非常大的解决方法:同步(synchronized)格式: synchronized(对象) { 需要同步的代码; }同步可以解决安全问题的根本原因就是在那个对象上,该对象如同锁的功能。4个窗口售票示例:[java] view plaincopy1.class Ticket implements Runnable 2.{ 3. private int tick = 1000; 4. Object obj = new Object();//建立一个obj类对象,供synchronized作参数 5. public void run() 6. { 7. while(true) 8. { 9. synchronized(obj)//同步锁 10. { 11. if(tick>0) 12. System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); 13. 14. } 15. } 16. } 17.} 18. 19.class TicketDemo2 20.{ 21. public static void main(String[] args) 22. { 23. 24. Ticket t = new Ticket();//创建Ticket对象供Thread对象作构造函数的参数用 25. 26. Thread t1 = new Thread(t); 27. Thread t2 = new Thread(t); 28. Thread t3 = new Thread(t); 29. Thread t4 = new Thread(t); 30. //开始4个线程 31. t1.start(); 32. t2.start(); 33. t3.start(); 34. t4.start(); 35. 36. } 37.} 同步的前提:|---->同步需要两个或者两个以上的线程|---->多个线程使用的是同一个锁未满足这两个条件,不能称其同步。同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行的效率。同步函数 格式:在函数上加上synchronized修饰符即可。 同步函数使用的锁:函数需要被对象调用,那么函数都有一个所属对象引用,就是this。 所以同步函数使用的锁是this。关于死锁 同步中嵌套同步,但是锁却不同,就会产生死锁 如:A线程持有A锁,B线程持有B锁,让A线程去抢夺B锁,B线程去抢夺A锁[java] view plaincopy1.class Dead implements Runnable 2.{ 3. private booleanb = false; 4. Dead(booleanb) 5. { 6. this.b = b; 7. } 8. public void run() 9. { 10. while(true) 11. { 12. if(b) 13. { 14. synchronized(Locks.locka) 15. {//0线程,持有了A锁 16. System.out.println(Thread.currentThread().getName()+"locka...+..if"); 17. //等待B锁 18. synchronized(Locks.lockb) 19. { 20. System.out.println(Thread.currentThread().getName()+"lockb...+..if"); 21. } 22. } 23. } 24. else 25. { 26. synchronized(Locks.lockb) 27. { 28. //1线程就进来了,持有了B锁 29. System.out.println(Thread.currentThread().getName()+"lockb...+..else"); 30. synchronized(Locks.locka)//等待获得A锁 31. { 32. System.out.println(Thread.currentThread().getName()+"locka...+..else"); 33. 34. } 35. } 36. } 37. } 38. } 39.} 40.//创造锁 41.class Locks 42.{ 43. public static Object locka = new Object(); 44. public static Object lockb = new Object(); 45.} 46.class DeadLock 47.{ 48. public static void main(String[]args) 49. { 50. Dead d1 = new Dead(true); 51. Dead d2 = new Dead(false); 52. Thread t1 = new Thread(d1); 53. Thread t2 = new Thread(d2); 54. t1.start(); 55. t2.start(); 56. } 57.} 6.线程间通信多个线程操作同一个资源,但是操作的动作不同 注意:wait();notify();notifyAll()都使用在同步中,因为要对持有监视器的线程操作,所以要使用在同步中,因为只有同步才具有锁。问题:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中? 1.这些方法存在与同步中。 2.使用这些方法时必须要标识所属的同步的锁。 3.锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。注意:不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。问题:wait(),sleep()有什么区别? wait():释放cpu执行权,释放锁。 sleep():释放cpu执行权,不释放锁。 新特性:JDK1.5之后新增了java.until.concurrent.locks这个包为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。该框架允许更灵活地使用锁和条件,但以更难用的语法为代价。生产和消费实例中升级:JDK1.5 中提供了多线程升级解决方案。 将同步Synchronized替换成现实Lock操作。 将Object中的wait,notify notifyAll,替换了Condition对象。 该对象可以Lock锁 进行获取。 该示例中,实现了本方只唤醒对方操作。代码如下:[java] view plaincopy1.import java.util.concurrent.locks.*; 2. 3.class ProducerConsumerDemo 4.{ 5. public static void main(String[] args) 6. { 7. Resource r = new Resource(); 8. 9. Producer pro = new Producer(r); 10. Consumer con = new Consumer(r); 11. 12. Thread t1 = new Thread(pro); 13. Thread t2 = new Thread(pro); 14. Thread t3 = new Thread(con); 15. Thread t4 = new Thread(con); 16. 17. t1.start(); 18. t2.start(); 19. t3.start(); 20. t4.start(); 21. 22. } 23.} 24. 25.class Resource 26.{ 27. private String name; 28. private int count = 1; 29. private boolean flag = false; 30. private Lock lock = new ReentrantLock(); 31. 32. private Condition condition_pro = lock.newCondition(); 33. private Condition condition_con = lock.newCondition(); 34. 35. 36. public void set(String name)throws InterruptedException 37. { 38. lock.lock(); 39. try 40. { 41. while(flag) 42. condition_pro.await(); 43. this.name = name+"--"+count++; 44. 45. System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); 46. flag = true; 47. condition_con.signal(); 48. } 49. finally 50. { 51. lock.unlock();//释放锁的动作一定要执行。 52. } 53. } 54. public void out()throws InterruptedException 55. { 56. lock.lock(); 57. try 58. { 59. while(!flag) 60. condition_con.await(); 61. System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name); 62. flag = false; 63. condition_pro.signal(); 64. } 65. finally 66. { 67. lock.unlock(); 68. } 69. 70. } 71.} 72. 73.class Producer implements Runnable 74.{ 75. private Resource res; 76. 77. Producer(Resource res) 78. { 79. this.res = res; 80. } 81. public void run() 82. { 83. while(true) 84. { 85. try 86. { 87. res.set("+商品+"); 88. } 89. catch (InterruptedException e) 90. { 91. } 92. 93. } 94. } 95.} 96. 97.class Consumer implements Runnable 98.{ 99. private Resource res; 100. 101. Consumer(Resource res) 102. { 103. this.res = res; 104. } 105. public void run() 106. { 107. while(true) 108. { 109. try 110. { 111. res.out(); 112. } 113. catch (InterruptedException e) 114. { 115. } 116. } 117. } 118.} 7.停止线程1.定义循环结束标记(run方法结束) 因为线程运行代码一般都是循环,只要控制了循环即可。2.使用interrupt(中断)方法。 该方法是结束线程的冻结状态,使线程回到运行状态中来。注:stop方法已经过时不再使用。 特殊情况: 当线程处于了冻结状态,就不会读取到标记。那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。 Thread类提供该方法 interrupt();测试代码:[java] view plaincopy1.class StopThread implements Runnable 2.{ 3. private boolean flag =true; 4. public synchornized void run() 5. { 6. while(flag) 7. { 8. try 9. { 10. wait();//调用线程等待 11. } 12. catch(InterruptedException e) 13. { 14. System.out.println(Thread.currentThread().getName()+"...Exception"); 15. flag = false;//在异常中控制线程的运行 16. } 17. System.out.println(Thread.currentThread().getName()+"....run"); 18. } 19. } 20. public void changeFlag() 21. { 22. flag = false; 23. } 24.} 25. 26.class StopThreadDemo 27.{ 28. public static void main(String[] args) 29. { 30. StopThread st = new StopThread(); 31. 32. Thread t1 = new Thread(st); 33. Thread t2 = new Thread(st); 34. 35. 36. t1.setDaemon(true); 37. t2.setDaemon(true); 38. t1.start(); 39. t2.start(); 40. 41. int num = 0; 42. 43. while(true) 44. { 45. if(num++ == 60) 46. { 47. //st.changeFlag(); 48. t1.interrupt();//调用interrupt方法来中断线程,抛出异常 49. t2.interrupt(); 50. break; 51. } 52. System.out.println(Thread.currentThread().getName()+"......."+num); 53. } 54. System.out.println("over"); 55. } 56.} 8.线程类的其他方法守护线程 setDaemon():将该线程标记为守护线程或用户线程,守护线程就相当于后台线程,当前台线程结束时,后台线程跟着也结束。 注意:该方法必须在启动线程前调用。 该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。用法示例代码如下:[java] view plaincopy1.class StopThread implements Runnable 2.{ 3. public void run()//原本run方法是一个死循环,但是将线程定义为守护线程后,主线程结束,访问run方法的线程立即也结束了 4. { 5. while(true) 6. { 7. System.out.println(Thread.currentThread().getName()+"....run"); 8. } 9. } 10.} 11.class StopThreadDemo 12.{ 13. public static void main(String[] args) 14. { 15. StopThread st = new StopThread(); 16. Thread t1 = new Thread(st); 17. Thread t2 = new Thread(st); 18. //将t1,t2定义为守护线程 19. t1.setDaemon(true); 20. t2.setDaemon(true); 21. t1.start();//开始线程 22. t2.start(); 23. int num = 0; 24. while(true) 25. { 26. if(num++ == 60) 27. { 28. break; 29. } 30. System.out.println(Thread.currentThread().getName()+"......."+num); 31. } 32. System.out.println("over");//主线程结束,t1,t2也结束 33. } 34.} join()抢夺cpu执行权,适用于临时加入线程用,先执行完,其他线程才执行。测试代码如下:[java] view plaincopy1.class Demo implements Runnable 2.{ 3. public void run() 4. { 5. for(int x=0; x<70; x++) 6. { 7. System.out.println(Thread.currentThread().getName()+"....."+x); 8. } 9. } 10.} 11.class JoinDemo 12.{ 13. public static void main(String[] args) throws Exception 14. { 15. Demo d = new Demo(); 16. Thread t1 = new Thread(d); 17. Thread t2 = new Thread(d); 18. t1.start(); 19. t1.join();//t1获取cpu的执行权,主线程处于冻结状态,只有t1结束主线程才能恢复运行状态 20. t2.start(); 21. //t1.join();主线程冻结,t1,t2交替运行,t1结束,主线程才继续 22. for(int x=0; x<80; x++) 23. { } 24. System.out.println("over"); 25. } 26.} setPriority()更改线程的优先级。三个优先级分别为: MAX_PRIORITY(最高优先级,10) MIN_PRIORITY(最低优先级,1) NORM_PRIORITY(默认优先级,5)首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。在其他情况下,线程优先级被设定为指定的newPriority 和该线程的线程组的最大允许优先级相比较小的一个。 yeild()暂停当前正在执行的线程对象,并执行其他线程。(使线程交替执行) 个人总结: 线程的运行状态与线程之间的通信个人很难理解,可能还得多多写代码操练才行,线程的操作给我的感觉就是你必须要考虑得很全面,每个线程的执行状态,每个线程读取的数据什么的都必须要考虑清楚,这要才能避免线程的安全问题。还是觉得线程这一块比较难,得多下点工夫。
0 0
- 黑马程序员:我对多线程的总结以及练习(重点)
- 黑马程序员:关于我对IO流的总结以及习题(面试必考项目)
- 黑马程序员_我的多线程卖票练习
- 黑马程序员_java的多线程(对第十一课创建多线程及多线程安全问题总结)
- 黑马程序员+对Java中多线程总结
- 黑马程序员-java的多线程的练习
- “黑马程序员"我的总结
- 黑马程序员_我的死锁练习
- 黑马程序员—C重点--总结一些琐碎的知识点
- 黑马程序员--对多线程的理解
- 黑马程序员--对多线程的理解
- 黑马程序员_多线程练习
- 黑马程序员_2014 7月份 我对使用多线程的心得
- 黑马程序员 多线程总结
- 黑马程序员 多线程总结
- 多线程总结-----黑马程序员
- 黑马程序员-多线程总结
- 黑马程序员-多线程总结
- Objective C在目录中查找指定的文件
- POJ1734 Sightseeing trip 【Floyd】+【最小环】+【路径记录】
- istView 和 Adapter 的基础
- ThinkPHP 中权限管理控制(RBAC)
- Openwrt中添加ndpi支持
- 黑马程序员:我对多线程的总结以及练习(重点)
- Mysql学习笔记<4>数据库的备份与恢复
- 基于Openfire消息服务器应用Smack发送即时消息源码示例
- java.lang.ThreadLocal类
- Bezier曲线的原理 及 二次Bezier曲线的实现
- 第一次当面试官的经历
- 机器学习——数据预处理
- 求二叉树的镜像
- Collection 和 Collections的区别