一、死锁

开发中不用死锁,面试会问:

应用场景:并发场景,多线程,线程之间互不相让,得借助于锁

加锁得目的是为了线程安全,物极必反,尤其是加了锁以后

死锁是一个状态,当两个线程互相持有对方想要得资源得时候,却又不主动释放这个资源得时候,会导致死锁,到家都用不了线程,就无法执行下去。

线程1有一个锁1

线程2有个锁2

线程1等待锁2得释放

线程2等待锁1得释放

死锁案例:背会,面试需手写

  1. package com.qfedu.test;
  2. //死锁
  3. class DeadLock implements Runnable{
  4. private boolean isFlag;
  5. private Object obj1;
  6. private Object obj2;
  7. public DeadLock(boolean isFlag, Object obj1, Object obj2) {
  8. this.isFlag = isFlag;
  9. this.obj1 = obj1;
  10. this.obj2 = obj2;
  11. }
  12. @Override
  13. public void run() {
  14. if(isFlag) {//如果是true让线程1进来
  15. synchronized (obj1) {//锁得是obj1对象
  16. //线程1
  17. System.out.println(Thread.currentThread().getName() + "锁1");
  18. try {
  19. Thread.sleep(1000);
  20. } catch (InterruptedException e) {
  21. // TODO Auto-generated catch block
  22. e.printStackTrace();
  23. }
  24. System.out.println("等待锁2得释放");
  25. synchronized (obj2) {
  26. System.out.println(Thread.currentThread().getName() + "拿到锁2");
  27. }
  28. }
  29. }
  30. if (!isFlag) {//如果为false,让线程2进来
  31. synchronized(obj2) {
  32. System.out.println(Thread.currentThread().getName() + "锁2");
  33. try {
  34. Thread.sleep(1000);
  35. } catch (InterruptedException e) {
  36. // TODO Auto-generated catch block
  37. e.printStackTrace();
  38. }
  39. System.out.println("等待锁1得释放");
  40. synchronized (obj1) {
  41. System.out.println(Thread.currentThread().getName() + "拿到锁1");
  42. }
  43. }
  44. }
  45. }
  46. }
  47. public class Demo1 {
  48. public static void main(String[] args) {
  49. Object object1 = new Object();
  50. Object object2 = new Object();
  51. //第一个线程执行锁1
  52. new Thread(new DeadLock(true, object1, object2),"线程1").start();
  53. //第二个线程执行锁2
  54. new Thread(new DeadLock(false, object1, object2),"线程2").start();
  55. }
  56. }

二、线程的生命周期

1.线程的创建,开启线程

2.可运行状态,CPU等待,CPU抢占

3.运行状态,CPU使用,CPU等待,CPU抢占

4.阻塞状态,锁,sleep,wait

5.消亡

三、和线程相关的重要方法

object类下面的方法

  1. public final void wait() throws InterruptedException

导致线程等待,知道另一个线程调用该对象的notify()或notifyAll()方法。

总结:对象来调用wait方法,在一个线程中 对象.wait() 这个线程会阻塞。

想让这个线程继续执行,需要使用另外一个线程去拿同一个对象调用notify方法去唤醒线程

