VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • 七、多线程

一、概念:

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 }
复制代码

 

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

来源:https://www.cnblogs.com/lixiuming521125/p/14289040.html

相关教程