------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
多线程(1)
主要内容:《 多线程概述与例子、实现的方式1、获取线程的名称、优先级、sleep、join、yield、setDeamon、stop_interrupt、实现的方式2、Lock锁、死锁》
1.多线程概述
1.进程:
1).这是"操作系统"中的概念,每个独立运行的程序,就是一个"进程";
2).一个操作系统可以维护多个"进程"的同时运行,同一分配"系统资源";
3).一个操作系统可以维护多个"进程",这叫:支持"多进程"。
多进程"的意义:
A.可以充分利用CPU的资源;
B.为客户的使用提供了很好支持;客户可以同时启动多个软件;
2.线程:
1).一个"进程"内部的一些"代码块",可以以"独立于此进程"的方式单独运行。它可以与此进程
同时竞争"系统资源"。这些由"进程"内部以独立的方式运行的代码,就叫做:线程;
2).意义:
A.可以提高程序的运行效率;
B.可以提高CPU的使用效率;
3.多进程和多线程:
1.多进程:相对操作系统,可以同时运行多个应用程序,这个就叫:多进程;
2.多线程:一个主"进程",可以开出多个"线程"单独运行,这个就叫:多线程;
3.单线程:以前我们做的都是"单线程"程序,只有一条执行路径;
4.并行和并发:
1.并行:是指"在某个时间段内",两个线程同时运行;
2.并发:是指"在某个时间点上",同时发生。尤其是指,多个线程同时访问同一资源时;
1 public class Demo { 2 public static void main(String[] args) { 3 fun1(); 4 fun2();//必须要等待fun1()执行完毕 5 for(int i = 1 ; i < 1000 ; i++){//主进程的for循环必须要等待fun2()执行完毕; 6 System.out.println("i = " + i); 7 } 8 } 9 10 private static void fun1() { 11 for(int j = 1 ; j < 1000 ; j++){ 12 System.out.println("j = " + j); 13 } 14 } 15 16 private static void fun2() { 17 for(int k = 1 ; k < 1000 ; k++){ 18 System.out.println("k = " + k); 19 } 20 } 21 }
2.多线程的例子
1 public class Demo { 2 public static void main(String[] args) { 3 //启动两个线程 4 new MyThread().start(); 5 new MyThread2().start(); 6 7 for(int i = 1 ; i < 1000 ; i++){//主进程的for循环必须要等待fun2()执行完毕; 8 System.out.println("i = " + i); 9 } 10 } 11 }
1 public class MyThread extends Thread { 2 @Override 3 public void run() { 4 for(int j = 1 ; j < 1000 ; j++){ 5 System.out.println("j = " + j); 6 } 7 } 8 }
1 public class MyThread2 extends Thread { 2 @Override 3 public void run() { 4 for(int k = 1 ; k < 1000 ; k++){ 5 System.out.println("k = " + k); 6 } 7 } 8 }
3.多线程实现的方式1
线程:在类库中,被封装为:Thread类
1.Thread类就是:程序中的执行线程;
2.实现方式一:
1).自定义类,并继承自:Thread;
2).重写run()方法;
3).启动:
A.实例化我们的子类对象;
B.调用该对象的start()方法,启动线程
伪代码:
class Thread{
public void run(){
System.out.println("a");
}
public void start(){
run();
}
}
class MyThread extends Thread{
public void run(){
System.out.println("b");
}
}
main(){
MyThread t = new MyThread();
t.start();//启动线程
}
1 public class Demo { 2 public static void main(String[] args) { 3 //启动线程; 4 MyThread t1 = new MyThread(); 5 t1.start();//是启动线程的方法; 6 // t1.run();//不是启动线程,一般的方法调用; 7 8 for(int k = 0;k < 1000 ;k++){ 9 System.out.println("k = " + k); 10 } 11 } 12 }
1 public class MyThread extends Thread { 2 //重写run()方法 3 @Override 4 public void run() { 5 //想在单独的线程中执行的代码,写到这里; 6 for(int i = 0; i < 1000 ;i++){ 7 System.out.println("i = " + i); 8 } 9 } 10 }
4.获取线程的名称
Thread类:
String getName();获取线程名称
默认的线程名称格式:
"Thread-索引":
void setName(String s):设置线程名称
1 public class Demo { 2 public static void main(String[] args) { 3 //可以将一个"线程类",实例化出多个"线程对象",分别启动 4 MyThread t1 = new MyThread();//默认:Thread-0 5 MyThread t2 = new MyThread();//默认:Thread-1 6 MyThread t3 = new MyThread();//默认:Thread-2 7 8 t1.setName("梁朝伟"); 9 t2.setName("苗侨伟"); 10 t3.setName("曾志伟"); 11 12 t1.start(); 13 t2.start(); 14 t3.start(); 15 16 /* for(int i = 0;i < 100 ;i++){ 17 System.out.println(Thread.currentThread().getName() + "-i = " + i); 18 }*/ 19 20 21 22 } 23 }
1 public class MyThread extends Thread { 2 @Override 3 public void run() { 4 for(int i = 0;i < 100; i++){ 5 6 // System.out.println(this.getName() + " i = " + i); 7 8 } 9 System.out.println(this.getName() + "我的优先级是:" + this.getPriority()); 10 } 11 }
5.线程的优先级
设置线程的优先级:
1.setPriority(int p):设置优先级。范围:1-10.否则抛异常;
getPriority():获取优先级;
2.线程的优先级依赖于"操作系统的调度",需要跟操作系统配合,并不能保证优先级高的线程一定会先执行完毕;
所以,我们不要依赖于"线程的优先级"去让某个线程先执行完毕!
3.如果线程内部的业务逻辑很简单,那么线程的优先级将看不出效果;
1 public class Demo { 2 public static void main(String[] args) { 3 MyThread t1 = new MyThread();//Thread-0 4 MyThread t2 = new MyThread();//Thread-1 5 MyThread t3 = new MyThread();//Thread-2 6 MyThread t4 = new MyThread();//Thread-3 7 MyThread t5 = new MyThread();//Thread-4 8 MyThread t6 = new MyThread();//Thread-5 9 10 t1.setPriority(1); 11 t2.setPriority(1); 12 t3.setPriority(1); 13 t4.setPriority(1); 14 t5.setPriority(10); 15 16 t6.setPriority(1);//最高优先级; 17 18 t1.start(); 19 t2.start(); 20 t3.start(); 21 t4.start(); 22 t5.start(); 23 24 t6.start(); 25 } 26 }
1 public class MyThread extends Thread { 2 @Override 3 public void run() { 4 double sum = 0; 5 for (int i = 1; i <= 10; i++) { 6 for (int j = 1; j <= 2000; j++) { 7 sum += (Math.E + Math.PI) / i; 8 if (j % 200000 == 0) { 9 // 让当前线程退回到"就绪"状态,让其它线程有机会执行; 10 Thread.yield(); 11 } 12 } 13 System.out.println(this.getName() + " i= " + i + " 我的优先级:" 14 + this.getPriority()); 15 16 } 17 18 System.out.println(this.getName() + " 执行完毕!"); 19 } 20 }
6.线程休眠_sleep
线程休眠
public static void sleep(long millis):参数单位:毫秒
1 public class Demo { 2 public static void main(String[] args) { 3 //在主进程,让主进程休眠; 4 /*for(int i = 0 ;i < 10 ;i++){ 5 try { 6 Thread.sleep(1000); 7 } catch (InterruptedException e){ 8 e.printStackTrace(); 9 } 10 Date date = new Date(); 11 SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); 12 String str = sdf.format(date); 13 System.out.println(str); 14 15 }*/ 16 17 new MyThread().start(); 18 } 19 }
1 public class MyThread extends Thread { 2 @Override 3 public void run() { 4 for(int i = 0 ;i < 10 ;i++){ 5 try { 6 Thread.sleep(1000); 7 } catch (InterruptedException e){ 8 e.printStackTrace(); 9 } 10 Date date = new Date(); 11 SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); 12 String str = sdf.format(date); 13 System.out.println(str); 14 15 } 16 } 17 }
7.线程加入_join
1 public class Demo { 2 public static void main(String[] args) throws InterruptedException { 3 MyThread t1 = new MyThread(); 4 MyThread t2 = new MyThread(); 5 MyThread t3 = new MyThread(); 6 7 t1.setName("兵1"); 8 t2.setName("兵2"); 9 t3.setName("兵3"); 10 11 t1.start(); 12 //让兵1先上,你先杀,完事我再上 13 /*try { 14 t1.join(); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 }*/ 18 t2.start(); 19 20 t2.join(); 21 t3.start(); 22 23 } 24 } 25 public class MyThread extends Thread { 26 @Override 27 public void run() { 28 for(int i = 0;i < 100; i++){ 29 System.out.println(this.getName() + " 正在杀怪 " + i); 30 } 31 } 32 }
8.线程礼让_yield
线程的礼让:
yield():让当前线程退回到"就绪状态",同其它线程一样,一起等待操作系统分配资源,
很可能刚刚yield的线程会立即被重新分配CPU执行;
调用yield()不保证线程会最后执行完毕;
1 public class Demo { 2 public static void main(String[] args) { 3 MyThread t1 = new MyThread(); 4 MyThread t2 = new MyThread(); 5 MyThread t3 = new MyThread(); 6 7 t1.setName("章子怡"); 8 t2.setName("汪峰"); 9 t3.setName("撒贝宁"); 10 11 t1.start(); 12 t2.start(); 13 t3.start(); 14 15 16 } 17 } 18 public class MyThread extends Thread{ 19 @Override 20 public void run() { 21 for(int i = 0;i < 1000; i++){ 22 System.out.println(this.getName() + " i = " + i); 23 if(this.getName().equals("汪峰")){ 24 //礼让一下 25 Thread.yield(); 26 } 27 } 28 System.out.println(this.getName() + "--执行完毕!"); 29 30 } 31 }
9.后台线程_setDeamon
当主方法结束时,主线程开出的线程仍然继续运行,直到运行完毕;
这个就是:非守护线程;开出的线程不会守护我们的主进程;
可以设置线程为:守护线程:setDeamon(true):它将守护主进程,如果主进程结束,守护线程会跟着结束,
(但不会立即结束,会有个小缓冲);
默认情况下,开出的线程都是:非守护线程;
1 public class Demo { 2 public static void main(String[] args) { 3 MyThread t = new MyThread(); 4 t.setName("兵1"); 5 t.setDaemon(true);//设为守护线程; 6 t.start(); 7 8 for(int i = 0;i < 10;i++){ 9 System.out.println("将军杀敌: i = " + i); 10 } 11 System.out.println("将军杀完,收兵!!回家....."); 12 } 13 } 14 public class MyThread extends Thread{ 15 @Override 16 public void run() { 17 for(int i = 0;i < 100;i++){ 18 System.out.println(this.getName() + " 正在杀敌 i = " + i); 19 } 20 } 21 }
10.线程的中断_stop_interrupt
在主进程中可以结束某个线程:
方式一:stop();(已过时)
方式二:interrupt()(建议使用):当线程处于:Object--wait()及Thread的--join()以及Thread--sleep()受阻时,
此方法调用将会导致上述三个方法阻塞时的异常抛出;
1 public class Demo { 2 public static void main(String[] args) { 3 MyThread t = new MyThread(); 4 t.start(); 5 6 //让主进程等待3秒 7 System.out.println("主进程等待3秒......"); 8 try { 9 Thread.sleep(1000 * 3); 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 } 13 System.out.println("干掉线程:"); 14 // t.stop(); 15 16 t.interrupt(); 17 18 19 } 20 } 21 public class MyThread extends Thread{ 22 @Override 23 public void run() { 24 for(int i = 0;i < 100; i++){ 25 try { 26 Thread.sleep(200);//Thread--sleep()阻塞(Object--wait();Thread-->join()) 27 } catch (InterruptedException e) { 28 //当线程外部对此线程调用interrupt()方法时,会导致这里产生异常。 29 //可以在这里,结束当前线程; 30 31 // e.printStackTrace(); 32 System.out.println("外部调用了我的interrupt()方法,我被终止了,拜拜......"); 33 break; 34 } 35 System.out.println(this.getName() + " i= " + i); 36 37 } 38 } 39 }
11.多线程程序实现的方式2
多线程程序实现的方式2:
1.实现Runnable接口;
2.重写run()方法;
3.启动线程:
1).实例化我们自定义类的对象;
2).实例化一个Thread对象,并用自定义对象做参数;
3).调用Thread对象的start()
1 public class Demo { 2 public static void main(String[] args) { 3 MyRunnable myRun = new MyRunnable(); 4 new Thread(myRun).start(); 5 for(int k = 0;k < 100 ; k++){ 6 System.out.println(" k = " + k); 7 } 8 } 9 } 10 public class MyRunnable implements Runnable { 11 12 @Override 13 public void run() { 14 for(int i = 0;i < 1000; i++){ 15 System.out.println(Thread.currentThread().getName() + " i = " + i); 16 } 17 } 18 19 }
12.继承Thread类的方式卖电影票案例
三个窗口共同享有同一个对象的引用,并且获取同一对象内的数据,此对象,就面临着"并发访问的问题"。
这种并发性访问会导致数据的不一致性;
1 public class Demo { 2 public static void main(String[] args) { 3 //1.实例化一个票池 4 TicketPool pool = new TicketPool(); 5 //2.实例化三个窗口线程 6 MyThread t1 = new MyThread(pool); 7 MyThread t2 = new MyThread(pool); 8 MyThread t3 = new MyThread(pool); 9 10 t1.setName("窗口1"); 11 t2.setName("窗口2"); 12 t3.setName("窗口3"); 13 14 t1.start(); 15 t2.start(); 16 t3.start(); 17 18 } 19 } 20 import java.util.TreeSet; 21 22 public class MyThread extends Thread { 23 //每个线程内部,都要持有一个TicketPool对象,表示几个线程共同抢一个票池的票 24 private TicketPool tp; 25 26 //一个集合,存已抢到的票。为了把票排序,使用TreeSet 27 TreeSet<Integer> set = new TreeSet<>(); 28 //通过构造方法赋值 29 public MyThread(TicketPool pool){ 30 this.tp = pool; 31 } 32 @Override 33 public void run() { 34 while(true){ 35 int ticket = this.tp.getTicket(); 36 if(ticket > 0){ 37 // System.out.println(this.getName() + " 抢到票:" + ticket); 38 set.add(ticket); 39 }else{ 40 // System.out.println("没票了,不抢了!" + this.getName() + "结束!" ); 41 break; 42 } 43 } 44 //总结一下,一共抢到多少张票 45 System.out.println(this.getName() + " 一共抢到:" + this.set.size() + " 张票。明细:" + set); 46 } 47 } 48 public class TicketPool { 49 private int tickets = 100; 50 51 //公有方法,供外部调用:获取一张票 52 public int getTicket(){//窗口1,窗口2 53 54 if(tickets > 0 ){ 55 //窗口1,窗口2 56 return tickets--;//窗口1--取走:100,窗口2--取走:100 :tickets-- 57 }else{ 58 return 0; 59 } 60 61 62 } 63 64 }
13.使用同步解决并发性问题
什么情况下会产生并发性问题:
1.是否是多线程环境:是
2.是否有共享数据:是:TicketPool
3.是否有多条语句操作共享数据:即使:ticket--;这样的一句话都不具有"原子性"
解决并发性问题,在被多线程访问的代码上添加一个关键字:synchronized
语法格式:
synchronized(被锁的对象){
//可能被多个线程并发访问的代码;
}
被锁的对象:指在这个对象上加锁,也就意味着,当一个线程访问这段代码时,对于"被锁对象"中的其它的
synchronized的代码块或方法,不允许被其它线程访问;
1 public class Demo { 2 public static void main(String[] args) { 3 // 1.实例化一个票池 4 TicketPool pool = new TicketPool(); 5 // 2.实例化三个窗口线程 6 MyThread t1 = new MyThread(pool); 7 MyThread t2 = new MyThread(pool); 8 MyThread t3 = new MyThread(pool); 9 10 t1.setName("窗口1"); 11 t2.setName("窗口2"); 12 t3.setName("窗口3"); 13 14 t1.start(); 15 t2.start(); 16 t3.start(); 17 } 18 } 19 import java.util.TreeSet; 20 21 public class MyThread extends Thread { 22 //每个线程内部,都要持有一个TicketPool对象,表示几个线程共同抢一个票池的票 23 private TicketPool tp; 24 25 //一个集合,存已抢到的票。为了把票排序,使用TreeSet 26 TreeSet<Integer> set = new TreeSet<>(); 27 //通过构造方法赋值 28 public MyThread(TicketPool pool){ 29 this.tp = pool; 30 } 31 @Override 32 public void run() { 33 while(true){ 34 int ticket = this.tp.getTicket(); 35 if(ticket > 0){ 36 // System.out.println(this.getName() + " 抢到票:" + ticket); 37 set.add(ticket); 38 }else{ 39 // System.out.println("没票了,不抢了!" + this.getName() + "结束!" ); 40 break; 41 } 42 } 43 //总结一下,一共抢到多少张票 44 System.out.println(this.getName() + " 一共抢到:" + this.set.size() + " 张票。明细:" + set); 45 } 46 } 47 public class TicketPool { 48 private int tickets = 100; 49 50 //公有方法,供外部调用:获取一张票 51 public int getTicket(){//窗口1,窗口2 52 //同步代码块; 53 synchronized (this) {//里面的代码,可能会被多线程访问,加锁; 54 if(tickets > 0 ){ 55 //窗口1,窗口2 56 return tickets--;//窗口1--取走:100,窗口2--取走:100 :tickets-- 57 }else{ 58 return 0; 59 } 60 } 61 62 } 63 64 }
14.JDK5的Lock锁
JDK5之后,提供一种的加锁的方式:Lock(接口):
java.util.concurrent.locks.Lock(接口):
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
参考帮助文档:
使用方式:
Lock l = new ReentrantLock();
l.lock();//加锁
try {
//被同步的代码;
} finally {
l.unlock();//释放锁
1 public class Demo { 2 public static void main(String[] args) { 3 //1.实例化一个票池 4 TicketsPool pool = new TicketsPool(); 5 //2.实例化三个线程,表示三个售票窗口 6 MyThread t1 = new MyThread(pool); 7 MyThread t2 = new MyThread(pool); 8 MyThread t3 = new MyThread(pool); 9 10 t1.setName("窗口1"); 11 t2.setName("窗口2"); 12 t3.setName("窗口3"); 13 14 //3.启动三个窗口 15 t1.start(); 16 t2.start(); 17 t3.start(); 18 } 19 }
1 import java.util.TreeSet; 2 3 /* 4 * 此线程代表"窗口"售票线程,此类会被实例化三个对象,表示三个售票窗口; 5 */ 6 public class MyThread extends Thread { 7 // 由于每个对象都需要使用同一个票池,所以,都要包含同一个TicketsPool对象 8 private TicketsPool pool; 9 // 存已抢到的票的集合.每个售票窗口都有一个独立的集合; 10 TreeSet<Integer> set = new TreeSet<>(); 11 12 public MyThread(TicketsPool pool) { 13 this.pool = pool; 14 } 15 16 @Override 17 public void run() { 18 while (true) { 19 int ticket = this.pool.getTicket(); 20 if (ticket > 0) { 21 set.add(ticket); 22 } else { 23 break; 24 } 25 } 26 System.out.println(this.getName() + " 共抢到:" + this.set.size() + " 张票:" 27 + set); 28 } 29 }
1 import java.util.concurrent.locks.Lock; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 /* 5 * JDK5的Lock锁: 6 * 7 * 1.在类的"成员变量"处,实例化一个Lock的子类对象;目的是使此类的所有方法都可以使用此锁; 8 * 2.在需要加锁的地方按照建议的格式使用: 9 * l.lock();//加锁 10 * try{ 11 * 12 * }finally{ 13 * l.unlock();//释放锁 14 * } 15 */ 16 public class TicketsPool { 17 private int tickets = 100; 18 private Lock lock = new ReentrantLock(); 19 //之前加锁的方式 20 /* 21 public synchronized int getTicket(){ 22 if(this.tickets > 0){ 23 return this.tickets--; 24 }else{ 25 return 0; 26 } 27 } 28 */ 29 //Lock锁 30 public int getTicket(){ 31 lock.lock(); 32 try{ 33 if(this.tickets > 0){ 34 return this.tickets--; 35 }else{ 36 return 0; 37 } 38 }finally{ 39 lock.unlock(); 40 } 41 } 42 }
15.死锁的现象
实例
1 public class Demo { 2 public static void main(String[] args) { 3 //1.先实例化两个共享资源 4 MyClass1 c1 = new MyClass1(); 5 MyClass2 c2 = new MyClass2(); 6 7 //2.实例化两个线程 8 MyThread1 t1 = new MyThread1(c1,c2); 9 MyThread2 t2 = new MyThread2(c1,c2); 10 11 //3.启动 12 t1.start(); 13 t2.start(); 14 15 } 16 } 17 public class MyClass1 { 18 public synchronized void show1(){ 19 System.out.println("MyClass1-->show1()"); 20 } 21 } 22 public class MyClass2 { 23 public synchronized void show2(){ 24 System.out.println("MyClass2-->show2()"); 25 } 26 } 27 public class MyThread1 extends Thread { 28 private MyClass1 c1; 29 private MyClass2 c2; 30 31 public MyThread1(MyClass1 c1 ,MyClass2 c2){ 32 this.c1 = c1; 33 this.c2 = c2; 34 } 35 36 @Override 37 public void run() { 38 synchronized (c1) { 39 try { 40 Thread.sleep(1000); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 System.out.println("线程1即将访问c2的show2()......"); 45 c2.show2(); 46 System.out.println("线程1访问c2的show2()结束!"); 47 } 48 } 49 } 50 public class MyThread2 extends Thread{ 51 private MyClass1 c1; 52 private MyClass2 c2; 53 54 public MyThread2(MyClass1 c1 ,MyClass2 c2){ 55 this.c1 = c1; 56 this.c2 = c2; 57 } 58 public void run() { 59 synchronized (c2) { 60 try { 61 Thread.sleep(1000); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 System.out.println("线程2即将访问c1的show1()......"); 66 c1.show1(); 67 System.out.println("线程2访问c1的show1()结束!"); 68 } 69 } 70 }
16.生产和消费者(案例)
包子铺卖包子的例子:
1 import java.util.ArrayList; 2 3 public class Demo { 4 public static void main(String[] args) { 5 //1.实例化一个包子铺 6 BaoZiPu bzp = new BaoZiPu(); 7 //2.实例化生产者和消费者线程 8 SetThread setThread = new SetThread(bzp); 9 GetThread getThread = new GetThread(bzp); 10 11 //3.启动线程 12 setThread.start(); 13 getThread.start(); 14 15 } 16 } 17 18 19 import java.util.ArrayList; 20 21 public class BaoZiPu { 22 private ArrayList<String> baoZiList = new ArrayList<>(); 23 24 //此方法供"生产者线程"访问 25 public synchronized void setBaoZi(String s){ 26 this.baoZiList.add(s); 27 } 28 //此方法供"消费方线程"调用; 29 public synchronized String getBaoZi(){ 30 if(this.baoZiList.size() > 0){//取包子 31 String s = this.baoZiList.get(0);//取集合中的第一个元素 32 this.baoZiList.remove(0);//将第一个元素删除。因为包子被取走了。 33 return s;//将包子返回给消费者 34 35 }else{ 36 return null; 37 } 38 } 39 } 40 41 public class GetThread extends Thread { 42 private BaoZiPu bzp ; 43 public GetThread(BaoZiPu bzp){ 44 this.bzp = bzp; 45 } 46 47 @Override 48 public void run() { 49 while(true){ 50 String s = this.bzp.getBaoZi(); 51 System.out.println("我兴高采烈的买到了一个:" + s); 52 } 53 } 54 } 55 56 57 public class SetThread extends Thread { 58 private BaoZiPu bzp; 59 public SetThread(BaoZiPu bzp){ 60 this.bzp = bzp; 61 } 62 @Override 63 public void run() { 64 while(true){ 65 //为了效果明显,让生产方慢点生产 66 try { 67 Thread.sleep(1); 68 } catch (InterruptedException e) { 69 e.printStackTrace(); 70 } 71 this.bzp.setBaoZi("包子"); 72 } 73 } 74 }
17.使用等待和唤醒更改_生产和消费者
此例是"单生产"和"单消费",不适用于"多生产"和"多消费";
注意:
1.让当前访问的线程等待:Object-->wait();
唤醒当前等待的线程:Object-->notify()
notifyAll();
上述三个方法一定要在同步的方法中使用;
1 public class Demo { 2 public static void main(String[] args) { 3 //1.实例化一个包子铺 4 BaoZiPu bzp = new BaoZiPu(); 5 //2.实例化生产者和消费者线程 6 SetThread setThread = new SetThread(bzp); 7 GetThread getThread = new GetThread(bzp); 8 9 //3.启动线程 10 setThread.start(); 11 getThread.start(); 12 13 } 14 } 15 16 import java.util.ArrayList; 17 18 public class BaoZiPu { 19 private ArrayList<String> baoZiList = new ArrayList<>(); 20 21 // 此方法供"生产者线程"访问 22 public synchronized void setBaoZi(String s) { 23 this.baoZiList.add(s); 24 //唤醒所有等待的线程 25 notifyAll();//使用notify()也可以; 26 } 27 28 // 此方法供"消费方线程"调用; 29 public synchronized String getBaoZi() { 30 /* 31 * if(this.baoZiList.size() > 0){//取包子 String s = 32 * this.baoZiList.get(0);//取集合中的第一个元素 33 * this.baoZiList.remove(0);//将第一个元素删除。因为包子被取走了。 return s;//将包子返回给消费者 34 * 35 * }else{ return null; } 36 */ 37 if (this.baoZiList.size() == 0) { 38 try { 39 this.wait();//让当前访问的线程等待...... 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 } 44 45 //为用户取包子 46 String s = this.baoZiList.get(0); 47 this.baoZiList.remove(0); 48 return s; 49 } 50 } 51 52 public class GetThread extends Thread { 53 private BaoZiPu bzp ; 54 public GetThread(BaoZiPu bzp){ 55 this.bzp = bzp; 56 } 57 58 @Override 59 public void run() { 60 while(true){ 61 String s = this.bzp.getBaoZi(); 62 System.out.println("我兴高采烈的买到了一个:" + s); 63 } 64 } 65 } 66 67 public class SetThread extends Thread { 68 private BaoZiPu bzp; 69 public SetThread(BaoZiPu bzp){ 70 this.bzp = bzp; 71 } 72 @Override 73 public void run() { 74 while(true){ 75 //为了效果明显,让生产方慢点生产 76 try { 77 Thread.sleep(1); 78 } catch (InterruptedException e) { 79 e.printStackTrace(); 80 } 81 this.bzp.setBaoZi("包子"); 82 } 83 } 84 }