java
并行:指两个或多个事件在同一时刻编程
java.lang.Thread 类安全
public Thread() :分配一个新的线程对象。多线程
public Thread(String name) :分配一个指定名字的新的线程对象。并发
public Thread(Runnable target) :分配一个带有指定目标新的线程对象。ide
public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。测试
public void start() :致使此线程开始执行; Java虚拟机调用此线程的run方法。this
多线程的开启方式只有调用start方法开启。spa
public String getName() :获取当前线程名称。线程
public static Thread currentThread() :返回对当前正在执行的线程对象的引用。
1 /* 2 * 获取线程的名称: 3 * 1,使用Thread类中的getName()方法 4 * String getName() 返回该线程的名称 5 * 2,能够先获取到当前正在执行的线程。使用线程中的方法getName()获取线程的名称 6 * static Thread currentThread() 返回对当前正在执行的线程对象的引用 7 * */ 8 9 10 public class MyThread extends Thread { 11 12 @Override 13 public void run(){ 14 // 获取线程的名称 15 16 // String name = getName(); 17 // System.out.println(name); 18 19 20 // Thread t = Thread.currentThread(); 21 // System.out.println(t); 22 // 23 // String name = t.getName(); 24 // System.out.println(name); 25 26 27 // 链式编程 28 System.out.println(Thread.currentThread().getName()); 29 } 30 } 31 32 33 ------------------------------------------------------------- 34 /* 35 * 线程的名称: 36 * 主线程:main 37 * 新线程:Thread-0,Thread-1 38 * */ 39 40 public class Demo01GetThreadName { 41 public static void main(String[] args) { 42 // 建立Thread类的子类对象 43 MyThread mt = new MyThread(); 44 45 // 调用start方法,开启新线程执行run方法 46 mt.start(); 47 48 new MyThread().start(); 49 new MyThread().start(); 50 51 System.out.println(Thread.currentThread().getName()); 52 53 } 54 }
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时中止执行)。
1 /* 2 * public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时中止执行)。 3 * 毫秒数结束以后,线程继续执行 4 */ 5 6 public class Demo01Sleep { 7 public static void main(String[] args) { 8 // 模拟秒表 9 for(int i =0;i<60;i++){ 10 11 System.out.println(i); 12 13 // 使用Thread类中的sleep方法让程序睡眠1秒 14 try { 15 Thread.sleep(1000); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 } 20 } 21 }
java.long.Thread类:是描述线程的类,咱们想要实现多线程程序,就必须继承Thread类
实现步骤:
1,建立一个Thread类的子类
2,在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要干的事)
3,建立Thread类的子类对象
4,调用Thread类中的方法start方法,开启新的线程,执行run方法【start方法自动去子类中找run方法执行】
void start() 使该线程开始执行;java虚拟机调用该线程的run方法。
结果是两个线程并发的运行,当前线程(main线程)和另外一个线程(建立的新线程,执行其run方法)。
屡次启动一个线程是非法的。特别是当前线程已经结束执行后,不能再从新启动
1 //1.建立一个Thread类的子类 2 public class MyThread extends Thread { 3 4 //2,在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要干的事) 5 @Override 6 public void run() { 7 for (int i = 0; i < 20; i++) { 8 System.out.println("run" + i); 9 } 10 } 11 } 12 13 14 --------------------------------------------------------------- 15 public class Demo01Thread { 16 17 public static void main(String[] args) { 18 // 3,建立Thread类的子类对象 19 MyThread my = new MyThread(); 20 21 //4,调用Thread类中的方法start方法,开启新的线程,执行run方法 22 my.start(); 23 24 //my.start();错误的,屡次启动一个线程非法 25 26 for (int i = 0; i < 20; i++) { 27 System.out.println("main" + i); 28 } 29 } 30 }
注意:java程序属于抢占式调度,那个线程优先级高,那个线程就优先执行。同一个优先级就随机选一个执行
多线程的流程图解和内存图解:
java.long.Runnable:Runnable 接口应该由那些打算经过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
java.long.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方法
1 // 1,建立一个Runnable接口的实现类 2 public class RunnableImp implements Runnable { 3 4 // 2,在实现类中重写Runnable接口中的run方法,设置线程任务。 5 @Override 6 public void run() { 7 for (int i = 0; i < 20; i++) { 8 System.out.println(Thread.currentThread().getName() + i); 9 } 10 } 11 } 12 13 14 ----------------------------------------------------------------- 15 public class Dem01Runnable { 16 public static void main(String[] args) { 17 // 3,建立一个Runnable接口的实现类对象 18 RunnableImp run = new RunnableImp(); 19 20 // 4,建立一个Thread类对象,参数中传递Runnable接口的实现类对象 21 // Thread t = new Thread(run);// 打印线程名称 22 Thread t = new Thread(new RunnableImp2());// 打印hello world 23 24 // 5,调用Thread类中的start方法开启新的线程执行run方法 25 t.start(); 26 27 for (int i = 0; i < 20; i++) { 28 System.out.println(Thread.currentThread().getName()+i); 29 } 30 } 31 }
实现Runnable接口建立多线程的好处:
1,避免了单继承的局限性:一个类只能继承一个类(一我的只能有一个亲爹),类继承了Thread类就不能继承其余的类。
实现了Runnable便可,还能够继承其余的类,实现其余的接口
2,加强了程序的扩展性,下降了程序的耦合性(解耦):实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
1)实现类中,重写了run方法:用来设置线程任务。
2)建立Thread类对象,调用start方法:用来开启多线程。
匿名内部类方法实现线程的建立
匿名:没有名字
内部类:写在其余类内部的类
匿名内部类的做用:简化代码
把子类继承父类,重写父类的方法,建立子类对象集合一步完成
把实现类实现接口,重写接口中的方法,建立实现类对象合成一步完成
匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
格式:
1 new 父类/接口(){ 2 重写父类/接口中的方法 3 };
1 public class Demo01InnerClassThread { 2 3 public static void main(String[] args) { 4 // 线程的父类是Thread 5 // 原来的步骤:new MyThread子类,调用start方法 6 7 new Thread(){ 8 // 重写run方法,设置线程任务 9 @Override 10 public void run(){ 11 for (int i = 0; i < 20; i++) { 12 System.out.println(Thread.currentThread().getName()+"哈哈哈"); 13 } 14 } 15 }.start(); 16 17 18 19 20 // 线程的接口Runnable 21 // Runnable r = new RunnableImp() 22 23 Runnable run = new Runnable(){ 24 @Override 25 public void run(){ 26 for (int i = 0; i < 20; i++) { 27 System.out.println(Thread.currentThread().getName()+"呜呜呜"); 28 } 29 } 30 }; 31 32 new Thread(run).start(); 33 34 35 36 // 简化接口的方式 37 new Thread(new Runnable() { 38 @Override 39 public void run() { 40 for (int i = 0; i < 20; i++) { 41 System.out.println(Thread.currentThread().getName()+"嗯嗯嗯"); 42 43 } 44 } 45 }).start(); 46 47 } 48 }
定义:若是有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是同样 的,并且其余的变量的值也和预期的是同样的,就是线程安全的。
下面使用一个卖票案例显示可能出现的问题
1 /* 2 实现买票案例 3 */ 4 public class RunnableImp implements Runnable { 5 // 定义一个多个线程共享的票源 6 private int ticket = 100; 7 8 // 设置线程任务:买票 9 @Override 10 public void run() { 11 // 使用死循环,让买票操做重复执行 12 while (true){ 13 // 判断票是否存在 14 if (ticket>0){ 15 // 提升安全问题出现的几率,让程序睡眠 16 try { 17 Thread.sleep(10); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 22 23 // 票存在,卖票 24 System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"票"); 25 ticket--; 26 } 27 } 28 } 29 } 30 31 32 33 34 -------------------------------------------------------- 35 /* 36 模拟卖票案例: 37 建立三个线程,同时开启,对共享的票出售 38 */ 39 public class Demo01Ticket { 40 public static void main(String[] args) { 41 // 建立Runnable接口的实现类对象 42 RunnableImp run = new RunnableImp(); 43 44 // 一个实现类让三个线程访问 45 46 // 建立Thread类对象,构造方法中传递Runnable接口的实现类对象 47 Thread t1 = new Thread(run); 48 Thread t2 = new Thread(run); 49 Thread t3 = new Thread(run); 50 51 // 开启多线程 52 t1.start(); 53 t2.start(); 54 t3.start(); 55 56 // 输出的有不存在的票和重复的票 57 } 58 }
输出结果的部分截图
发现程序出现了两个问题:
1. 相同的票数,好比5这张票被卖了两回。
2. 不存在的票,好比0票与-1票,是不存在的
线程不安全:这种问题,几个窗口(线程)票数不一样步了,这种问题称为线程不安全
当咱们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操做,就容易出现线程安全问题。
要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制 (synchronized)来解决
线程同步的三种实现方式:
1. 同步代码块。
2. 同步方法。
3. 锁机制。
语法:
1 synchronized(锁对象){ 2 可能会出现线程安全问题的代码(访问了共享数据的代码) 3 }
注意:
1,经过代码块中的锁对象,可使用任意的对象作为锁对象
2,可是必须保证多个线程使用的锁对象是同一个
3,锁对象的做用:把同步代码块锁住,只让一个线程在同步代码块中执行
1 public class RunnableImp implements Runnable { 2 // 定义一个多个线程共享的票源 3 private int ticket = 100; 4 5 // 建立一个锁对象 6 Object obj = new Object(); 7 8 9 // 设置线程任务:买票 10 @Override 11 public void run() { 12 // 使用死循环,让买票操做重复执行 13 while (true) { 14 // 建立同步代码块 15 synchronized (obj) { 16 // 判断票是否存在 17 if (ticket > 0) { 18 // 提升安全问题出现的几率,让程序睡眠 19 try { 20 Thread.sleep(100); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 25 26 // 票存在,卖票 27 System.out.println(Thread.currentThread().getName() + "--->正在卖第" + ticket + "票"); 28 ticket--; 29 } 30 } 31 } 32 } 33 } 34 35 36 37 38 ------------------------------------------------------------------------------- 39 public class Demo01Ticket { 40 public static void main(String[] args) { 41 // 建立Runnable接口的实现类对象 42 RunnableImp run = new RunnableImp(); 43 44 // 一个实现类让三个线程访问 45 46 // 建立Thread类对象,构造方法中传递Runnable接口的实现类对象 47 Thread t1 = new Thread(run); 48 Thread t2 = new Thread(run); 49 Thread t3 = new Thread(run); 50 51 // 开启多线程 52 t1.start(); 53 t2.start(); 54 t3.start(); 55 } 56 }
格式:
1 修饰符 synchronized 返回值类型 方法名(参数列表){ 2 可能出现线程安全问题的代码(访问了共享数据的代码) 3 }
使用步骤:
1,访问了共享数据的代码抽取出来,放在一个方法中
2,在方法上添加synchronized修饰符
1 public class RunnableImp implements Runnable { 2 // 定义一个多个线程共享的票源 3 private static int ticket = 100; 4 5 6 // 设置线程任务:买票 7 @Override 8 public void run() { 9 // 使用死循环,让买票操做重复执行 10 while (true) { 11 // 判断票是否存在 12 // payTicket(); 13 payTicketStatic(); 14 } 15 } 16 17 // 静态同步方法 18 /* 19 * 锁对象是谁? 20 * 不能是this 21 * this是建立对象以后产生的,静态方法优先于对象 22 * 静态方法的锁对象是本类的class属性--->class文件对象 23 * 24 * */ 25 26 public static synchronized void payTicketStatic() { 27 if (ticket > 0) { 28 // 提升安全问题出现的几率,让程序睡眠 29 try { 30 Thread.sleep(10); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 35 36 // 票存在,卖票 37 System.out.println(Thread.currentThread().getName() + "--->正在卖第" + ticket + "票"); 38 ticket--; 39 } 40 } 41 42 43 // 定义一个同步方法 44 /* 45 * 同步方法也会把方法内部的代码锁住 46 * 只让一个线程执行 47 * 同步方法的锁对象是谁? 48 * 就是实现类对象 new RunnableImp() 49 * 也就是this 50 * 51 * */ 52 public synchronized void payTicket() { 53 if (ticket > 0) { 54 // 提升安全问题出现的几率,让程序睡眠 55 try { 56 Thread.sleep(10); 57 } catch (InterruptedException e) { 58 e.printStackTrace(); 59 } 60 61 62 // 票存在,卖票 63 System.out.println(Thread.currentThread().getName() + "--->正在卖第" + ticket + "票"); 64 ticket--; 65 } 66 } 67 } 68 69 70 71 ------------------------------------------------------------------ 72 public class Demo01Ticket { 73 public static void main(String[] args) { 74 // 建立Runnable接口的实现类对象 75 RunnableImp run = new RunnableImp(); 76 77 // 一个实现类让三个线程访问 78 79 // 建立Thread类对象,构造方法中传递Runnable接口的实现类对象 80 Thread t1 = new Thread(run); 81 Thread t2 = new Thread(run); 82 Thread t3 = new Thread(run); 83 84 // 开启多线程 85 t1.start(); 86 t2.start(); 87 t3.start(); 88 89 // 输出的有不存在的票和重复的票 90 } 91 }
java.util.concurrent.locks.lock接口:Lock 实现提供了比使用 synchronized 方法和语句可得到的更普遍的锁定操做。
Lock接口中的方法:
void lock() 获取锁。
void unlock() 释放锁。
Lock为接口,因此咱们使用其实现类(ReentrantLock):java.util.concurrent.locks.ReentrantLock implements Lock
使用步骤:
1,在成员位置建立一个Lock实现类对象ReentrantLock对象
2,在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
3,在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁
1 import java.util.concurrent.locks.Lock; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 public class RunnableImp implements Runnable { 5 // 定义一个多个线程共享的票源 6 private int ticket = 100; 7 8 // 1,在成员位置建立一个Lock实现类对象ReentrantLock对象 9 Lock l = new ReentrantLock(); 10 11 12 13 // // 设置线程任务:买票 14 // @Override 15 // public void run() { 16 // // 使用死循环,让买票操做重复执行 17 // while (true){ 18 // 19 // // 2,在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁 20 // l.lock(); 21 // // 判断票是否存在 22 // if (ticket>0){ 23 // // 提升安全问题出现的几率,让程序睡眠 24 // try { 25 // Thread.sleep(10); 26 // } catch (InterruptedException e) { 27 // e.printStackTrace(); 28 // } 29 // 30 // 31 // // 票存在,卖票 32 // System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"票"); 33 // ticket--; 34 // } 35 // 36 // // 3,在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁 37 // l.unlock(); 38 // } 39 // } 40 41 42 // 更好的写法 43 44 // 设置线程任务:买票 45 @Override 46 public void run() { 47 // 使用死循环,让买票操做重复执行 48 while (true){ 49 50 // 2,在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁 51 l.lock(); 52 // 判断票是否存在 53 if (ticket>0){ 54 // 提升安全问题出现的几率,让程序睡眠 55 try { 56 Thread.sleep(10); 57 // 票存在,卖票 58 System.out.println(Thread.currentThread().getName()+"--->正在卖第"+ticket+"票"); 59 ticket--; 60 } catch (InterruptedException e) { 61 e.printStackTrace(); 62 } finally { 63 // 不管程序出现不出现异常,都会把锁释放 64 // 3,在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁 65 l.unlock(); 66 } 67 } 68 } 69 } 70 } 71 72 73 ----------------------------------------------------------- 74 public class Demo01Ticket { 75 public static void main(String[] args) { 76 // 建立Runnable接口的实现类对象 77 RunnableImp run = new RunnableImp(); 78 79 // 一个实现类让三个线程访问 80 81 // 建立Thread类对象,构造方法中传递Runnable接口的实现类对象 82 Thread t1 = new Thread(run); 83 Thread t2 = new Thread(run); 84 Thread t3 = new Thread(run); 85 86 // 开启多线程 87 t1.start(); 88 t2.start(); 89 t3.start(); 90 } 91 }
各类状态之间的互相转换
多线程中在使用锁对象的时候,大多数使用Object类来建立对象,由于该对象中有一些关于多线程的方法
Object类中的方法:
void wait(long timeout) :在其余线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,致使当前线程等待。
void wait()在其余线程调用此对象的 notify() 方法或 notifyAll() 方法前,致使当前线程等待。
void notify() 唤醒在此对象监视器上等待的单个线程。会执行wait以后的代码
void notifyAll() :唤醒在此对象监视器上等待的全部线程。
1 /* 2 等待唤醒案例:线程之间的通讯 3 建立一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃CPU的执行,进入到waiting状态(无限等待) 4 建立一个老板线程(消费者):花了5秒作包子,包子作好以后调用notify()方法,唤醒顾客吃包子 5 6 7 注意: 8 顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行 9 同步使用的锁对象必须保证惟一 10 只有锁对象才能调用wait和notify方法 11 */ 12 13 14 public class Demo01WaitAndNotify { 15 public static void main(String[] args) { 16 Object obj = new Object(); 17 18 // 建立一个老板线程(生产者) 19 new Thread() { 20 @Override 21 public void run() { 22 // 一直作包子 23 while (true){ 24 // 花5秒作包子 25 try { 26 Thread.sleep(5000); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 // 保证等待和唤醒的线程只能有一个在执行 31 synchronized (obj) { 32 33 System.out.println("老板5秒中以后作好包子,告知顾客能够吃包子了"); 34 // 调用wait方法,放弃CPU的执行,进入到waiting状态(无限等待) 35 obj.notify(); 36 } 37 } 38 } 39 }.start(); 40 41 42 new Thread(){ 43 @Override 44 public void run() { 45 // 一直等着买包子 46 while (true){ 47 // 保证等待和唤醒的线程只能有一个在执行 48 synchronized (obj) { 49 System.out.println("告知老板要的包子种类和数量"); 50 // 调用wait方法,放弃CPU的执行,进入到waiting状态(无限等待) 51 try { 52 obj.wait(); 53 } catch (InterruptedException e) { 54 e.printStackTrace(); 55 } 56 // 唤醒以后执行的代码 57 System.out.println("包子作好了,开吃"); 58 System.out.println("---------------------"); 59 } 60 } 61 } 62 }.start(); 63 } 64 }
1 public class Demo02WaitAndNotify { 2 public static void main(String[] args) { 3 4 Object obj = new Object(); 5 6 7 new Thread(){ 8 @Override 9 public void run() { 10 // 一直等着买包子 11 while (true){ 12 // 保证等待和唤醒的线程只能有一个在执行 13 synchronized (obj) { 14 System.out.println("顾客1:告知老板要的包子种类和数量"); 15 // 调用wait方法,放弃CPU的执行,进入到waiting状态(无限等待) 16 try { 17 obj.wait(); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 // 唤醒以后执行的代码 22 System.out.println("顾客1:包子作好了,开吃"); 23 System.out.println("---------------------"); 24 } 25 } 26 } 27 }.start(); 28 29 new Thread(){ 30 @Override 31 public void run() { 32 // 一直等着买包子 33 while (true){ 34 // 保证等待和唤醒的线程只能有一个在执行 35 synchronized (obj) { 36 System.out.println("顾客2:告知老板要的包子种类和数量"); 37 // 调用wait方法,放弃CPU的执行,进入到waiting状态(无限等待) 38 try { 39 obj.wait(); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 // 唤醒以后执行的代码 44 System.out.println("顾客2:包子作好了,开吃"); 45 System.out.println("---------------------"); 46 } 47 } 48 } 49 }.start(); 50 51 52 // 建立一个老板线程(生产者) 53 new Thread() { 54 @Override 55 public void run() { 56 // 一直作包子 57 while (true){ 58 // 花5秒作包子 59 try { 60 Thread.sleep(5000); 61 } catch (InterruptedException e) { 62 e.printStackTrace(); 63 } 64 // 保证等待和唤醒的线程只能有一个在执行 65 synchronized (obj) { 66 67 System.out.println("老板5秒中以后作好包子,告知顾客能够吃包子了"); 68 // 调用wait方法,放弃CPU的执行,进入到waiting状态(无限等待) 69 // 若是有多个等待线程随机唤醒一个等待 70 //obj.notify(); 71 72 //若是多个等待的线程唤醒全部 73 obj.notifyAll(); 74 } 75 } 76 } 77 }.start(); 78 } 79 }
1 /* 2 资源类:包子类 3 设置包子的属性 4 皮 5 陷 6 包子的状态: 有 true,没有 false 7 */ 8 public class BaoZi { 9 //皮 10 String pi; 11 //陷 12 String xian; 13 //包子的状态: 有 true,没有 false,设置初始值为false没有包子 14 boolean flag = false; 15 16 } 17 18 19 ------------------------------------------------------------------ 20 /* 21 消费者(吃货)类:是一个线程类,能够继承Thread 22 设置线程任务(run):吃包子 23 对包子的状态进行判断 24 false:没有包子 25 吃货调用wait方法进入等待状态 26 true:有包子 27 吃货吃包子 28 吃货吃完包子 29 修改包子的状态为false没有 30 吃货唤醒包子铺线程,生产包子 31 */ 32 public class ChiHuo extends Thread{ 33 //1.须要在成员位置建立一个包子变量 34 private BaoZi bz; 35 36 //2.使用带参数构造方法,为这个包子变量赋值 37 public ChiHuo(BaoZi bz) { 38 this.bz = bz; 39 } 40 //设置线程任务(run):吃包子 41 @Override 42 public void run() { 43 //使用死循环,让吃货一直吃包子 44 while (true){ 45 //必须同时同步技术保证两个线程只能有一个在执行 46 synchronized (bz){ 47 //对包子的状态进行判断 48 if(bz.flag==false){ 49 //吃货调用wait方法进入等待状态 50 try { 51 bz.wait(); 52 } catch (InterruptedException e) { 53 e.printStackTrace(); 54 } 55 } 56 57 //被唤醒以后执行的代码,吃包子 58 System.out.println("吃货正在吃:"+bz.pi+bz.xian+"的包子"); 59 //吃货吃完包子 60 //修改包子的状态为false没有 61 bz.flag = false; 62 //吃货唤醒包子铺线程,生产包子 63 bz.notify(); 64 System.out.println("吃货已经把:"+bz.pi+bz.xian+"的包子吃完了,包子铺开始生产包子"); 65 System.out.println("----------------------------------------------------"); 66 } 67 } 68 } 69 } 70 71 72 ------------------------------------------------------------------- 73 /* 74 生产者(包子铺)类:是一个线程类,能够继承Thread 75 设置线程任务(run):生产包子 76 对包子的状态进行判断 77 true:有包子 78 包子铺调用wait方法进入等待状态 79 false:没有包子 80 包子铺生产包子 81 增长一些趣味性:交替生产两种包子 82 有两种状态(i%2==0) 83 包子铺生产好了包子 84 修改包子的状态为true有 85 唤醒吃货线程,让吃货线程吃包子 86 87 注意: 88 包子铺线程和包子线程关系-->通讯(互斥) 89 必须同时同步技术保证两个线程只能有一个在执行 90 锁对象必须保证惟一,可使用包子对象做为锁对象 91 包子铺类和吃货的类就须要把包子对象做为参数传递进来 92 1.须要在成员位置建立一个包子变量 93 2.使用带参数构造方法,为这个包子变量赋值 94 */ 95 public class BaoZiPu extends Thread{ 96 //1.须要在成员位置建立一个包子变量 97 private BaoZi bz; 98 99 //2.使用带参数构造方法,为这个包子变量赋值 100 public BaoZiPu(BaoZi bz) { 101 this.bz = bz; 102 } 103 104 //设置线程任务(run):生产包子 105 @Override 106 public void run() { 107 //定义一个变量 108 int count = 0; 109 //让包子铺一直生产包子 110 while(true){ 111 //必须同时同步技术保证两个线程只能有一个在执行 112 synchronized (bz){ 113 //对包子的状态进行判断 114 if(bz.flag==true){ 115 //包子铺调用wait方法进入等待状态 116 try { 117 bz.wait(); 118 } catch (InterruptedException e) { 119 e.printStackTrace(); 120 } 121 } 122 123 //被唤醒以后执行,包子铺生产包子 124 //增长一些趣味性:交替生产两种包子 125 if(count%2==0){ 126 //生产 薄皮三鲜馅包子 127 bz.pi = "薄皮"; 128 bz.xian = "三鲜馅"; 129 }else{ 130 //生产 冰皮 牛肉大葱陷 131 bz.pi = "冰皮"; 132 bz.xian = "牛肉大葱陷"; 133 134 } 135 count++; 136 System.out.println("包子铺正在生产:"+bz.pi+bz.xian+"包子"); 137 //生产包子须要3秒钟 138 try { 139 Thread.sleep(3000); 140 } catch (InterruptedException e) { 141 e.printStackTrace(); 142 } 143 //包子铺生产好了包子 144 //修改包子的状态为true有 145 bz.flag = true; 146 //唤醒吃货线程,让吃货线程吃包子 147 bz.notify(); 148 System.out.println("包子铺已经生产好了:"+bz.pi+bz.xian+"包子,吃货能够开始吃了"); 149 } 150 } 151 } 152 } 153 154 155 -------------------------------------------------------------- 156 /* 157 测试类: 158 包含main方法,程序执行的入口,启动程序 159 建立包子对象; 160 建立包子铺线程,开启,生产包子; 161 建立吃货线程,开启,吃包子; 162 */ 163 public class Demo { 164 public static void main(String[] args) { 165 //建立包子对象; 166 BaoZi bz =new BaoZi(); 167 //建立包子铺线程,开启,生产包子; 168 new BaoZiPu(bz).start(); 169 //建立吃货线程,开启,吃包子; 170 new ChiHuo(bz).start(); 171 } 172 }
java.util.concurrent.Executors:线程池工程类,用来生产线程池
Executors类中的静态方法:static ExecutorService newFixedThreadPool(int nThreads):建立一个可重用固定线程数的线程池
参数:int nThreads:建立线程池中包含的线程数量
返回值:ExecutorService接口:返回的是ExecutorService接口的实现类对象,可使用ExecutorService接收
java.util.ExecutorService:线程池接口。用来从线程池中获取线程,调用start方法,执行线程任务
提交一个Runnable任务用于执行:submit(Runnable task)
关闭/销毁 线程池的方法:void shutdown()
使用步骤:
1,使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2,建立一个类,实现Runnable接口,重写run方法,设置线程任务
3,调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程执行run方法
4,调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
1 public class RunableImpl implements Runnable { 2 3 @Override 4 public void run(){ 5 System.out.println(Thread.currentThread().getName()+"建立了一个新的线程并执行"); 6 } 7 } 8 9 10 ------------------------------------------------------------------ 11 import java.util.concurrent.ExecutorService; 12 import java.util.concurrent.Executors; 13 14 public class Demo01ThreadPool { 15 16 public static void main(String[] args) { 17 ExecutorService es = Executors.newFixedThreadPool(2); 18 19 es.submit(new RunableImpl());//pool-1-thread-2建立了一个新的线程并执行 20 es.submit(new RunableImpl());//pool-1-thread-1建立了一个新的线程并执行 21 es.submit(new RunableImpl());//pool-1-thread-1建立了一个新的线程并执行 22 es.submit(new RunableImpl());//pool-1-thread-1建立了一个新的线程并执行 23 24 // 4,调用ExecutorService中的方法shutdown销毁线程池(不建议执行) 25 es.shutdown(); 26 } 27 }
---------------------------