补充:

并发:两个或多个事件在同一时间段发生—-交替执行—效率低

并行:两个或多个事件在同时发生—————同时执行—效率高

多线程语法

多线程是java中的一个并发的概念:

  • 在计算机的运行中,所有的进程都是同时运行的(CPU会同时调用,之后会随机的获取分配)

  • 1.在java程序中也是支持的
  • 2.当java执行main方法的时候,其实就是执行一个名字叫做main的线程—主线程
  • 可以在main线程执行的时候,开启多个其他的线程A,B,C等等
  • 多个线程A,B,C等等都是同时执行的,同时等待CPU的调用,相互抢夺CPU调用自己的时间片段
  • 3.Thread类是java中的专门处理线程的api,是java.lang包下的常用类,每一个Thread类的
  • 对象,就代表一个某种状态的线程

  • java.lang包下常用的类(不需要导包):String,System,八大基本类型,Thread…

  • 进程:表示一个个的正在运行的程序
  • 线程:表示一个个正在运行的程序中的子进程(线程)
  • 包含关系:一个进程包含多个线程 ```java package com.xincheng;

/**

  • @author Lynn
  • @create 2020-12-14-14:46 */

import java.util.Date;

/**

  • 多线程是java中的一个并发的概念:
  • 在计算机的运行中,所有的进程都是同时运行的(CPU会同时调用,之后会随机的获取分配) *
  • 1.在java程序中也是支持的
  • 2.当java执行main方法的时候,其实就是执行一个名字叫做main的线程
  • 可以在main线程执行的时候,开启多个其他的线程A,B,C等等
  • 多个线程A,B,C等等都是同时执行的,同时等待CPU的调用,相互抢夺CPU调用自己的时间片段
  • 3.Thread类是java中的专门处理线程的api,是java.lang包下的常用类,每一个Thread类的
  • 对象,就代表一个某种状态的线程 *
  • java.lang包下常用的类(不需要导包):String,System,八大基本类型,Thread… *
  • 进程:表示一个个的正在运行的程序
  • 线程:表示一个个正在运行的程序中的子进程(线程)
  • 包含关系:一个进程包含多个线程 */ public class ThreadDemo { //main是一个主线程 public static void main(String[] args) {

    1. //是主线程调用的一个其他线程
    2. Date d=new Date();
    3. System.out.println(d);
    4. //是主线程调用的一个其他线程
    5. System.out.println(System.currentTimeMillis());
    6. say();

    }

    public static void say(){

    1. System.out.println("hello");

    } } ```

    案例1

    ```java package com.xincheng_01;

/**

  • @author Lynn
  • @create 2020-12-14-15:20 */

/**

  • 自定义线程类 *
  • 定义一个Thread的子类 *
  • 创建线程的第一种方式:继承Thread类 */ public class MyThread extends Thread {

    //重写run方法—相关的逻辑写进去 @Override public void run(){

    1. for (int i = 0; i <50 ; i++) {
    2. /**
    3. * this表示当前被调用的的这个线程
    4. * 获取现成的名字是java干的事情,我们不管
    5. */
    6. System.out.println(this.getName()+":"+i);
    7. }

    } } java package com.xincheng_01;

/**

  • @author Lynn
  • @create 2020-12-14-15:13 */

/**

  • java中使用Thread类来创建线程: 第一种方式: *
  • 步骤:
  • 1.指定执行现成的目标:定义一个Thread子类,重写run方法,将相关的逻辑实现
  • public void run():将需要的逻辑写在里面,该方法相当于线程中的main方法
  • 2.创建自定义线程的子类对象
  • 3.开启线程操作—触发
  • public void start():只要调用了这个方法,线程才会启动,开始执行 *
  • Thread中的相关方法:
  • public final String getName():获取线程的名字
  • public final void setName():设置线程的名字
  • public static Thread currentThread():获取当前的线程对象 *
  • 线程的运行默认是不可控制的,是CPU随机调用的,我们只能看到运行的结果 / public class ThreadDemo { //主线程 public static void main(String[] args) { //创建线程对象—用于开启线程 Thread t1=new MyThread(); Thread t2=new MyThread();

    //开启线程—直接运行 t1.start(); t2.start();

    System.out.println(“———————————————————“);

    //返回一个main线程—其实就是main方法的线程 Thread mainThread=Thread.currentThread(); System.out.println(mainThread);//Thread[main,5,main]

    for (int i = 0; i <50 ; i++) {

    1. System.out.println(mainThread.getName()+":"+i);

    } } } ```

    案例2

    ```java package com.xincheng_01;

/**

  • @author Lynn
  • @create 2020-12-14-15:36 */

/**

  • 创建线程的第二种方式:
  • 实现Runnable接口,lang包下的: *
  • 1.实现接口,并且要实现run方法
  • 2.在run方法中,添加逻辑代码
  • 3.通过start启动线程 */ public class MyRunable implements Runnable{ @Override public void run(){
    1. //返回当前线程
    2. Thread thisThread=Thread.currentThread();
    3. for (int i = 0; i <20 ; i++) {
    4. System.out.println(thisThread.getName()+":"+i);
    5. }
    } } java package com.xincheng_01;

/**

  • @author Lynn
  • @create 2020-12-14-15:13 */

/**

  • 1.使用匿名内部类创建线程的原理和普通类创建线程的原理完全一样
  • 2.区别在于利用匿名内部类创建线程的写法更加简洁,使用非常多 ```java package com.xincheng_02;

