线程
-
主线程(单线程)
-
Person类
package cn.xiaoge.day17.demo01; public class Person { private String name; public void run() { // 定义循环, 执行5次 for (int i = 0; i < 5; i++) { System.out.println(name + "--->" + i); } } public Person() { } public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
执行路口
package cn.xiaoge.day17.demo01; /* 主线程: 执行主(main)方法的线程 单线程程序: java程序中只有一个线程 执行从main方法开始, 从上到下依次执行 JVM执行main方法, main方法会进入到栈内存 JVM会找操作系统开辟一条main方法通过cpu的执行路径 而这个路径有一个名字, 叫main(主)线程 */ public class Demo01MainThread { public static void main(String[] args) { Person p1 = new Person("小强"); p1.run(); Person p2 = new Person("旺财"); p2.run(); } } // 运行结果 小强--->0 小强--->1 小强--->2 小强--->3 小强--->4 旺财--->0 旺财--->1 旺财--->2 旺财--->3 旺财--->4
-
-
创建多线程的第一种方式
-
MyThread类
package cn.xiaoge.day17.demo02; // 1. 创建一个Thread类的子类 public class MyThread extends Thread { // 2. 在Thread类的子类中重写Thread类中的run方法, 设置线程任务(开启线程要做什么?) @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("run" + i); } } }
-
执行路口
package cn.xiaoge.day17.demo02; /* 创建多线程程序的第一种方式: 创建Thread类的子类 java.lang.Thread类: 是描述线程的类, 我们想实现多线程程序, 就必须继承Thread类 实现步骤: 1. 创建一个Thread类的子类 2. 在Thread类的子类中重写Thread类中的run方法, 设置线程任务(开启线程要做什么?) 3. 创建Thread类的子类对象 4. 调用Thread类中的方法start方法, 开启新的线程, 执行run方法 void start() 使该线程开始执行: Java虚拟机调用该线程的run方法. 结果是两个线程并发地运行; 当线程(main线程)和另一个线程(创建的新线程, 执行其run方法) 多次启动一个线程是非法的. 特别是当线程已经结束执行后, 不能再重新启动. java程序属于抢占式调度, 那个线程的优先级高, 那个线程优先执行, 用一个优先级, 随机选择一个执行 */ public class Demo01Thread { public static void main(String[] args) { // 3. 创建Thread类的子类对象 MyThread myThread = new MyThread(); // 4. 调用Thread类中的方法start方法, 开启新的线程, 执行run方法 myThread.start(); for (int i = 0; i < 5; i++) { System.out.println("main" + i); } } } // 运行结果 main0 main1 run0 run1 run2 run3 run4 main2 main3 main4
-
-
获取线程名称
-
MyThread类
package cn.xiaoge.day17.demo03; /* 获取线程的名称: 1. 使用Thread类中的方法getName() String getName() 返回该线程的名称. 2. 可以先获取到当前正在执行的线程, 使用线程中的方法getName()获取线程的名称 static Thread currentThread() 返回对当前正在执行的线程对象的引用 */ // 定义一个Thread类的子类 public class MyThread extends Thread { // 重写Tread类中的run方法, 设置线程任务 @Override public void run() { // 获取线程名称--方法getName String name = this.getName(); System.out.println(name); } }
-
执行路口
package cn.xiaoge.day17.demo03; /* 线程名称: 主线程: main 新线程: Thread-0 */ public class Demo01GetThreadName { public static void main(String[] args) { // 创建Thread的子类对象 MyThread mt = new MyThread(); // 调用start方法, 开启新线程, 执行run方法 mt.start(); // Thread-0 MyThread mt2 = new MyThread(); mt2.start(); // Thread-1 // 返回对当前正在执行的线程对象的引用--方法currentThread Thread name = MyThread.currentThread(); System.out.println(name.getName()); // main } } // 运行结果 Thread-0 Thread-1 main
-
-
设置线程名称
-
类
package cn.xiaoge.day17.demo04; /* 设置线程的名称: (了解) 1. 使用Thread类中的方法setName(名字) void setName(String name) 改变线程名称. 使之与参数 name 相同 2. 创建一个带参数的构造方法, 参数传递线程的名称, 调用父类的带参构造方法, 把线程名称传递给父类, 让父类(Thread)给子线程起一个名字 Thread(String name) 分配新的 Thread 对象. */ public class MyThread extends Thread { public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { // 获取线程的名称 System.out.println(Thread.currentThread().getName()); } }
-
执行路口
package cn.xiaoge.day17.demo04; public class Demo01SetThreadName { public static void main(String[] args) { // 开启多线程 MyThread mt = new MyThread(); mt.setName("小强"); // 设置线程名称 mt.start(); // 使用构造方法设置线程名称 MyThread mt2 = new MyThread("旺财"); mt2.start(); } } // 运行结果 旺财 小强
-
-
线程睡眠
-
执行路口
package cn.xiaoge.day17.demo05; /* public static void sleep(long millis): 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行). 毫秒数结束之后, 线程继续执行 */ public class Demo01Sleep { public static void main(String[] args) { // 模拟秒表 for (int i = 1; i <= 60; i++) { System.out.println(i); try{ // 使用Thread类的sleep方法让程序睡眠1秒钟 Thread.sleep(1000); }catch (Exception e) { e.printStackTrace(); } } } } // 运行结果 1-60 打印时间间隔1秒
-
-
创建多线程的第二种方式
-
RunnableImpl类
package cn.xiaoge.day17.demo06; // 1. 创建一个Runnable接口的实现类 public class RunnableImpl implements Runnable { // 2. 在实现类中重写Runnable接口的run方法, 设置线程任务 @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + i); } } }
-
RunnableImpl2类
package cn.xiaoge.day17.demo06; // 1. 创建一个Runnable接口的实现类 public class RunnableImpl2 implements Runnable { // 2. 在实现类中重写Runnable接口的run方法, 设置线程任务 @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("Hello World" + i); } } }
-
执行路口
package cn.xiaoge.day17.demo06; /* 创建多线程程序的第二种方式: 实现Runnable接口 java.lang.Runnable Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现, 类必须定义一个称为 run 的无参数方法. java.lang.Thread类的构造方法 Thread(Runnable target) 分配新的 Thread 对象. Thread(Runnable target, String name) 分配新的 Thread 对象. 实现步骤: 1. 创建一个Runnable接口的实现类 2. 在实现类中重写Runnable接口的run方法, 设置线程任务 3. 创建一个Runnable接口的实现对象 4. 创建Thread类对象, 构造方法中传递Runnable接口的实现类对象 5. 调用Thread类中的start方法, 让他开启新的线程执行run方法. 实现Runnable接口创建多线程的好处: 1. 避免了单继承的局限性 一个类只能继承一个类(一个人只能有一个爹), 类继承了Thread类就不能继承其他的类 实现了Runnable接口, 还可以继承其他的类, 实现其他的接口 2. 增强了程序的扩展性, 降低了程序的耦合性(解耦) 实现Runnable接口的方式, 把设置线程任务和开启新线程进行了分离(解耦) 实现类中, 重写了run方法: 用来设置线程任务 创建Thread类对象, 调用start方法: 用来开启新的线程 */ public class Demo01Runnable { public static void main(String[] args) { // 3. 创建一个Runnable接口的实现对象 RunnableImpl runnableImpl = new RunnableImpl(); // 4. 创建Thread类对象, 构造方法中传递Runnable接口的实现类对象 // Thread thread = new Thread(runnableImpl); // 打印线程名称 Thread thread = new Thread(new RunnableImpl2()); // 打印HelloWorld // 5. 调用Thread类中的start方法, 让他开启新的线程执行run方法. thread.start(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + i); } } } // 运行结果 main0 Hello World0 Hello World1 Hello World2 Hello World3 Hello World4 main1 main2 main3 main4
-
-
内部类线程
-
执行路口
package cn.xiaoge.day17.demo07; /* 匿名内部类方式实现线程的创建 匿名: 没有名字 内部类: 写在其他类内部的类 匿名内部类作用: 简化代码 把子类继承父类, 重写父类的方法, 创建子类对象合成一步完成 把实现类实现接口, 重写接口中的方法, 创建实现类对象合成一步完成 匿名内部类的最终产物: 子类/实现类对象, 而这个类没有名字 格式: new 父类/接口(){ 重复父类/接口中的方法 }; */ public class Demo01InnerClassThread { public static void main(String[] args) { // 线程的父类Thread // new MyThread().start(); new Thread() { // 重写run方法, 设置线程任务 @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "--->" + "潇哥"); } } }.start(); // 线程的接口Runnable // Runnable r = new RunnableImpl(); // 多态 new Thread(new Runnable(){ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "--->" + "程序员"); } } }).start(); } } // 运行结果 Thread-0--->潇哥 Thread-0--->潇哥 Thread-0--->潇哥 Thread-0--->潇哥 Thread-0--->潇哥 Thread-1--->程序员 Thread-1--->程序员 Thread-1--->程序员 Thread-1--->程序员 Thread-1--->程序员
-
-
同时开启3个线程卖票
-
RunnableImpl类
package cn.xiaoge.day17.demo08; /* 实现卖票案例 */ public class RunnableImpl implements Runnable { // 定义一个多线程共享的票源 private int ticket = 100; // 设置线程任务: 卖票 @Override public void run() { // 使用死循环, 让买票操作重复执行 while(true) { // 先判断是否存在 if(ticket > 0) { // 提高安全问题出现的概率, 让程序睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 票存在, 卖票 ticket-- System.out.println(Thread.currentThread().getName() + "-->正在买第: " + ticket + "张票"); ticket--; } } } }
-
执行路口
package cn.xiaoge.day17.demo08; /* 模拟卖票案例 创建3个线程, 同时开启, 对共享的票进行出售 */ public class Demo01Ticket { public static void main(String[] args) { // 创建Runnable接口的实现类对象 RunnableImpl run = new RunnableImpl(); // 创建Thread类对象, 构造方法中传递Runnable接口的实现类对象 Thread t0 = new Thread(run); Thread t1 = new Thread(run); Thread t2 = new Thread(run); // 调用start开启多线程 t0.start(); t1.start(); t2.start(); } } // 运行结果 Thread-0-->正在买第: 100张票 Thread-2-->正在买第: 99张票 Thread-1-->正在买第: 98张票 Thread-2-->正在买第: 97张票 Thread-1-->正在买第: 97张票 Thread-0-->正在买第: 97张票 Thread-1-->正在买第: 94张票 Thread-2-->正在买第: 94张票 Thread-0-->正在买第: 92张票 ..... 卖出去了重复票
-
-
使用同步技术—synchronized
-
RunnableImpl类
package cn.xiaoge.day17.demo09; /* 卖票案例出现了线程安全问题 卖出了不存在的票和重复的票 解决线程安全问题的一种方案: 使用同步代码块 格式: synchronized(锁对象) { 可能会出现线程安全问题的代码(访问了共享数据的代码) } 注意: 1. 通过代码块中的锁对象, 可以使用任意的对象 2. 但是必须保证多个线程使用的锁对象是同一个 3. 锁对象作用: 把同步代码块锁住, 只让一个线程在同步代码块中执行 */ public class RunnableImpl implements Runnable { // 定义一个多线程共享的票源 private int ticket = 100; // 创建锁对象 Object obj = new Object(); // 设置线程任务: 卖票 @Override public void run() { // 使用死循环, 让买票操作重复执行 while(true) { synchronized(obj) { // 先判断是否存在 if(ticket > 0) { // 提高安全问题出现的概率, 让程序睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 票存在, 卖票 ticket-- System.out.println(Thread.currentThread().getName() + "-->正在买第: " + ticket + "张票"); ticket--; } } } } }
-
执行路口
package cn.xiaoge.day17.demo09; /* 模拟卖票案例 创建3个线程, 同时开启, 对共享的票进行出售 */ public class Demo01Ticket { public static void main(String[] args) { // 创建Runnable接口的实现类对象 RunnableImpl run = new RunnableImpl(); // 创建Thread类对象, 构造方法中传递Runnable接口的实现类对象 Thread t0 = new Thread(run); Thread t1 = new Thread(run); Thread t2 = new Thread(run); // 调用start开启多线程 t0.start(); t1.start(); t2.start(); } } // 运行结果 Thread-0-->正在买第: 100张票 Thread-0-->正在买第: 99张票 Thread-0-->正在买第: 98张票 Thread-0-->正在买第: 97张票 Thread-0-->正在买第: 96张票 Thread-0-->正在买第: 95张票 Thread-0-->正在买第: 94张票 Thread-0-->正在买第: 93张票 Thread-0-->正在买第: 92张票 ... 没有重复票
-
-
静态同步方法
-
RunnableImpl类
package cn.xiaoge.day17.demo10; /* 卖票案例出现了线程安全问题 卖出了不存在的票和重复的票 解决线程安全问题的二种方案: 使用同步方法 使用步骤: 1. 把访问了共享数据的代码抽取出来, 放到一个方法中 2. 在方法上添加synchronized修饰符 格式: 定义方法的格式 修饰符 synchronized 返回值类型 方法名称(参数列表) { 可能会出现线程安全问题的代码(访问了共享数据的代码) } */ public class RunnableImpl implements Runnable { // 定义一个多线程共享的票源 private static int ticket = 100; // 创建锁对象 Object obj = new Object(); // 设置线程任务: 卖票 @Override public void run() { // 使用死循环, 让买票操作重复执行 while(true) { payTicket(); } } /* 静态的同步方法 锁对象是谁? 不能是this this是创建对象之后产生的, 静态方法优先于对象 静态方法的锁对象是本类的class属性-->class文件对象(反射) */ public static synchronized void payTicketStatic() { // 先判断是否存在 if(ticket > 0) { // 静态访问变量也必须是静态变量 // 提高安全问题出现的概率, 让程序睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 票存在, 卖票 ticket-- System.out.println(Thread.currentThread().getName() + "-->正在买第: " + ticket + "张票"); ticket--; } } /* 定义一个同步方法 同步方法也会把方法内部的代码锁住 只让一个线程执行 同步方法的锁对象是谁? 就是实现类对象 new RunnableImpl() 也就是this */ public /*synchronized*/ void payTicket() { synchronized(this) { // 先判断是否存在 if(ticket > 0) { // 提高安全问题出现的概率, 让程序睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 票存在, 卖票 ticket-- System.out.println(Thread.currentThread().getName() + "-->正在买第: " + ticket + "张票"); ticket--; } } } /*public synchronized void payTicket() { // 先判断是否存在 if(ticket > 0) { // 提高安全问题出现的概率, 让程序睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 票存在, 卖票 ticket-- System.out.println(Thread.currentThread().getName() + "-->正在买第: " + ticket + "张票"); ticket--; } }*/ }
-
执行路口
package cn.xiaoge.day17.demo10; /* 模拟卖票案例 创建3个线程, 同时开启, 对共享的票进行出售 */ public class Demo01Ticket { public static void main(String[] args) { // 创建Runnable接口的实现类对象 RunnableImpl run = new RunnableImpl(); // 创建Thread类对象, 构造方法中传递Runnable接口的实现类对象 Thread t0 = new Thread(run); Thread t1 = new Thread(run); Thread t2 = new Thread(run); // 调用start开启多线程 t0.start(); t1.start(); t2.start(); } } // 运行结果 Thread-0-->正在买第: 100张票 Thread-0-->正在买第: 99张票 Thread-0-->正在买第: 98张票 Thread-0-->正在买第: 97张票 Thread-0-->正在买第: 96张票 Thread-0-->正在买第: 95张票 Thread-0-->正在买第: 94张票 Thread-0-->正在买第: 93张票 Thread-0-->正在买第: 92张票 ... 没有重复票
-
-
解决线程安全问题—Lock
-
RunnableImpl类
package cn.xiaoge.day17.demo11; /* 卖票案例出现了线程安全问题 卖出了不存在的票和重复的票 解决线程安全问题的三种方案: 使用Lock锁 java.util.concurrent.locks.Lock接口 Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作. Lock接口中的方法: void lock() 获取锁 void unlock() 释放锁 java.util.concurrent.locks.ReentrantLock implements Lock接口 使用步骤: 1. 在成员位置创建一个ReentrantLock对象 2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁 3. 在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁 */ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class RunnableImpl implements Runnable { // 定义一个多线程共享的票源 private int ticket = 100; // 1. 在成员位置创建一个ReentrantLock对象 Lock l = new ReentrantLock(); // 创建锁对象 Object obj = new Object(); // 设置线程任务: 卖票 @Override public void run() { // 使用死循环, 让买票操作重复执行 while(true) { // 2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁 l.lock(); payTicket(l); } } public void payTicket(Lock l) { // 先判断是否存在 if(ticket > 0) { // 提高安全问题出现的概率, 让程序睡眠 try { Thread.sleep(10); // 票存在, 卖票 ticket-- System.out.println(Thread.currentThread().getName() + "-->正在买第: " + ticket + "张票"); ticket--; } catch (InterruptedException e) { e.printStackTrace(); } finally { // 3. 在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁 l.unlock(); // 无论程序是否异常, 都会释放锁 } } } }
-
执行路口
package cn.xiaoge.day17.demo11; /* 模拟卖票案例 创建3个线程, 同时开启, 对共享的票进行出售 */ public class Demo01Ticket { public static void main(String[] args) { // 创建Runnable接口的实现类对象 RunnableImpl run = new RunnableImpl(); // 创建Thread类对象, 构造方法中传递Runnable接口的实现类对象 Thread t0 = new Thread(run); Thread t1 = new Thread(run); Thread t2 = new Thread(run); // 调用start开启多线程 t0.start(); t1.start(); t2.start(); } } // 运行结果 // 运行结果 Thread-0-->正在买第: 100张票 Thread-0-->正在买第: 99张票 Thread-0-->正在买第: 98张票 Thread-0-->正在买第: 97张票 Thread-0-->正在买第: 96张票 Thread-0-->正在买第: 95张票 Thread-0-->正在买第: 94张票 Thread-0-->正在买第: 93张票 Thread-0-->正在买第: 92张票 ... 没有重复票
-
-
Object类中的wait(等待)和notify(唤醒)和notifyAll(唤醒所有等待线程)
-
执行路口
package cn.xiaoge.day17.demo12; /* 等待唤醒实例: 线程之间的通信 创建一个顾客线程(消费者): 告知老板要的包子的种类和数量, 调用wait方法, 放弃cpu的执行, 进入到WAITING状态(无线等待) 创建一个老板线程(生产者): 花了5秒做包子, 做好了包子之后, 调用notify方法, 唤醒顾客吃包子 注意: 顾客和老板线程必须使用同步代码块包裹起来, 保证等待和唤醒只能有一个在执行 同步使用的锁对象必须保证唯一 只有锁对象才能调用wait和notify方法 Object类中的方法: void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前, 导致当前线程等待. void notify() 唤醒在此对象监视器上等待的单个线程. 会继续执行wait方法之后的代码 */ public class Demo01WaitAndNotify { public static void main(String[] args) { // 创建锁对象, 保证唯一 Object obj = new Object(); // 创建一个顾客线程(消费者) new Thread() { @Override public void run() { // 保证等待和唤醒的线程只能有一个执行, 需要使用同步技术 synchronized(obj) { System.out.println("告知老板要的包子的种类和数量"); try { // 调用wait方法, 放弃cpu的执行, 进入到WAITING状态(无线等待) obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 唤醒之后执行的代码 System.out.println("包子已经做好了, 开吃!"); } } }.start(); // 创建老板的线程(生产者) new Thread() { @Override public void run() { try { // 花5秒做包子 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 保证等待和唤醒的线程只能有一个执行, 需要使用同步技术 synchronized(obj) { System.out.println("老板5秒钟之后做好包子, 告知顾客, 可以吃包子了"); // 做好包子之后, 调用notify方法, 唤醒顾客吃包子 obj.notify(); } } }.start(); } } // 运行结果 告知老板要的包子的种类和数量 // ---等待了5秒--- 老板5秒钟之后做好包子, 告知顾客, 可以吃包子了 包子已经做好了, 开吃!
-
执行路口
package cn.xiaoge.day17.demo12; /* 进入到TimeWaiting(计时等待)有两种方法 1. 使用sleep(long m)方法, 在毫秒值结束之后, 线程睡醒进入到Runnable/Blocked状态 2. 使用wait(long m)方法, wait方法如果在毫秒值结束之后, 还没有被notify唤醒, 就会自动醒来, 线程睡醒进入到Runnable/Blocked状态 唤醒的方法: void notify() 唤醒在此对象监视器上等待的单个线程. void notifyAll() 唤醒在此对象监视器上等待的所有线程. */ public class Demo02WaitAndNotify { public static void main(String[] args) { Object obj = new Object(); // 创建一个顾客线程(消费者) new Thread() { @Override public void run() { // 保证等待和唤醒的线程只能有一个执行, 需要使用同步技术 synchronized(obj) { System.out.println("顾客1告知老板要的包子的种类和数量"); try { // 调用wait方法, 放弃cpu的执行, 进入到WAITING状态(无线等待) obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 唤醒之后执行的代码 System.out.println("包子已经做好了, 顾客1开吃!"); } } }.start(); // 创建一个顾客线程(消费者) new Thread() { @Override public void run() { // 保证等待和唤醒的线程只能有一个执行, 需要使用同步技术 synchronized(obj) { System.out.println("顾客2告知老板要的包子的种类和数量"); try { // 调用wait方法, 放弃cpu的执行, 进入到WAITING状态(无线等待) obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 唤醒之后执行的代码 System.out.println("包子已经做好了, 顾客2开吃!"); } } }.start(); // 创建老板的线程(生产者) new Thread() { @Override public void run() { try { // 花5秒做包子 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 保证等待和唤醒的线程只能有一个执行, 需要使用同步技术 synchronized(obj) { System.out.println("老板5秒钟之后做好包子, 告知顾客, 可以吃包子了"); // 做好包子之后, 调用notify方法, 唤醒顾客吃包子 // obj.notify(); 如果有多个等待线程, 随机唤醒一个 obj.notifyAll(); // 唤醒所有等待的线程 } } }.start(); } } // 运行结果 顾客1告知老板要的包子的种类和数量 顾客2告知老板要的包子的种类和数量 老板5秒钟之后做好包子, 告知顾客, 可以吃包子了 包子已经做好了, 顾客2开吃! 包子已经做好了, 顾客1开吃!
-
-
案列
-
BaoZi类
package cn.xiaoge.day17.demo13; /* 资源类: 包子类 设置包子的属性 皮 陷 包子的状态: 有 true, 没有 false */ public class BaoZi { // 皮 String pi; // 陷 String xian; // 包子状态: 有 true, 没有 false boolean flag = false; }
-
BaoZiPu类
package cn.xiaoge.day17.demo13; /* 生产者(包子铺)类: 是一个线程类, 可以继承Thread 设置线程任务(run): 生产包子 对包子的状态进行判断 true: 有包子 包子铺调用wait方法进入等待状态 false: 没有包子 包子铺生产包子 增加一些趣味性: 交替生产两种包子 有两种状态(i % 2 == 0) 包子铺生产好了包子 修改包子的状态为true 有 唤醒吃货线程, 让吃货线程吃包子 注意: 包子铺线程和包子线程关系-->通信(互斥) 必须使用同步技术保证两个线程只能有一个在执行 锁对象必须保证唯一, 可以使用包子对象作为锁对象 包子铺和吃货这个类就需要把包子对象作为参数传递进来 1. 需要在成员位置创建一个包子变量 2. 使用带参数构造方法, 为这个包子变量赋值 */ public class BaoZiPu extends Thread { // 锁对象包子 private BaoZi baoZi; public BaoZi getBoZi() { return baoZi; } public void setBoZi(BaoZi baoZi) { this.baoZi = baoZi; } public BaoZiPu(BaoZi baoZi) { this.baoZi = baoZi; } @Override public void run() { // 定义一个变量 int count = 0; // 让包子铺一直生产包子 while(true){ // 必须使用同步技术保证两个线程只能有一个在执行 synchronized (this.baoZi) { // 对包子的状态进行判断 if (this.baoZi.flag) { // 有包子 try { // 唤醒吃货线程 this.baoZi.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 被唤醒之后执行, 包子铺生产包子 // 增加一些趣味性: 交替生产两种包子 if (count % 2 == 0) { // 生产 薄皮三鲜馅包子 this.baoZi.pi = "薄皮"; this.baoZi.xian = "三鲜馅"; } else { // 生产 冰皮 牛肉大葱馅 this.baoZi.pi = "冰皮"; this.baoZi.xian = "牛肉大葱馅"; } count++; System.out.println("包子铺正在生产: " + this.baoZi.pi + this.baoZi.xian + "包子"); // 生产包子需要3秒钟 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // 包子铺生产好了包子 // 修改包子的状态为true 有 this.baoZi.flag = true; // 唤醒吃货线程, 让吃货线程吃包子 this.baoZi.notify(); System.out.println("包子铺已经生产好了: " + this.baoZi.pi + this.baoZi.xian + "包子, 吃货可以开始吃了"); } } } }
-
ChiHuo类
package cn.xiaoge.day17.demo13; /* 消费者(吃货)类: 是一个线程类, 可以继承Thread 设置线程任务(run): 吃包子 对包子的状态进行判断 false: 没有包子 吃货调用wait方法进入等待状态 true: 有包子 吃货吃包子 吃货吃完包子 修改包子的状态为false 吃货唤醒包子铺线程, 生产包子 */ public class ChiHuo extends Thread { // 锁对象 包子 private BaoZi baoZi; public ChiHuo(BaoZi baoZi) { this.baoZi = baoZi; } public BaoZi getBoZi() { return baoZi; } public void setBoZi(BaoZi baoZi) { this.baoZi = baoZi; } @Override public void run() { // 使用死循环, 让吃货一直吃包子 while(true){ // 必须使用同步技术保证两个线程只能有一个在执行 synchronized (this.baoZi) { // 对包子的状态进行判断 if (this.baoZi.flag) { // 被唤醒之后执行的代码, 吃包子 System.out.println("吃货正在吃: " + this.baoZi.pi + this.baoZi.xian + "的包子"); // 有包子, 吃包子 // 修改包子的状态为false this.baoZi.flag = false; // 吃货唤醒包子铺线程, 生产包子 this.baoZi.notify(); System.out.println("吃货已经把: " + this.baoZi.pi + this.baoZi.xian + "的包子吃完了, 包子铺开始生产包子"); } // 没有包子 吃货调用wait方法进入等待状态 try { this.baoZi.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("==================================================="); } } } }
-
执行路口
package cn.xiaoge.day17.demo13; /* 测试类: 包含main方法, 程序执行入口, 启动程序 创建包子对象; 创建包子铺线程, 开启, 生产包子; 创建吃货线程, 开启, 吃包子; */ public class Demo { public static void main(String[] args) { // 创建包子对象 BaoZi baoZi = new BaoZi(); new BaoZiPu(baoZi).start(); new ChiHuo(baoZi).start(); } } // 运行结果 包子铺正在生产: 薄皮三鲜馅包子 包子铺已经生产好了: 薄皮三鲜馅包子, 吃货可以开始吃了 吃货正在吃: 薄皮三鲜馅的包子 吃货已经把: 薄皮三鲜馅的包子吃完了, 包子铺开始生产包子 包子铺正在生产: 冰皮牛肉大葱馅包子 包子铺已经生产好了: 冰皮牛肉大葱馅包子, 吃货可以开始吃了 =================================================== 吃货正在吃: 冰皮牛肉大葱馅的包子 吃货已经把: 冰皮牛肉大葱馅的包子吃完了, 包子铺开始生产包子 包子铺正在生产: 薄皮三鲜馅包子 .....
-
-
线程池
-
类
package cn.xiaoge.day17.demo14; public class RunnableImpl implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "创建了一个新的线程执行"); } }
-
执行路口
package cn.xiaoge.day17.demo14; /* 线程池: JDK1.5之后提供的 java.util.concurrent.Executors: 线程池的工厂类, 用来生成线程池 Executors类中的静态方法: static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池 参数: int nThreads: 创建线程池中包含的线程数量 返回值: ExecutorService接口, 返回的是ExecutorService接口的实现类对象, 我们可以使用ExecutorService接口接收(面向接口编程) java.util.concurrent.ExecutorService: 线程池接口 用来从线程池中获取线程, 调用start方法, 执行线程任务 submit(Runnable task) 提交一个 Runnable 任务用于执行 关闭/销毁线程池的方法 void shutdown() 线程池的使用步骤: 1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池 2. 创建一个类, 实现Runnable接口, 重写run方法, 设置线程任务 3. 调用ExecutorService中的方法submit, 传递线程任务(实现类), 开启线程, 执行run方法 4. 调用ExecutorService中的方法shutdown销毁线程池(不建议使用) */ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo01ThreadPool { public static void main(String[] args) { // 1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池 ExecutorService es = Executors.newFixedThreadPool(2); // 3. 调用ExecutorService中的方法submit, 传递线程任务(实现类), 开启线程, 执行run方法 es.submit(new RunnableImpl()); // pool-1-thread-2创建了一个新的线程执行 es.submit(new RunnableImpl()); // pool-1-thread-1创建了一个新的线程执行 // 线程池会一直开启, 使用完了线程, 会自动把线程归还给线程池, 线程可以继续使用 es.submit(new RunnableImpl()); // pool-1-thread-2创建了一个新的线程执行 // 4. 调用ExecutorService中的方法shutdown销毁线程池(不建议使用) es.shutdown(); // es.submit(new RunnableImpl()); // 抛异常RejectedExecutionException, 线程池都没有了, 就不能获取线程了 } } // 运行结果 pool-1-thread-2创建了一个新的线程执行 pool-1-thread-1创建了一个新的线程执行 pool-1-thread-1创建了一个新的线程执行
-