注意事项:

  1. 1.功能是线程等待(阻塞状态),
  2. 2.需要使用对象调用
  3. 3.wait方法一般搭配锁一起使用(保证是同一个对象锁)
  1. package com.qfedu.test2wait;
  2. //wait
  3. //对象调用wait方法,得有对象,有对象得先有类
  4. class Message{
  5. private String message;
  6. public Message() {
  7. }
  8. public Message(String message) {
  9. this.message = message;
  10. }
  11. public String getMessage() {
  12. return message;
  13. }
  14. public void setMessage(String message) {
  15. this.message = message;
  16. }
  17. @Override
  18. public String toString() {
  19. return "Message [message=" + message + "]";
  20. }
  21. }
  22. //线程 作为等待线程
  23. class Waiter implements Runnable{
  24. //Waiter 是一个类 可以写构造方法
  25. private Message msg;//一个类下面得属性可以是另外一个类对象
  26. //因为要使用对象.wait方法
  27. public Waiter(Message msg) {
  28. this.msg = msg;
  29. }
  30. @Override
  31. public void run() {
  32. String name = Thread.currentThread().getName();
  33. synchronized (msg) {
  34. System.out.println(name + "等待唤醒时间:" + System.currentTimeMillis());
  35. try {
  36. msg.wait();//走到这一步 线程不会往下走了
  37. } catch (InterruptedException e) {
  38. // TODO Auto-generated catch block
  39. e.printStackTrace();
  40. }
  41. System.out.println("线程wait了");
  42. System.out.println(name + "被唤醒时间:" + System.currentTimeMillis());
  43. System.out.println(name + "#线程#" + msg.getMessage());
  44. }
  45. }
  46. }
  47. //唤醒线程
  48. class Notifier implements Runnable{
  49. private Message msg;
  50. public Notifier(Message msg) {
  51. super();
  52. this.msg = msg;
  53. }
  54. @Override
  55. public void run() {
  56. String name = Thread.currentThread().getName();
  57. System.out.println(name + "开始唤醒等待线程");
  58. try {
  59. Thread.sleep(2000);
  60. } catch (InterruptedException e) {
  61. // TODO Auto-generated catch block
  62. e.printStackTrace();
  63. }
  64. synchronized (msg) {
  65. msg.setMessage("!!!!我是修改之后的Message属性值");
  66. //开始唤醒 msg是等待线程和唤醒线程中的同一个对象
  67. msg.notify();
  68. msg.notify();
  69. msg.notify();
  70. // msg.notifyAll();
  71. System.out.println("hahaha?");
  72. }
  73. }
  74. }
  75. public class Demo1 {
  76. public static void main(String[] args) {
  77. Message message = new Message("我是Message属性");
  78. Waiter waiter = new Waiter(message);
  79. new Thread(waiter,"等待线程1").start();
  80. new Thread(waiter,"等待线程2").start();
  81. new Thread(waiter,"等待线程3").start();
  82. Notifier notifier = new Notifier(message);
  83. new Thread(notifier,"唤醒线程").start();
  84. }
  85. }

四、生产者消费者模式【重要】