/**

  • @author Lynn
  • @create 2020-12-14-16:06 */

/**

  • 演示匿名内部类创建线程—实际开发中使用 *
  • 1.使用匿名内部类创建线程的原理和普通类创建线程的原理完全一样
  • 2.区别在于利用匿名内部类创建线程的写法更加简洁,使用非常多 */ public class AnonysThreadDemo { public static void main(String[] args) {

    1. //方式一:使用匿名内部类创建线程的子类对象
    2. Thread t1 = new Thread() {
    3. @Override
    4. public void run() {
    5. System.out.println("还记得去年陪你过圣诞节的人吗");
    6. }
    7. };
    8. t1.start();
    9. System.out.println("----------------------------------------------------------------");
    10. //使用匿名内部类,创建线程的匿名子类对象,并且启动线程
    11. new Thread() {
    12. @Override
    13. public void run() {
    14. System.out.println("还记得上一次陪你去看演唱会的人吗");
    15. }
    16. }.start();
    17. //方式二:使用匿名内部类的方式,创建执行目标类的对象
    18. Runnable r = new Runnable() {
    19. @Override
    20. public void run() {
    21. System.out.println("她来听我的演唱会");
    22. }
    23. };
    24. //传入到构造函数
    25. Thread t2 = new Thread(r);
    26. t2.start();
    27. System.out.println("-----------------------------");
    28. //组合写法-精简写法
    29. new Thread(new Runnable() {
    30. @Override
    31. public void run() {
    32. System.out.println("往事只能回味");
    33. }
    34. }).start();
    35. //组合写法--普通写法
    36. Thread t3=new Thread(new Runnable() {
    37. @Override
    38. public void run() {
    39. System.out.println("往事只能回味");
    40. }
    41. });
    42. t3.start();

    } } ```

    练习(售卖车票)

    ```java package com.xincheng_03;

/**

  • @author Lynn
  • @create 2020-12-14-16:29 */

import javax.crypto.interfaces.PBEKey;

/**

  • 这是一个任务
  • 使用第二种方式—使用Runnable接口 */ public class Ticket implements Runnable{ //定义车票数 private int number=100;

    //卖票的逻辑 @Override public void run() {

    1. //模拟车站不停地卖票--卖光为止
    2. while (true){
    3. //有票就卖
    4. if (number>0){
    5. System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");
    6. }else {
    7. //没有票了
    8. break;
    9. }
    10. }

    } } java package com.xincheng_03;

/**

  • @author Lynn
  • @create 2020-12-14-16:25 */

