一、概念:
1.程序、进程、线程
程序:一段静态代码块;
进程:正在运行的程序;系统会为每个进程分配内存空间
线程:程序内部的一条执行路径,若一个进程同一时间并行执行多个线程,就是支持多线程;
一个进程中的多个线程共享同一个内存单元/内存地址空间(他们从同一堆中分配对象,可以访问相同的变量和对象(因为线程之间共享方法去和堆),就使得线程通信更简便高效,注意线程之间共享方法去和堆,其他不共享),但是多个线程共享的系统资源可能会带来安全隐患;
2.单核CPU和多核cup;
单核CPU的多线程:一个假的多线程,在一个单位时间内,也只能执行一个线程任务。例如收费站收费的工作人员,虽然多个车道,但是只有一个收费人员,当有车不想缴费时,工作人员挂起该车辆,同时给其他车辆做收费;这么个道理;
多核CPU:才能更好的发挥多线程的效率; 一个java应用程序,至少有3个线程:main() 主线程和gc()和异常;
使用多线程的优点;以单核为例;
如果 需要c盘文件 复制到 D盘 e盘文件复制到 f盘 方式一、先复制c->d 然后 e->f 方式二:c->d 同时
e->f 此时 方式一更快;
如果是多核cpu,那么方式二更快;因为单核CPU执行多个线程,则需要切换不同的线程需要花费时间;二多核cpu呢,他不需要切换线程,则方式二就更快了;
3.什么时候需要用到多线程:
a.程序需要同时执行两个或者多个任务, b..程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等; c.需要一些后台运行的程序
4.并行 和 并发 并行:
多个CPU同时执行多个任务,多个人做不同的事情; 并发:一个CPU同时指向多个任务;
二、创建多线程的4中方式
1、继承Thread方式:
a1.创建一个继承Thread的子类,
a2.重写run方法;
a3.创建Thread子类的对象,通过此对象 调用start方法;start的作用:使得线程begin,调用当前线程的run方法;
例1:
1 ///1.创建一个继承 Thread的子类; 2 class SubThread1 extends Thread { 3 // 2.重写Thread类的run方法,方法内实现此子线程要完成的功能 4 public void run() { 5 for (int i = 1; i < 101; i++) { 6 try { 7 if(i%2 ==0) { 8 System.out.println(Thread.currentThread().getName() + ":" + i); 9 } 10 Thread.currentThread().sleep(1000);//休息一秒钟 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 16 } 17 } 18 } 19 20 public class Day18CreateThreadByExtendsThread13 { 21 public static void main(String[] args) { 22 23 24 // 25 //3.创建一个子类的对象 26 SubThread1 st = new SubThread1(); 27 //4.调用线程的start方法,启动此线程;再调用run方法(一个线程只能执行一次start(),可以创建两个对象来分别执行start方法!) 28 st.start();//start 使得子线程begin 然后执行run方法; 29 //再启动一个线程,则需要重新创建一个子类对象,再执行start方法。 30 SubThread1 st1= new SubThread1(); 31 st1.start(); 32 System.out.println(Thread.currentThread().getName()+":hello!");//主线程内打印“hello” 33 } 34 35 36 }
2、实现runnable 接口方式
b1.创建一个实现Runnable接口的类,
b2.重写run方法;
...
例2:
1 public class Day18CreateThreadByRunnableInterface13 { 2 3 public static void main(String[] args) { 4 //3创建一个Runnable接口实现类的对象 5 PrintNum2 pn = new PrintNum2(); 6 //要想启动一个多线程,必须调用start 7 //4将实现类对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程 8 Thread t1 = new Thread(pn); 9 //启动线程,执行Thread对象生成时构造器形参的对象 10 //5调动start,启动线程执行run方法 11 t1.start(); 12 //再创建一个线程 13 Thread t2 = new Thread(pn); 14 t2.start(); 15 } 16 } 17 18 //1创建一个实现了Runnable接口类 19 class PrintNum2 implements Runnable { 20 //2实现接口的抽象方法 21 public void run() { 22 for (int i = 1; i < 101; i++) { 23 System.out.println(Thread.currentThread().getName() + ":" + i); 24 } 25 } 26 }
3.实现callable接口方式
c1.创建一个实现Runnable接口的类,
c2.重写call方法;
c3.创建 callable接口实现类的对象
c4.将callable 接口实现类的对象 放入 Future中
c5.将futuretask 放入 Thread构造器中,,创建thread对象,并启动线程
c6.获取返回值;
如何理解理解实现callable接口方式创建多线程比实现runnable接口创建对现场方式强大?
1.call方法可以有返回值;
2.call方法可以抛出异常,别外面的操作捕获;
3.支持泛型
例3
1 public class Day19CreatThreadByCallable18 { 2 public static void main(String[] args) { 3 //3创建 callable接口实现类的对象 4 NumThread uumThread = new NumThread(); 5 //4.将callable 接口实现类的对象 放入 Future中 6 FutureTask futureTask = new FutureTask(uumThread); 7 //5.将futuretask 放入 Thread构造器中,,创建thread对象,并启动线程 8 Thread thread = new Thread(futureTask); 9 thread.start(); 10 try { 11 //6.获取返回值; 12 //get方法的返回值,即为futuretask 构造器参数callable实现类重写的call()的返回值 13 int sum = (int)futureTask.get(); 14 System.out.println(sum); 15 } catch (InterruptedException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } catch (ExecutionException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 } 23 } 24 //1.创建一个实现callable的实现了 25 class NumThread implements Callable{ 26 //遍历100以内的偶数,并且返回和 27 //2.重写 Call方法,将此线程需要执行的操作声明在call()中,同事call()方法可以有返回值 28 @Override 29 public Object call() throws Exception { 30 int sum = 0; 31 for(int i=1;i<=100;i++) { 32 if(i%2 == 0) { 33 sum = sum+i; 34 } 35 } 36 return sum; 37 } 38 }
4.使用线程池方式创建多线程;
d1.提供指定数量线程的线程池
d2.创建实现runnable接口的实现类 or 创建实现callable接口的实现类;
d3..执行指定线程的操作;
d4.关闭线程池
使用线程池:
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大;
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具;
好处:
提前响应速度、降低资源消耗、便于线程管理(
corePoolSize :核心池的大小;
MaximumPoolSize:最大线程数;
keepAliveTime:线程没有任务时最多保持多长后会终止;
);
例4:
1 public class Day19CreatThreadByPool19 { 2 public static void main(String[] args) { 3 //1.提供指定数量线程的线程池 4 ExecutorService service = Executors.newFixedThreadPool(10); 5 //其他:设置线程池的属性: 6 ThreadPoolExecutor service1 = (ThreadPoolExecutor)service; 7 // service1.setCorePoolSize(15); 8 //2.创建实现runnable接口的实现类 or 创建实现callable接口的实现类 9 NumberThread1 numThread1 = new NumberThread1(); 10 NumberThread2 numberThread2 = new NumberThread2(); 11 FutureTask futureTask = new FutureTask(numberThread2); 12 //3.执行指定线程的操作; 13 service.execute(numThread1);//适合使用runnable 14 service.submit(futureTask);//适合使用 callable方法 15 //4.关闭连接池 16 service.shutdown();//关闭连接池 17 } 18 } 19 class NumberThread1 implements Runnable{ 20 @Override 21 public void run() { 22 int sum = 0; 23 for(int i = 1;i<=100;i++) { 24 if(i%2 == 0) { 25 sum = sum +i; 26 System.out.println(Thread.currentThread().getName()+":"+i); 27 } 28 29 } 30 System.out.println("runnabl sum:"+sum); 31 32 } 33 34 } 35 36 class NumberThread2 implements Callable{ 37 @Override 38 public Object call() throws Exception { 39 int sum = 0; 40 for(int i = 1;i<=100;i++) { 41 if(i%2 == 0) { 42 sum = sum +i; 43 44 }else { 45 System.out.println(Thread.currentThread().getName()+":"+i); 46 } 47 48 } 49 System.out.println("callable sum:"+sum); 50 return sum; 51 } 52 53 }
三、Thread的常用方法
1.start()方法:1.begin 线程 ,2.调用当前线程的run方法;
2.run()方法:需要重写Thread类中的此方法,将创建的线程哟啊执行的操作声明在此方法中
3.currentThread();返回一个Threan 当前执行代码的线程;
4.getName();获取当前线程的名称
5.setName():设置当前线程的名字 注意在 start前面
6.yield():释放当前cpu的执行权
7.join();在线程a中调用线程b的join(),此时a就进入了阻塞状态,知道b完全指向完以后,a才结束阻塞状态;
8.stop();强制结束当前线程;已过期
9.sleep();让当前线程“睡眠”指定的millitime毫秒,在指定的millitime毫秒时间内,当前线程是阻塞状态;
10.isAlive():判断当前线程是否存活;当线程执行完成后,则线程死亡;
1 public class Day18ThreadMethods { 2 3 public static void main(String[] args) { 4 ThreadTest1 test1 = new ThreadTest1(); 5 ThreadMethods2 test2 = new ThreadMethods2("线程2"); 6 test1.setName("线程1"); 7 test1.start(); 8 test2.start(); 9 for(int i=0;i<100;i++) { 10 if(i%10 == 0) { 11 System.out.println(Thread.currentThread().getName()+":"+i); 12 } 13 if(i==20) { 14 try { 15 test1.join(); 16 } catch (InterruptedException e) { 17 // TODO Auto-generated catch block 18 e.printStackTrace(); 19 } 20 } 21 System.out.println(test1.isAlive()); 22 23 } 24 25 } 26 } 27 28 class ThreadMethods1 extends Thread{ 29 @Override 30 public void run() { 31 try { 32 sleep(1000);//阻塞一秒;sleep在此时只能使用 try catch,why?因为run的父类方法没有抛异常,所以子类就不能抛异常 33 } catch (InterruptedException e) { 34 // TODO Auto-generated catch block 35 e.printStackTrace(); 36 }//睡眠1秒 37 // TODO Auto-generated method stub 38 for(int i=0;i<100;i++) { 39 if(i%2 == 0) { 40 System.out.println(Thread.currentThread().getName()+":"+i); 41 } 42 if(i%20 == 0) { 43 yield(); 44 } 45 46 } 47 } 48 public ThreadMethods1(String name) { 49 super(name); 50 } 51 } 52 class ThreadMethods2 extends Thread{ 53 @Override 54 public void run() { 55 // TODO Auto-generated method stub 56 for(int i=0;i<100;i++) { 57 if(i%2 != 0) { 58 System.out.println(Thread.currentThread().getName()+":"+i); 59 } 60 61 } 62 } 63 public ThreadMethods2(String name) { 64 super(name); 65 } 66 }
四、线程的调度
时间片
抢占式:高优先级的线程抢占cpu
java的调度方法:
同优先级的组成先进先出队列(先到先服务),使用时间片服务;
对高优先级,使用优先调度的抢占式策略;
线程的优先等级:
MAX_PRIORITY:10
MIN_PRIORITY:1;
NORM_PRIORITY:5
方法:
getPriority():返回线程的优先等级值
setPriority():设置线程的优先等级值;
说明:高优先级的线程要抢占低优先级CPU执行次序,但是只是从概率上讲,高概率上执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行
1 public class Day18Priority17 { 2 public static void main(String[] args) { 3 System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());//获取主线程的优先等级值 5 4 ThreadPriority test = new ThreadPriority(); 5 test.setPriority(10);//设置分线程的优先级 6 test.start(); 7 for(int i=0;i<100;i++) { 8 9 System.out.println(Thread.currentThread().getName()+":"+i); 10 } 11 } 12 } 13 14 class ThreadPriority extends Thread{ 15 @Override 16 public void run() { 17 for(int i=0;i<100;i++) { 18 System.out.println(Thread.currentThread().getName()+":"+i); 19 } 20 super.run(); 21 }
五、线程安全
1.线程安全 产生背景:
例5场景是,3个窗口同时卖火车票(共100张);出现的问题是:不同窗口可能出现相同的票号;可能会卖出错票(即出现了-1号票等情况):
原因:当某线程操作车票时,操作尚未完成,其他线程参与进来,也操作车票导致;
解决:当一个线程在操作ticket,其他线程不能参与进来,知道该线程操作完了,其他线程才可以开始操作ticket,这种情况即使该线程出现阻塞,也不能被改变;
在java中,我们通过同步机制来解决线程的安全问题;
例5:
1 / 2 例子:创建3个买票窗口卖票 3 @Description 4 @author lixiuming 5 @version 6 @date 2020年12月22日下午7:43:25 7 创建线程的两种方式的比较: 8 开发中优先选择实现runnable接口; 9 原因:实现方式没有类的单继承的局限性 10 实现方式更实现多个线程的有共享数据的情况 11 联系:Thread也实现了runnable接口 12 两种方式都需要重写run方法,将逻辑代码都写在run()方法中; 13 java 程序至少有3个线程;一个是主线程 一个是gc()垃圾回收线程,异常处理线程; 14 / 15 public class Day18WindowTest18 { 16 public static void main(String[] args) { 17 // Window window1 = new Window(); 18 // Window window2 = new Window(); 19 // Window window3 = new Window(); 20 // window1.start(); 21 // window2.start(); 22 // window3.start(); 23 WindowbyRunnable windowbyRunnable = new WindowbyRunnable(); 24 Thread wr1 = new Thread(windowbyRunnable); 25 Thread wr2 = new Thread(windowbyRunnable); 26 Thread wr3 = new Thread(windowbyRunnable); 27 wr1.start(); 28 wr2.start(); 29 wr3.start(); 30 31 32 } 33 } 34 35 36 //继承Thread 方式实现 窗口卖票 37 class Window extends Thread{ 38 private static int ticket = 100; 39 @Override 40 public void run() { 41 // TODO Auto-generated method stub 42 while(true) { 43 if(ticket>0) { 44 System.out.println(Thread.currentThread().getName()+":"+ticket); 45 ticket--; 46 }else { 47 break; 48 } 49 } 50 }} 51 52 //使用runnable接口方式实现窗口 53 class WindowbyRunnable implements Runnable{ 54 private int ticket = 100; 55 @Override 56 public void run(){ 57 // TODO Auto-generated method stub 58 while(true) { 59 if(ticket>0) { 60 System.out.println(Thread.currentThread().getName()+":"+ticket); 61 ticket--; 62 }else { 63 break; 64 } 65 } 66 67 } 68 }
2.同步块|同步方法 解决线程安全:
方式一:同步代码块 1.synchronized(同步监视器){ 需要被同步的代码 }; 说明:
1.操作共享数据的代码就是 需要被同步的代码;不能包含代码多了,也不能包含少了;
2.共享数据:多个线程操作的变量;比如本问题的ticket
3.同步监视器:俗称锁;任何类的对象都可以充当锁;(一定保证所有线程共用一个锁,也就是说作为锁的类只能保证只被创建过一次)
补充:在实现runnable接口创建多线程方式中,可以考虑使用this 来充当同步监视器;
说明,在继承Thread创建多线程方式中,慎用this充当,可以考虑当前类来充当同步监视器;
方式二:同步方法 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的;
说明:
使用同步方法来解决实现runnable接口的线程安全问题;
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们现实的声明。
2.非静态的同步方法:同步监视器是this;
静态的同步方法:同步监视器是当前类本身;
例6(同步代码块方式)
1 public class Day19ThreadSafe6 { 2 public static void main(String[] args) { 3 WindowOfSaleTicket windowOfSaleTicket = new WindowOfSaleTicket(); 4 Thread thread1 = new Thread(windowOfSaleTicket); 5 Thread thread2 = new Thread(windowOfSaleTicket); 6 Thread thread3 = new Thread(windowOfSaleTicket); 7 thread1.start(); 8 thread2.start(); 9 thread3.start(); 10 } 11 } 12 13 class WindowOfSaleTicket implements Runnable { 14 private int ticketcount = 100; 15 Object obj = new Object(); 16 17 @Override 18 public void run() { 19 20 while (true) { 21 synchronized (obj) {// obj 可以用 this来充当,this代表当前对象,即windowOfSaleTicket 22 if (ticketcount > 0) { 23 try { 24 Thread.sleep(100); 25 System.out.println(Thread.currentThread().getName() + ":" + ticketcount); 26 ticketcount--; 27 } catch (InterruptedException e) { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 } 31 } else { 32 33 break; 34 } 35 36 } 37 } 38 39 } 40 }
例7(同步方法:)
1 public class Day19ThreadSafe8 { 2 public static void main(String[] args) { 3 WindowOfSaleTicket1 windowOfSaleTicket = new WindowOfSaleTicket1(); 4 Thread thread1 = new Thread(windowOfSaleTicket); 5 Thread thread2 = new Thread(windowOfSaleTicket); 6 Thread thread3 = new Thread(windowOfSaleTicket); 7 thread1.start(); 8 thread2.start(); 9 thread3.start(); 10 } 11 } 12 13 class WindowOfSaleTicket1 implements Runnable{ 14 private int ticketcount = 100; 15 Object obj = new Object(); 16 @Override 17 public void run() { 18 while (true) { 19 show(); 20 } 21 22 } 23 24 public synchronized void show() { 25 if (ticketcount > 0) { 26 try { 27 Thread.sleep(100); 28 System.out.println(Thread.currentThread().getName() + ":" + ticketcount); 29 ticketcount--; 30 } catch (InterruptedException e) { 31 // TODO Auto-generated catch block 32 e.printStackTrace(); 33 } 34 } 35 } 36 37 }
3.使用Lock方式解决线程安全问题:
解决线程安全问题的方式三:Lock锁,JDK5.0新增的方式;
面试题:synchronized 与Lock的异同?
同:二者都是解决线程安全的;
不同:synchronized 在执行完代码后,自动释放同步监视器,Lock需要手动的锁定和手动的解锁;
线程安全的解决方式:
1.同步sychronized :同步代码块 和 同步方法;
2.Lock的方式
例8:
1 public class Day19CreateThreadSafeLock13 { 2 public static void main(String[] args) { 3 WindowOfSaleTicket2 windowOfSaleTicket = new WindowOfSaleTicket2(); 4 Thread thread1 = new Thread(windowOfSaleTicket); 5 Thread thread2 = new Thread(windowOfSaleTicket); 6 Thread thread3 = new Thread(windowOfSaleTicket); 7 thread1.start(); 8 thread2.start(); 9 thread3.start(); 10 } 11 } 12 13 class WindowOfSaleTicket2 implements Runnable{ 14 //实例化Lock 15 private ReentrantLock lock = new ReentrantLock(); 16 17 18 19 private int ticketcount = 100; 20 Object obj = new Object(); 21 @Override 22 public void run() { 23 while (true) { 24 try { 25 26 //调用lock方法 锁定 27 lock.lock(); 28 show(); 29 30 }finally { 31 //解锁 32 lock.unlock(); 33 } 34 35 } 36 37 } 38 39 public void show() { 40 if (ticketcount > 0) { 41 try { 42 Thread.sleep(100); 43 System.out.println(Thread.currentThread().getName() + ":" + ticketcount); 44 ticketcount--; 45 } catch (InterruptedException e) { 46 // TODO Auto-generated catch block 47 e.printStackTrace(); 48 } 49 } 50 } 51 52 }
4.线程安全使用案例:
有两个储户分别向一个账户存3000元,每次存1000,存3次,每次存完打印账户余额;
1 public class Day19ThreadQuestion14 { 2 public static void main(String[] args) { 3 BankCount bankCount = new BankCount(); 4 Thread thread1 = new Thread(bankCount); 5 Thread thread2 = new Thread(bankCount); 6 thread1.setName("甲"); 7 thread2.setName("乙"); 8 thread1.start(); 9 thread2.start(); 10 } 11 } 12 class BankCount implements Runnable{ 13 double account = 0; 14 int count = 0; 15 @Override 16 public void run() { 17 while(true) { 18 try { 19 Thread.sleep(200); 20 } catch (InterruptedException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 addAccount(1000); 25 26 27 } 28 29 30 } 31 private synchronized double addAccount(double mony) { 32 if(count<3) { 33 account += mony; 34 System.out.println(Thread.currentThread().getName()+"存完后的余额:"+account+",count:"+count); 35 36 count++; 37 38 } 39 40 return account; 41 } 42 43 }
5.线程安全的单例模式
1 public class Bank{ 2 private Bank(){ 3 } 4 public static Bank instance = null; 5 public static Band getInstance(){ 6 if(instance == null){ 7 synchronized(Bank.class){ 8 instance = new Bank(); 9 } 10 } 11 return instance; 12 } 13 }
六、线程的生命周期:
1.创建;Thread类 or 子类的对象声明并创建时,新生线程对象处于新建状态 MyThread thread = new MyThread();
2.就绪;调用start thread.start();
3.运行;就绪的线程被CPU调度并获得cpu资源,那么就是进入了运行状态;
4.阻塞;让出CPU并临时中断自己的执行,就是进入了阻塞状态;
5.死亡;线程完成了它的全部工作或被强制终止or遇到异常导致结束;
七、线程的通信:
涉及到的3个方法:
1.wait():一旦执行此方法,当前线程就处于阻塞状态;并释放同步监视器
2.notify():一旦执行此方法:就会唤醒的一个被wait()的方法,如果有多个线程被wait(),就唤醒优先级高的;
3.notifyAll():一旦执行此方法:就会唤醒的所有被wait()的方法,
说明:
1.线程通信这3个方法,都得在同步代码块或者同步方法中,;
2.线程通信这3个方法调用者必须是同步代码块或者是同步方法中的同步监视器,否则出异常;
3.线程通信这3个方法,定义在object类中的 java.long.object
线程通信例子:打印1-100,线程1 线程二 交替打印;
1 public class Day19ThreadCommunication15 { 2 public static void main(String[] args) { 3 Communication1 communication1 = new Communication1(); 4 Thread thread1 = new Thread(communication1); 5 Thread thread2 = new Thread(communication1); 6 thread1.setName("甲"); 7 thread2.setName("乙"); 8 thread1.start(); 9 thread2.start(); 10 } 11 } 12 class Communication1 implements Runnable{ 13 int count = 1; 14 @Override 15 public void run() { 16 while(true) { 17 try { 18 Thread.sleep(200); 19 } catch (InterruptedException e) { 20 // TODO Auto-generated catch block 21 e.printStackTrace(); 22 } 23 printNum(); 24 25 26 } 27 28 29 } 30 private synchronized void printNum() { 31 32 if(count<101) { 33 System.out.println(Thread.currentThread().getName()+":"+count); 34 count++; 35 notify(); 36 try { 37 wait(); 38 } catch (InterruptedException e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 } 42 43 44 } 45 46 } 47 48 }
面试题:
sleep 和 wait的异同
同:都会使得当前线程进入阻塞状态;
异:.
1.两个方法定义的位置不同;Thread 类中声明sleep();object中声明了wait();
2.调用的范围不同,sleep()可以在任何需要的位置调用,wait()必须在同步代码块or方法中;
3.如果两个方法都使用在同步代码块or同步方法中,sleep不会是否同步监视器;wait()会释放同步监视器
八、死锁:
使用同步机制改写单例模式的懒汉式,改写为线程安全
1.死锁的理解:不同的线程 占用 对方的需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源就形成了死锁
2.说明:出现死锁后,不会抛出异常,不会提示,只是所有的线程都处于阻塞状态;
我们使用同步时,要避免死锁;
解决方法:
1.专门的算法、原则
2.尽量减少同步资源的定义;
3.尽量避免嵌套同步方法;
九、综合例子:
面试题:
生产者(Productor)将产品交到店员(clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有
固定数量的产品(比如20),如果生产者视图生产更多的产品,店员就好叫生产者停一下,如果点店中有了空位放产品了
在通知生产者继续生产,如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了在通知消费者取走产品;
分析:
是否是多线程问题:生产者线程;消费者线程
是否有线程安全问题:共享数据是 ”产品数“
如何解决安全问题,synchronize的两张方法 or lock;
是否需要线程的通信 :是
1 public class Day19ThreadQuestionProductorAndConsumer17 { 2 public static void main(String[] args) { 3 ProductorAndConsumer productorAndConsumer = new ProductorAndConsumer(); 4 Thread Productor = new Thread(productorAndConsumer); 5 Thread Comsumer = new Thread(productorAndConsumer); 6 Productor.setName("生产者"); 7 Comsumer.setName("消费者"); 8 Productor.start(); 9 Comsumer.start(); 10 } 11 } 12 class ProductorAndConsumer implements Runnable{ 13 int account = 0; 14 @Override 15 public void run() { 16 while(true) { 17 try { 18 Thread.sleep(2000); 19 } catch (InterruptedException e) { 20 // TODO Auto-generated catch block 21 e.printStackTrace(); 22 } 23 addAccount(); 24 25 26 } 27 28 29 } 30 private synchronized double addAccount() { 31 notify(); 32 String currentName = Thread.currentThread().getName(); 33 if(account==0) { 34 35 if(currentName.equals("消费者")) { 36 try { 37 System.out.println("account:"+account+",消费者阻塞"); 38 wait(); 39 } catch (InterruptedException e) { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 }else { 44 account = account+1; 45 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!"); 46 47 } 48 49 }else if(account==20) { 50 if(currentName.equals("生产者")) { 51 try { 52 System.out.println("account:"+account+",生产者阻塞"); 53 wait(); 54 } catch (InterruptedException e) { 55 // TODO Auto-generated catch block 56 e.printStackTrace(); 57 } 58 59 }else { 60 account = account-1; 61 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!"); 62 63 } 64 65 }else { 66 if(currentName.equals("生产者")) { 67 account++; 68 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!"); 69 70 71 }else { 72 73 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!"); 74 account--; 75 } 76 77 } 78 79 80 return account; 81 } 82 83 }
1 public class Day19ThreadQuestionProductorAndConsumer17_2 { 2 public static void main(String[] args) { 3 Clerk clerk = new Clerk(); 4 Productor productorAndConsumer = new Productor(clerk); 5 Consummer comsumer = new Consummer(clerk); 6 Thread Productor = new Thread(productorAndConsumer); 7 Thread Comsumer = new Thread(comsumer); 8 Productor.setName("生产者"); 9 Comsumer.setName("消费者"); 10 Productor.start(); 11 Comsumer.start(); 12 } 13 } 14 class Productor implements Runnable{ 15 private Clerk clert; 16 17 public Productor(Clerk clert) { 18 this.clert = clert; 19 } 20 21 @Override 22 public void run() { 23 while(true) { 24 try { 25 Thread.sleep(2000); 26 } catch (InterruptedException e) { 27 // TODO Auto-generated catch block 28 e.printStackTrace(); 29 } 30 clert.addAccount(); 31 } 32 33 34 } 35 36 } 37 class Consummer implements Runnable{ 38 private Clerk clerk ; 39 40 public Consummer(Clerk clerk) { 41 this.clerk = clerk; 42 } 43 44 @Override 45 public void run() { 46 while(true) { 47 try { 48 Thread.sleep(2000); 49 } catch (InterruptedException e) { 50 // TODO Auto-generated catch block 51 e.printStackTrace(); 52 } 53 clerk.addAccount(); 54 55 56 } 57 58 59 } 60 61 } 62 class Clerk{ 63 private int account=0; 64 65 public synchronized double addAccount() { 66 notify(); 67 String currentName = Thread.currentThread().getName(); 68 if(account==0) { 69 70 if(currentName.equals("消费者")) { 71 try { 72 System.out.println("account:"+account+",消费者阻塞"); 73 wait(); 74 } catch (InterruptedException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 }else { 79 account = account+1; 80 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!"); 81 82 } 83 84 }else if(account==20) { 85 if(currentName.equals("生产者")) { 86 try { 87 System.out.println("account:"+account+",生产者阻塞"); 88 wait(); 89 } catch (InterruptedException e) { 90 // TODO Auto-generated catch block 91 e.printStackTrace(); 92 } 93 94 }else { 95 account = account-1; 96 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!"); 97 98 } 99 100 }else { 101 if(currentName.equals("生产者")) { 102 account++; 103 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!"); 104 105 106 }else { 107 108 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!"); 109 account--; 110 } 111 112 } 113 114 115 return account; 116 } 117 118 }