和上午wait和notify方法有关

  1. package com.qfedu.test3Anili;
  2. //生产者消费者模式 案例 购车
  3. //这个类作为线程之间共享的一个资源,会当成一个锁对象
  4. class Goods {
  5. private String name;// 商品名字
  6. private double price;// 商品价格
  7. private boolean isProduct;// 商品是否需要生产
  8. public Goods() {
  9. }
  10. public Goods(String name, double price, boolean isProduct) {
  11. this.name = name;
  12. this.price = price;
  13. this.isProduct = isProduct;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. public double getPrice() {
  22. return price;
  23. }
  24. public void setPrice(double price) {
  25. this.price = price;
  26. }
  27. public boolean isProduct() {
  28. return isProduct;
  29. }
  30. public void setProduct(boolean isProduct) {
  31. this.isProduct = isProduct;
  32. }
  33. @Override
  34. public String toString() {
  35. return "Goods [name=" + name + ", price=" + price + ", isProduct=" + isProduct + "]";
  36. }
  37. }
  38. //生产者
  39. class Producer implements Runnable {
  40. private Goods goods;
  41. public Producer(Goods goods) {
  42. this.goods = goods;
  43. }
  44. @Override
  45. public void run() {
  46. int count = 0;
  47. while (true) {
  48. synchronized (goods) {
  49. if (goods.isProduct()) {// true 需要生成
  50. if (count % 2 == 0) {// 偶数生成汉
  51. goods.setName("比亚迪汉");
  52. goods.setPrice(30);
  53. } else {// 奇数生产唐
  54. goods.setName("比亚迪唐");
  55. goods.setPrice(25);
  56. }
  57. // 生产完以后就不需要生产了 改为false
  58. goods.setProduct(false);
  59. System.out.println("生产者生产了:" + goods.getName() + ",价格为:" + goods.getPrice());
  60. count++;
  61. // 生产好了 唤醒消费者
  62. goods.notify();
  63. } else {
  64. System.out.println("3");
  65. // 不需要生产 生产者进入等待状态(阻塞)
  66. try {
  67. goods.wait();
  68. } catch (InterruptedException e) {
  69. // TODO Auto-generated catch block
  70. e.printStackTrace();
  71. }
  72. System.out.println("4");
  73. }
  74. }
  75. }
  76. }
  77. }
  78. //消费者
  79. class Consumer implements Runnable {
  80. private Goods goods;
  81. public Consumer(Goods goods) {
  82. this.goods = goods;
  83. }
  84. @Override
  85. public void run() {
  86. while (true) {
  87. // System.out.println("睡一会儿再买");
  88. try {
  89. Thread.sleep(1000);
  90. } catch (InterruptedException e1) {
  91. // TODO Auto-generated catch block
  92. e1.printStackTrace();
  93. }
  94. synchronized (goods) {
  95. // 如果是true 需要生成 就是没商品
  96. if (!goods.isProduct()) {// false
  97. // 不需要生成
  98. System.out.println("消费者购买了:" + goods.getName() + ":价格为" + goods.getPrice());
  99. // 购买完商品就没了
  100. goods.setProduct(true);
  101. // 唤醒生产者去生产
  102. goods.notify();
  103. } else {
  104. System.out.println("1");
  105. // 需要生成 消费者进入阻塞状态
  106. try {
  107. goods.wait();
  108. continue;
  109. } catch (InterruptedException e) {
  110. // TODO Auto-generated catch block
  111. e.printStackTrace();
  112. }
  113. System.out.println("2");
  114. }
  115. }
  116. }
  117. }
  118. }
  119. public class Demo1 {
  120. public static void main(String[] args) {
  121. Goods goods = new Goods("比亚迪汉!!!", 178, false);
  122. Consumer consumer = new Consumer(goods);
  123. new Thread(consumer, "消费者线程").start();
  124. Producer producer = new Producer(goods);
  125. new Thread(producer, "生产者线程").start();
  126. }
  127. }

匿名内部类写法【简写】

  1. package com.qfedu.test4;
  2. //创建线程的第三种方式
  3. public class Demo1 {
  4. public static void main(String[] args) {
  5. Thread thread = new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. for (int i = 0; i < 10; i++) {
  9. System.out.println(i);
  10. }
  11. }
  12. });
  13. thread.start();
  14. new Thread(new Runnable() {
  15. @Override
  16. public void run() {
  17. for (int i = 0; i < 10; i++) {
  18. System.out.println(i);
  19. }
  20. }
  21. }).start();
  22. }
  23. }

五、创建线程的第三种方式

1.创建Callable接口的实现类,并实现call方法,该call方法有返回值,在去创建Callable的实现类对象
2.使用一个类 FutureTask这个类去包装Callable 对象,FutureTask对象变相的封装了call方法
3.使用Futruetask对象作为Thread对象的参数并启动新线程
4.可以使用FutureTask.get()这个的对象获取子线程执行结束返回值

  1. package com.qfedu.test4;
  2. import java.util.concurrent.Callable;
  3. import java.util.concurrent.ExecutionException;
  4. import java.util.concurrent.FutureTask;
  5. class MyThread implements Callable<String>{
  6. @Override
  7. public String call() throws Exception {
  8. // TODO Auto-generated method stub
  9. for (int i = 0; i < 5; i++) {
  10. System.out.println(Thread.currentThread().getName() + "啥啊");
  11. }
  12. return "aaaa";
  13. }
  14. }
  15. public class Demo2 {
  16. private static final int String = 0;
  17. public static void main(String[] args) throws InterruptedException, ExecutionException {
  18. MyThread myThread1 = new MyThread();
  19. MyThread myThread2 = new MyThread();
  20. FutureTask<String> task1 = new FutureTask<String>(myThread1);
  21. FutureTask<String> task2 = new FutureTask<String>(myThread2);
  22. new Thread(task1,"线程1").start();
  23. new Thread(task2,"线程2").start();
  24. System.out.println(task1.get());
  25. }
  26. }