/**

  • 多线程模拟售卖火车票 *
  • 要求使用第二种方式实现,方便数据共享 *
  • 步骤:
  • 1.定义卖票的线程执行目标:(任务)
  • 在成员变量定义为票数100张,卖掉一张,改数字就减去1,一直到0为止
  • 重写run方法,完成案例
  • 2.创建买票的线程且执行该目标的对象
  • 3.使用该卖票子线程执行对象创建多个线程任务
  • 4.开启多个线程 */ public class TicketDemo { public static void main(String[] args) {

    1. //创建车票的线程执行目标对象
    2. Ticket ticket=new Ticket();
    3. /*//使用该卖票的线程执行目标的对象创建多个任务
    4. Thread t1 = new Thread(ticket,"jack.Ma");
    5. Thread t2 = new Thread(ticket,"Huateng.Ma");
    6. Thread t3 = new Thread(ticket,"Yanhong.Li");
    7. t1.start();
    8. t2.start();
    9. t3.start();*/
    10. new Thread(ticket,"jack.Ma").start();
    11. new Thread(ticket,"Huateng.Ma").start();
    12. new Thread(ticket,"Yanhong.Li").start();

    } } ```

    回顾

    image.png

    线程池与线程生命周期

    线程生命周期图示

    image.png
    image.png

    线程池

    image.png ```java package com.igeek_01;

/**

  • @author Lynn
  • @create 2020-12-14-16:29 */

/**

  • 这是一个任务
  • 使用第二种方式—使用Runnable接口 */ public class Ticket implements Runnable{ //定义车票数 private int number=100;

    //卖票的逻辑 @Override public void run() {

    1. //模拟车站不停地卖票--卖光为止
    2. while (true){
    3. //有票就卖
    4. if (number>0){
    5. System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");
    6. }else {
    7. //没有票了
    8. break;
    9. }
    10. }

    } } java package com.igeek_01;

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;

