一、概念:

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同时指向多个任务;

七、多线程_i++

二、创建多线程的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():判断当前线程是否存活;当线程执行完成后,则线程死亡;

七、多线程_多线程_02

七、多线程_多线程_03

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

View Code

四、线程的调度

时间片
 抢占式:高优先级的线程抢占cpu
 java的调度方法:
 同优先级的组成先进先出队列(先到先服务),使用时间片服务;
 对高优先级,使用优先调度的抢占式策略;
 
 线程的优先等级:
 MAX_PRIORITY:10
 MIN_PRIORITY:1;
 NORM_PRIORITY:5
 方法:
 getPriority():返回线程的优先等级值
 setPriority():设置线程的优先等级值;
 
 说明:高优先级的线程要抢占低优先级CPU执行次序,但是只是从概率上讲,高概率上执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行

七、多线程_多线程_02

七、多线程_多线程_03

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

View Code

五、线程安全

1.线程安全 产生背景:

例5场景是,3个窗口同时卖火车票(共100张);出现的问题是:不同窗口可能出现相同的票号;可能会卖出错票(即出现了-1号票等情况):

原因:当某线程操作车票时,操作尚未完成,其他线程参与进来,也操作车票导致;

七、多线程_i++_06

 

 

 

 

 

 

 

 

 

 

 

 

 

解决:当一个线程在操作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次,每次存完打印账户余额;

七、多线程_多线程_02

七、多线程_多线程_03

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

View Code

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遇到异常导致结束;

七、多线程_Java_09

七、线程的通信:

涉及到的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

View Code

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

View Code

 

我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。 我要做一个自由又自律的人,靠势必实现的决心认真地活着。