/**

  • @author Lynn
  • @create 2020-12-15-9:08 */ public class ThreadPoolDemo { public static void main(String[] args) {

    1. //实例化线程池
    2. /**
    3. * ExecutorService是一个接口,专门处理线程池的
    4. * Executors是一个工具类,提供了很多的方法服务于线程
    5. * newFixedThreadPool(3)该方法传入的参数表示在线程启动的时候初始化几个线程
    6. */
    7. ExecutorService threadPool= Executors.newFixedThreadPool(3);
    8. //创建线程的执行目标
    9. Ticket ticket=new Ticket();
    10. //向线程池中提交任务目标
    11. threadPool.submit(ticket);
    12. threadPool.submit(ticket);
    13. threadPool.submit(ticket);
    14. threadPool.submit(ticket);
    15. threadPool.submit(ticket);
    16. //在适当的时候也可以关闭线程池,一般不关闭
    17. //意味着线程池中的所有的线程都销毁

    // threadPool.shutdown(); } } ```

image.png

Callable接口

image.png

  1. package com.igeek_02;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-14-16:29
  5. */
  6. /**
  7. * 这是一个任务
  8. * 使用第二种方式--使用Runnable接口
  9. */
  10. public class Ticket implements Runnable{
  11. //定义车票数
  12. private int number=100;
  13. //卖票的逻辑
  14. @Override
  15. public void run() {
  16. //模拟车站不停地卖票--卖光为止
  17. while (true){
  18. //有票就卖
  19. if (number>0){
  20. System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");
  21. }else {
  22. //没有票了
  23. break;
  24. }
  25. }
  26. }
  27. }
  1. package com.igeek_02;
  2. import java.util.concurrent.Callable;
  3. /**
  4. * @author Lynn
  5. * @create 2020-12-15-10:03
  6. */
  7. public class MyCallable implements Callable<String> {
  8. /**
  9. * 定义一个线程执行的目标逻辑
  10. * @return
  11. * @throws Exception
  12. */
  13. @Override
  14. public String call() throws Exception {
  15. return "我是线程任务的返回值结果";
  16. }
  17. }
  1. package com.igeek_02;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. import java.util.concurrent.Future;
  6. /**
  7. * @author Lynn
  8. * @create 2020-12-15-9:08
  9. */
  10. public class ThreadPoolDemo {
  11. public static void main(String[] args) throws ExecutionException, InterruptedException {
  12. //实例化线程池
  13. /**
  14. * ExecutorService是一个接口,专门处理线程池的
  15. * Executors是一个工具类,提供了很多的方法服务于线程
  16. * newFixedThreadPool(3)该方法传入的参数表示在线程启动的时候初始化几个线程
  17. */
  18. ExecutorService threadPool= Executors.newFixedThreadPool(3);
  19. //创建线程的执行目标
  20. Ticket ticket=new Ticket();
  21. //利用Callable接口返回Future,(没有返回值的run方法,返回的是null,没有意义)
  22. Future<?> ticketFuture = threadPool.submit(ticket);
  23. System.out.println(ticketFuture.get());
  24. //创建带有返回值的线程执行目标
  25. MyCallable callable=new MyCallable();
  26. //向线程中提交任务,并返回线程目标的执行结果
  27. Future<String> future1= threadPool.submit(callable);
  28. Future<String> future2= threadPool.submit(callable);
  29. Future<String> future3= threadPool.submit(callable);
  30. //从执行结果中返回call的具体值
  31. String result1=future1.get();
  32. String result2=future2.get();
  33. String result3=future3.get();
  34. System.out.println(result1);
  35. System.out.println(result2);
  36. System.out.println(result3);
  37. //在适当的时候也可以关闭线程池,一般不关闭
  38. //意味着线程池中的所有的线程都销毁
  39. // threadPool.shutdown();
  40. }
  41. }

线程安全问题

image.png

线程安全问题的解决方案—-加锁

image.png

  1. package com.igeek_04;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:32
  5. */
  6. /**
  7. * 使用第二种方式创建线程,原因是执行线程目标对象共享
  8. *
  9. * 定义一个卖票的线程执行目标对象--加锁
  10. */
  11. public class Ticket implements Runnable{
  12. //票数
  13. private int number=100;
  14. //声明成员变量为定义锁对象
  15. private Object lock=new Object();
  16. @Override
  17. public void run() {
  18. while (true){
  19. synchronized (lock){
  20. //线程暂停
  21. try {
  22. Thread.sleep(500);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. //有票就卖
  27. if (number>0){
  28. String threadName=Thread.currentThread().getName();
  29. System.out.println(threadName+"正在销售第"+number+"张票");
  30. number--;
  31. }else {
  32. //没票了
  33. break;
  34. }
  35. }
  36. }
  37. }
  38. }
  1. package com.igeek_04;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:44
  5. */
  6. /**
  7. * 线程安全问题的解决方案 :
  8. * 同步代码块
  9. * synchronized(锁对象){
  10. * 可能出现线程安全问题的代码
  11. * }
  12. */
  13. public class ThreadDemo {
  14. public static void main(String[] args) {
  15. //实例化目标
  16. Ticket ticket=new Ticket();
  17. //使用该卖票的目标创建多个线程
  18. Thread thread1=new Thread(ticket,"Jack");
  19. Thread thread2=new Thread(ticket,"Rose");
  20. Thread thread3=new Thread(ticket,"Tom");
  21. //启动多个线程
  22. thread1.start();
  23. thread2.start();
  24. thread3.start();
  25. }
  26. }

优化

  1. package com.igeek_05;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:32
  5. */
  6. /**
  7. * 使用第二种方式创建线程,原因是执行线程目标对象共享
  8. *
  9. * 定义一个卖票的线程执行目标对象--加锁
  10. */
  11. public class Ticket implements Runnable{
  12. //票数
  13. private int number=100;
  14. //声明成员变量为定义锁对象
  15. private Object lock=new Object();
  16. //创建一个标记,让不同的人来执行不同的代码块
  17. private int x=0;
  18. @Override
  19. public void run() {
  20. while (true){
  21. //进行取余的运算,模拟卖票的人
  22. if(x%2==0){
  23. //代码块
  24. //将完整的一套动作使用synchronized包裹
  25. synchronized (lock){
  26. //线程暂停
  27. try {
  28. Thread.sleep(100);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. //有票就卖
  33. if (number>0){
  34. String threadName=Thread.currentThread().getName();
  35. System.out.println(threadName+"正在销售第"+number+"张票");
  36. number--;
  37. }else {
  38. //没票了
  39. break;
  40. }
  41. }
  42. }else {
  43. //代码块
  44. //将完整的一套动作使用synchronized包裹
  45. synchronized (lock){
  46. //线程暂停
  47. try {
  48. Thread.sleep(500);
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }
  52. //有票就卖
  53. if (number>0){
  54. String threadName=Thread.currentThread().getName();
  55. System.out.println(threadName+"正在销售第"+number+"张票");
  56. number--;
  57. }else {
  58. //没票了
  59. break;
  60. }
  61. }
  62. }
  63. x++;
  64. }
  65. }
  66. }
  1. package com.igeek_05;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:44
  5. */
  6. /**
  7. * 线程安全问题的解决方案 :
  8. * 同步代码块
  9. * synchronized(锁对象){
  10. * 可能出现线程安全问题的代码
  11. * }
  12. */
  13. public class ThreadDemo {
  14. public static void main(String[] args) {
  15. //实例化目标
  16. Ticket ticket=new Ticket();
  17. //使用该卖票的目标创建多个线程
  18. Thread thread1=new Thread(ticket,"Jack");
  19. Thread thread2=new Thread(ticket,"Rose");
  20. Thread thread3=new Thread(ticket,"Tom");
  21. //启动多个线程
  22. thread1.start();
  23. thread2.start();
  24. thread3.start();
  25. }
  26. }

同步方法(用synchronized修饰)有静态和非静态的

image.png
非静态方法

  1. package com.igeek_06;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:32
  5. */
  6. /**
  7. * 使用第二种方式创建线程,原因是执行线程目标对象共享
  8. *
  9. * 定义一个卖票的线程执行目标对象--加锁
  10. *
  11. * 演示synchronized可以用来修饰方法,叫做同步方法
  12. *
  13. * 当前案例中,使用对象锁对当前的这个Ticket加锁,目的是this是当前正在被调用的这个Ticket对象,
  14. * 不管是synchronized代码块中还是封装的一个synchronized方法,都可以保证使用的是同一个锁对象
  15. */
  16. public class Ticket implements Runnable{
  17. //票数
  18. private int number=100;
  19. //声明成员变量为定义锁对象
  20. private Object lock=new Object();
  21. //创建一个标记,让不同的人来执行不同的代码块
  22. private int x=0;
  23. @Override
  24. public void run() {
  25. while (true){
  26. //进行取余的运算,模拟卖票的人
  27. if(x%2==0){
  28. //代码块
  29. //将完整的一套动作使用synchronized包裹
  30. //把当前这个被调用的对象锁住
  31. synchronized (this){
  32. //线程暂停
  33. try {
  34. Thread.sleep(100);
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. //有票就卖
  39. if (number>0){
  40. String threadName=Thread.currentThread().getName();
  41. System.out.println(threadName+"正在销售第"+number+"张票");
  42. number--;
  43. }else {
  44. this.sell();
  45. }
  46. }
  47. }
  48. //当票数=0,就表示没票了
  49. if(number<0){
  50. break;
  51. }
  52. x++;
  53. }
  54. }
  55. public synchronized void sell(){
  56. try {
  57. Thread.sleep(20);
  58. } catch (InterruptedException e) {
  59. e.printStackTrace();
  60. }
  61. if (number>0){
  62. String threadName=Thread.currentThread().getName();
  63. System.out.println(threadName+"正在销售第"+number+"张票");
  64. number--;
  65. }
  66. }
  67. }
  1. package com.igeek_06;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:44
  5. */
  6. import com.igeek_05.Ticket;
  7. /**
  8. * 线程安全问题的解决方案 :
  9. * 同步代码块
  10. * synchronized(锁对象){
  11. * 可能出现线程安全问题的代码
  12. * }
  13. */
  14. public class ThreadDemo {
  15. public static void main(String[] args) {
  16. //实例化目标
  17. com.igeek_05.Ticket ticket=new Ticket();
  18. //使用该卖票的目标创建多个线程
  19. Thread thread1=new Thread(ticket,"Jack");
  20. Thread thread2=new Thread(ticket,"Rose");
  21. Thread thread3=new Thread(ticket,"Tom");
  22. //启动多个线程
  23. thread1.start();
  24. thread2.start();
  25. thread3.start();
  26. }
  27. }

静态方法

  1. package com.igeek_07;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:32
  5. */
  6. /**
  7. * 使用第二种方式创建线程,原因是执行线程目标对象共享
  8. *
  9. * 定义一个卖票的线程执行目标对象--加锁
  10. *
  11. * 演示synchronized可以用来修饰方法,叫做同步方法
  12. * 同时也支持修饰静态方法!!!
  13. *
  14. * 当前案例中,使用对象锁对当前的这个Ticket加锁,目的是this是当前正在被调用的这个Ticket对象,
  15. * 不管是synchronized代码块中还是封装的一个synchronized方法,都可以保证使用的是同一个锁对象
  16. */
  17. public class Ticket implements Runnable{
  18. //票数--静态方法不能调用非静态资源
  19. private static int number=100;
  20. //声明成员变量为定义锁对象
  21. private Object lock=new Object();
  22. //创建一个标记,让不同的人来执行不同的代码块
  23. private int x=0;
  24. @Override
  25. public void run() {
  26. while (true){
  27. //进行取余的运算,模拟卖票的人
  28. if(x%2==0){
  29. //代码块
  30. //将完整的一套动作使用synchronized包裹
  31. //类名直接调用静态方法
  32. synchronized (Ticket.class){
  33. //线程暂停
  34. try {
  35. Thread.sleep(50);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. //有票就卖
  40. if (number>0){
  41. String threadName=Thread.currentThread().getName();
  42. System.out.println(threadName+"正在销售第"+number+"张票");
  43. number--;
  44. }/*else {
  45. //没票了
  46. break;
  47. }*/
  48. }
  49. }else {
  50. // this.sell();
  51. Ticket.sell();
  52. }
  53. //当票数=0,就表示没票了
  54. if(number<0){
  55. break;
  56. }
  57. x++;
  58. }
  59. }
  60. public static synchronized void sell(){
  61. try {
  62. Thread.sleep(20);
  63. } catch (InterruptedException e) {
  64. e.printStackTrace();
  65. }
  66. if (number>0){
  67. String threadName=Thread.currentThread().getName();
  68. System.out.println(threadName+"正在销售第"+number+"张票");
  69. number--;
  70. }
  71. }
  72. }
  1. package com.igeek_07;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-10:44
  5. */
  6. import com.igeek_05.Ticket;
  7. /**
  8. * 线程安全问题的解决方案 :
  9. * 同步代码块
  10. * synchronized(锁对象){
  11. * 可能出现线程安全问题的代码
  12. * }
  13. */
  14. public class ThreadDemo {
  15. public static void main(String[] args) {
  16. //实例化目标
  17. Ticket ticket=new Ticket();
  18. //使用该卖票的目标创建多个线程
  19. Thread thread1=new Thread(ticket,"Jack");
  20. Thread thread2=new Thread(ticket,"Rose");
  21. Thread thread3=new Thread(ticket,"Tom");
  22. //启动多个线程
  23. thread1.start();
  24. thread2.start();
  25. thread3.start();
  26. }
  27. }

回顾

image.png

演示等待和唤醒机制

image.png
image.png

案例

  1. package com.igeek_08;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-15:18
  5. */
  6. /**
  7. * 资源类(实体类)
  8. *
  9. * 定义共享的数据,其中成员:name,sex
  10. */
  11. public class Person {
  12. //不使用private是为了便于后面的操作
  13. String name;
  14. String sex;
  15. //状态标记
  16. /**
  17. * flag:
  18. * true:有数据,生产者等待被消费
  19. * false:没有数据,生产者生产,消费者等待
  20. */
  21. boolean flag;
  22. public Person(){
  23. super();
  24. }
  25. }
  1. package com.igeek_08;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-15:20
  5. */
  6. /**
  7. * 生产者
  8. *
  9. * 生产者的执行目标:
  10. * 反复为Person对象赋值:Jack 男 与 Rose 女
  11. */
  12. public class PutIn implements Runnable{
  13. //实例化对象--声明
  14. private Person person;
  15. //创建构造方法用于接收外部传入的共享数据Person
  16. public PutIn(Person person){
  17. this.person=person;
  18. }
  19. @Override
  20. public void run() {
  21. //使用循环达到反复赋值的目的
  22. //如果是奇数,就生成Jack 男,如果是偶数,救生成Rose 女
  23. int i=0;
  24. while (true){
  25. synchronized (person){
  26. //判断状态flag是true还是false
  27. while (person.flag){
  28. //有数据,就等待
  29. try {
  30. person.wait();//线程阻塞
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. //如果没有数据或者是刚被唤醒
  36. //判断生产的内容
  37. if (i%2==1){//男
  38. person.name="Jack";
  39. try {
  40. Thread.sleep(100);
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44. person.sex="男";
  45. }else {//女
  46. person.name="Rose";
  47. try {
  48. Thread.sleep(100);
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }
  52. person.sex="女";
  53. }
  54. System.out.println(person);
  55. System.out.println("生产了"+person.name+"-"+person.sex);
  56. //切换状态为true
  57. person.flag=true;
  58. //如果有消费者等待,就唤醒消费者,如果没有消费者等待,就不需要唤醒,相当于没有执行
  59. person.notify();
  60. }
  61. i++;
  62. }
  63. }
  64. }
  1. package com.igeek_08;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-15:29
  5. */
  6. /**
  7. * 消费者目标对象
  8. *
  9. * 反复的获取Person对象,并且打印
  10. */
  11. public class GetOut implements Runnable{
  12. //实例化共享资源
  13. private Person person;
  14. public GetOut(Person person) {
  15. this.person=person;
  16. }
  17. @Override
  18. public void run() {
  19. //使用循环获取
  20. while (true) {
  21. //利用共享对象作为锁
  22. synchronized (person){
  23. //获取判断状态--flag
  24. while (!person.flag){
  25. //没有数据就等待生产
  26. try {
  27. person.wait();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. //有数据或者是刚刚被唤醒
  33. try {
  34. Thread.sleep(100);
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. //进行获取操作
  39. String name= person.name;
  40. String sex= person.sex;
  41. System.out.println(person);
  42. System.out.println("消费了:"+name+"-"+sex);
  43. //切换状态为false
  44. person.flag=false;
  45. //唤醒生产者,如果有就唤醒,如果没有就不需要唤醒,相当于没有执行
  46. person.notify();
  47. }
  48. }
  49. }
  50. }
  1. package com.igeek_08;
  2. /**
  3. * @author Lynn
  4. * @create 2020-12-15-15:15
  5. */
  6. /**
  7. * 演示等待和唤醒机制
  8. */
  9. public class WaitNotifyDemo {
  10. public static void main(String[] args) {
  11. //实例化对象
  12. Person person=new Person();
  13. //创建生产者的执行目标
  14. PutIn putIn=new PutIn(person);
  15. //创建消费者的执行目标
  16. GetOut getOut=new GetOut(person);
  17. //开启生产者线程
  18. Thread putInThread=new Thread(putIn);
  19. putInThread.start();
  20. //开启消费者线程
  21. Thread getOutThread=new Thread(getOut);
  22. getOutThread.start();
  23. }
  24. }