Java 多线程总结

系统 1815 0

首先我们先来了解一下进程、线程、并发执行的概念:

进程是指: 一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

 

线程是指: 进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。

一般来说, 当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存空间,因为程序是在内存中运行的。

 

在进程中,有些程序流程块是可以乱序执行的,并且这个代码块可以同时被多次执行。实际上,这样的代码块就是线程体。线程是进程中乱序执行的代码流程。当多个线程同时运行的时候,这样的执行模式成为并发执行。

 

线程的状态

1、 线程共有下面 4 种状态:

 

  • 新建状态(New): 新创建了一个线程对象,当你用new创建一个线程时,该线程尚未运行。
  • 就绪状态(Runnable): 线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  • 运行状态(Running): 就绪状态的线程获取了CPU,执行程序代码。
  • 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

a. 等待阻塞: 运行的线程执行wait()方法,JVM会把该线程放入等待池中。

b. 同步阻塞: 运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM把该线程放入锁。

c. 其他阻塞: 运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead):

a.  由于run方法的正常退出而自然死亡;

b.  没有捕获到的异常事件终止了run方法的执行,从而导致线程突然死亡 

 

 

2、 若要确定某个线程当前是否活着,可以使用  isAlive  方法。

 

   如果该线程是可运行线程或者被中断线程,那么该方法返回 true ;如果该线程仍然是个新建线程,或者该线程是个死线程,那么该方法返回 false

 

3、 注意: 你无法确定一个活线程究竟是处于可运行状态还是被中断状态,也无法确定一个可运行线程是否正处在运行之中。另外,你也无法对尚未成为可运行的线程与已经死掉的线程进行区分。

 

4、 线程必须退出中断状态,并且返回到可运行状态,方法是使用与进入中断状态相反的过程:

 

a.  如果线程已经处于睡眠状态,就必须经过规定的毫秒数

b.  如果线程正在等待输入或输出操作完成,那么必须等待该操作完成

c.  如果线程调用了 wait 方法,那么另外一个线程必须调用 notifyAll 或者 notify 方法

d.  如果线程正在等待另一个线程拥有的对象锁,那么另一个线程必须放弃该锁的所有权

 

5、 下面这副图很好的反映了线程在不同情况下的状态变化。


Java 多线程总结
 

  

 了解完多线程的相关知识,下面来介绍一下在java中多线程的实现方式

JAVA多线程实现方式

 JAVA多线程实现方式主要有以下三种:

1、继承Thread类

2、实现Runnable接口

3、使用ExecutorService、Callable、Future实现有返回结果的多线程。

 

其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。其中最常用的也是前两种实现方式。 下面对前两种实现方式分别做下讲解。

 

1、继承Thread类实现多线程

   

      继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

例如:

Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  MyThread  extends  Thread {  
  4.      public   void  run() {  
  5.         System.out.println( "run()方法正在执行" );  
  6.     }  
  7. }  
Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  MyThread  extends  Thread {  
  4.      public   void  run() {  
  5.         System.out.println( "run()方法正在执行" );  
  6.     }  
  7. }  

 

启动线程方式如下:

Java代码   收藏代码
  1. MyThread myThread1 =  new  MyThread();  
  2. MyThread myThread2 =  new  MyThread();  
  3. myThread1.start();  
  4. myThread2.start();  
Java代码   收藏代码
  1. MyThread myThread1 =  new  MyThread();  
  2. MyThread myThread2 =  new  MyThread();  
  3. myThread1.start();  
  4. myThread2.start();  

 

2、实现Runnable接口方式实现多线程

 

如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口。

方法如下:

Java代码   收藏代码
  1. package  thread;  
  2. class  OtherClass{  
  3.      public   void  print(String str){  
  4.         System.out.println(str);  
  5.     }  
  6. }  
  7.   
  8. public   class  MyThread  extends  OtherClass  implements  Runnable {  
  9.      public   void  run() {  
  10.         System.out.println( "run()正在执行" );  
  11.     }  
  12. }  
Java代码   收藏代码
  1. package  thread;  
  2. class  OtherClass{  
  3.      public   void  print(String str){  
  4.         System.out.println(str);  
  5.     }  
  6. }  
  7.   
  8. public   class  MyThread  extends  OtherClass  implements  Runnable {  
  9.      public   void  run() {  
  10.         System.out.println( "run()正在执行" );  
  11.     }  
  12. }  

 

 

为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例。

具体方法如下:

Java代码   收藏代码
  1. MyThread myThread =  new  MyThread();  
  2. Thread thread =  new  Thread(myThread);  
  3. thread.start();  
Java代码   收藏代码
  1. MyThread myThread =  new  MyThread();  
  2. Thread thread =  new  Thread(myThread);  
  3. thread.start();  

 

 

事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:

Java代码   收藏代码
  1. public   void  run() {  
  2.      if  (target !=  null ) {  
  3.         target.run();  
  4.     }  
  5. }  
Java代码   收藏代码
  1. public   void  run() {  
  2.      if  (target !=  null ) {  
  3.         target.run();  
  4.     }  
  5. }  

 

学会了线程的创建方式,下面我们在举几个线程状态转换的例子

 3、线程状态的转换实例

 

Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  ThreadStateDemo  extends  Thread {  
  4.   
  5.     Thread thread;  
  6.   
  7.      public  ThreadStateDemo() {  
  8.         thread =  new  Thread( this );  
  9.         System.out.println( "创建一个线程:thread" );  
  10.         thread.start();  
  11.     }  
  12.   
  13.      public   void  run() {  
  14.         try  {  
  15.            System.out.println( "线程thread正在运行!" );  
  16.            System.out.println( "线程thread睡眠3秒中...!" );  
  17.            Thread.sleep( 3000 );   //静态方法,使当前正在执行的线程睡眠3秒   
  18.            System.out.println( "线程thread在睡眠后重新运行!" );  
  19.        } catch (InterruptedException e) {  
  20.            System.out.println( "线程被中断" );  
  21.        }  
  22.     }  
  23.   
  24.      public   static   void  main(String[] args) {  
  25.         new  ThreadStateDemo();  
  26.        System.out.println( "主线程main结束!" );  
  27.     }  
  28. }  
Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  ThreadStateDemo  extends  Thread {  
  4.   
  5.     Thread thread;  
  6.   
  7.      public  ThreadStateDemo() {  
  8.         thread =  new  Thread( this );  
  9.         System.out.println( "创建一个线程:thread" );  
  10.         thread.start();  
  11.     }  
  12.   
  13.      public   void  run() {  
  14.         try  {  
  15.            System.out.println( "线程thread正在运行!" );  
  16.            System.out.println( "线程thread睡眠3秒中...!" );  
  17.            Thread.sleep( 3000 );   //静态方法,使当前正在执行的线程睡眠3秒   
  18.            System.out.println( "线程thread在睡眠后重新运行!" );  
  19.        } catch (InterruptedException e) {  
  20.            System.out.println( "线程被中断" );  
  21.        }  
  22.     }  
  23.   
  24.      public   static   void  main(String[] args) {  
  25.         new  ThreadStateDemo();  
  26.        System.out.println( "主线程main结束!" );  
  27.     }  
  28. }  

 

 

【运行结果】如下:

Java代码   收藏代码
  1. 创建一个线程:thread  
  2. 主线程main结束!  
  3. 线程thread正在运行!  
  4. 线程thread睡眠 3 秒中...!  
  5. 线程thread在睡眠后重新运行!  
Java代码   收藏代码
  1. 创建一个线程:thread  
  2. 主线程main结束!  
  3. 线程thread正在运行!  
  4. 线程thread睡眠 3 秒中...!  
  5. 线程thread在睡眠后重新运行!  

 

 

终止线程的实例:

Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  ThreadShutDownDemo {  
  4.   
  5.      public   static   void  main(String args[]) {  
  6.         Runner runner =  new  Runner();  
  7.         Thread thread =  new  Thread(runner);  
  8.         thread.start();  
  9.          for ( int  i= 0 ;i< 10 ;i++) {  
  10.              if (i% 10 != 0 ) {  
  11.                 System.out.println( "在主线程中 i="  + i);  
  12.             }  
  13.         }  
  14.         System.out.println( "主线程main结束" );  
  15.          //通知线程结束   
  16.         runner.shutDown();  
  17.     }  
  18. }  
  19.   
  20. class  Runner  implements  Runnable {  
  21.      //控制线程是否结束   
  22.      private   boolean  flag =  true ;  
  23.       
  24.      public   void  run() {  
  25.          int  i= 0 ;  
  26.          while (flag ==  true ) {  
  27.             System.out.println( "在子线程中 i="  + i++);  
  28.         }  
  29.         System.out.println( "子线程结束" );  
  30.     }  
  31.       
  32.      //设置线程结束标志   
  33.      public   void  shutDown() {  
  34.         flag =  false ;  
  35.     }  
  36. }  
Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  ThreadShutDownDemo {  
  4.   
  5.      public   static   void  main(String args[]) {  
  6.         Runner runner =  new  Runner();  
  7.         Thread thread =  new  Thread(runner);  
  8.         thread.start();  
  9.          for ( int  i= 0 ;i< 10 ;i++) {  
  10.              if (i% 10 != 0 ) {  
  11.                 System.out.println( "在主线程中 i="  + i);  
  12.             }  
  13.         }  
  14.         System.out.println( "主线程main结束" );  
  15.          //通知线程结束   
  16.         runner.shutDown();  
  17.     }  
  18. }  
  19.   
  20. class  Runner  implements  Runnable {  
  21.      //控制线程是否结束   
  22.      private   boolean  flag =  true ;  
  23.       
  24.      public   void  run() {  
  25.          int  i= 0 ;  
  26.          while (flag ==  true ) {  
  27.             System.out.println( "在子线程中 i="  + i++);  
  28.         }  
  29.         System.out.println( "子线程结束" );  
  30.     }  
  31.       
  32.      //设置线程结束标志   
  33.      public   void  shutDown() {  
  34.         flag =  false ;  
  35.     }  
  36. }  

 

 

【运行结果】如下:

Java代码   收藏代码
  1. 在主线程中 i= 1   
  2. 在子线程中 i= 0   
  3. 在主线程中 i= 2   
  4. 在子线程中 i= 1   
  5. 在主线程中 i= 3   
  6. 在子线程中 i= 2   
  7. 在主线程中 i= 4   
  8. 在子线程中 i= 3   
  9. 在主线程中 i= 5   
  10. 在子线程中 i= 4   
  11. 在主线程中 i= 6   
  12. 在主线程中 i= 7   
  13. 在主线程中 i= 8   
  14. 在主线程中 i= 9   
  15. 主线程main结束  
  16. 在子线程中 i= 5   
  17. 子线程结束  
Java代码   收藏代码
  1. 在主线程中 i= 1   
  2. 在子线程中 i= 0   
  3. 在主线程中 i= 2   
  4. 在子线程中 i= 1   
  5. 在主线程中 i= 3   
  6. 在子线程中 i= 2   
  7. 在主线程中 i= 4   
  8. 在子线程中 i= 3   
  9. 在主线程中 i= 5   
  10. 在子线程中 i= 4   
  11. 在主线程中 i= 6   
  12. 在主线程中 i= 7   
  13. 在主线程中 i= 8   
  14. 在主线程中 i= 9   
  15. 主线程main结束  
  16. 在子线程中 i= 5   
  17. 子线程结束  

  

 join()方法实例:

 

Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  TheadJoinDemo {  
  4.   
  5.      public   static   void  main(String[] args) {  
  6.         Runner2 r =  new  Runner2();  
  7.         Thread t =  new  Thread(r);  
  8.         t.start();  
  9.          try  {  
  10.             t.join(); //主线程main将中断,直到线程t执行完毕   
  11.         } catch (InterruptedException e) {  
  12.         }  
  13.          for ( int  i= 0 ;i< 5 ;i++) {  
  14.             System.out.println( "主线程:"  + i);  
  15.         }  
  16.     }  
  17. }  
  18.   
  19. class  Runner2  implements  Runnable {  
  20.      public   void  run() {  
  21.          for ( int  i= 0 ;i< 10 ;i++) {  
  22.             System.out.println( "子线程:"  + i);  
  23.         }  
  24.     }  
  25. }  
Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  TheadJoinDemo {  
  4.   
  5.      public   static   void  main(String[] args) {  
  6.         Runner2 r =  new  Runner2();  
  7.         Thread t =  new  Thread(r);  
  8.         t.start();  
  9.          try  {  
  10.             t.join(); //主线程main将中断,直到线程t执行完毕   
  11.         } catch (InterruptedException e) {  
  12.         }  
  13.          for ( int  i= 0 ;i< 5 ;i++) {  
  14.             System.out.println( "主线程:"  + i);  
  15.         }  
  16.     }  
  17. }  
  18.   
  19. class  Runner2  implements  Runnable {  
  20.      public   void  run() {  
  21.          for ( int  i= 0 ;i< 10 ;i++) {  
  22.             System.out.println( "子线程:"  + i);  
  23.         }  
  24.     }  
  25. }  

 

【运行结果】如下:

Java代码   收藏代码
  1. 子线程: 0   
  2. 子线程: 1   
  3. 子线程: 2   
  4. 子线程: 3   
  5. 子线程: 4   
  6. 子线程: 5   
  7. 子线程: 6   
  8. 子线程: 7   
  9. 子线程: 8   
  10. 子线程: 9   
  11. 主线程: 0   
  12. 主线程: 1   
  13. 主线程: 2   
  14. 主线程: 3   
  15. 主线程: 4   
Java代码   收藏代码
  1. 子线程: 0   
  2. 子线程: 1   
  3. 子线程: 2   
  4. 子线程: 3   
  5. 子线程: 4   
  6. 子线程: 5   
  7. 子线程: 6   
  8. 子线程: 7   
  9. 子线程: 8   
  10. 子线程: 9   
  11. 主线程: 0   
  12. 主线程: 1   
  13. 主线程: 2   
  14. 主线程: 3   
  15. 主线程: 4   

 

介绍完以上几个实例,我们下面对sleep()、wait()、yeid()、join()几个方法进行下区别总结

sleep方法与wait方法的区别:

  1. sleep方法是静态方法,wait方法是非静态方法。
  2. sleep方法在时间到后会自己“醒来”,但wait不能,必须由其它线程通过notify(All)方法让它“醒来”。
  3. sleep方法通常用在不需要等待资源情况下的阻塞,像等待线程、数据库连接的情况一般用wait。

sleep/wait与yeld方法的区别:

调用sleep或wait方法后,线程即进入block状态,而调用yeld方法后,线程进入runnable状态。

 

wait与join方法的区别:

  1. wait方法体现了线程之间的互斥关系,而join方法体现了线程之间的同步关系。
  2. wait方法必须由其它线程来解锁,而join方法不需要,只要被等待线程执行完毕,当前线程自动变为就绪。
  3. join方法的一个用途就是让子线程在完成业务逻辑执行之前,主线程一直等待直到所有子线程执行完毕。

线程的同步问题

在实际应用中,我们通常会遇到多线程安全问题。多线程安全问题:当多条语句在操作同一线程共享数据是,一个线程对多条语句只执行了一部分,还没有执行完, 此时另一个线程参与进来执行,导致共享数据的错误。

 

解决办法:
 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
Java 对于多线程的安全提供了专业的解决方式。

线程的同步是保证多线程安全访问竞争资源的一种手段,对于同步,在具体的Java代码中需要完成一下两个操作:

把竞争访问的资源标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
Java代码   收藏代码
  1. synchronized (对象){  
  2.      代码块  
  3.      ...  
  4. }  
Java代码   收藏代码
  1. synchronized (对象){  
  2.      代码块  
  3.      ...  
  4. }  

 

同步的前提:
1、必须要有两个或者两个以上的线程运行;
2、必须是多个线程使用同一个锁;
好处: 解决了多线程的安全问题;
弊端: 多个线程需要判断锁,较为消耗资源;
注意:  非静态同步函数的对象锁为this,静态同步函数所使用的锁是该方法所在类的字节码文件对象,即类名.class,静态方法里的同步锁都是使用的是类的字节码对象。

 

Java代码   收藏代码
  1. //静态同步函数锁   
  2. public   static   synchronized   void  show(){  
  3.     ticket++;  
  4.     System.out.println(Thread.currentThread().getName()+ "runtime..." +ticket--);  
  5. }  
Java代码   收藏代码
  1. //静态同步函数锁   
  2. public   static   synchronized   void  show(){  
  3.     ticket++;  
  4.     System.out.println(Thread.currentThread().getName()+ "runtime..." +ticket--);  
  5. }  

 

 

 

 

下面来例举一个线程同步的例子:(同步方法)

Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  SynchronizedThread {   
  4.      public   static   void  main(String[] args) {   
  5.         User u =  new  User( "王某" 100 );   
  6.         MyThread2 t1 =  new  MyThread2( "线程A" , u,  10 );   
  7.         MyThread2 t2 =  new  MyThread2( "线程B" , u, - 50 );   
  8.         MyThread2 t3 =  new  MyThread2( "线程C" , u, - 60 );   
  9.         MyThread2 t4 =  new  MyThread2( "线程D" , u, - 40 );   
  10.         MyThread2 t5 =  new  MyThread2( "线程E" , u,  20 );   
  11.         MyThread2 t6 =  new  MyThread2( "线程F" , u,  28 );   
  12.   
  13.         t1.start();   
  14.         t2.start();   
  15.         t3.start();   
  16.         t4.start();   
  17.         t5.start();   
  18.         t6.start();   
  19.     }   
  20. }   
  21.   
  22. class  MyThread2  extends  Thread {   
  23.      private  User u;   
  24.      private   int  y =  0 ;   
  25.   
  26.     MyThread2(String name, User u,  int  y) {   
  27.          super (name);   
  28.          this .u = u;   
  29.          this .y = y;   
  30.     }   
  31.   
  32.      public   void  run() {   
  33.         u.oper(y);   
  34.     }   
  35. }   
  36.   
  37. class  User {   
  38.      private  String code;   
  39.      private   int  cash;   
  40.   
  41.     User(String code,  int  cash) {   
  42.          this .code = code;   
  43.          this .cash = cash;   
  44.     }   
  45.   
  46.      public  String getCode() {   
  47.          return  code;   
  48.     }   
  49.   
  50.      public   void  setCode(String code) {   
  51.          this .code = code;   
  52.     }   
  53.   
  54.      /**   
  55.      * 业务方法   
  56.      * @param x 添加x万元   
  57.      */    
  58.      public   synchronized   void  oper( int  x) {   
  59.          try  {   
  60.             Thread.sleep(10L);   
  61.              this .cash += x;   
  62.             System.out.println(Thread.currentThread().getName() +  "运行结束,增加“"  + x +  "”,当前用户账户余额为:"  + cash);   
  63.             Thread.sleep(10L);   
  64.         }  catch  (InterruptedException e) {   
  65.             e.printStackTrace();   
  66.         }   
  67.     }   
  68.   
  69.      @Override    
  70.      public  String toString() {   
  71.          return   "User{"  +   
  72.                          "code='"  + code + '\ ''  +   
  73.                          ", cash="  + cash +   
  74.                          '}' ;   
  75.     }   
  76. }  
Java代码   收藏代码
  1. package  thread;  
  2.   
  3. public   class  SynchronizedThread {   
  4.      public   static   void  main(String[] args) {   
  5.         User u =  new  User( "王某" 100 );   
  6.         MyThread2 t1 =  new  MyThread2( "线程A" , u,  10 );   
  7.         MyThread2 t2 =  new  MyThread2( "线程B" , u, - 50 );   
  8.         MyThread2 t3 =  new  MyThread2( "线程C" , u, - 60 );   
  9.         MyThread2 t4 =  new  MyThread2( "线程D" , u, - 40 );   
  10.         MyThread2 t5 =  new  MyThread2( "线程E" , u,  20 );   
  11.         MyThread2 t6 =  new  MyThread2( "线程F" , u,  28 );   
  12.   
  13.         t1.start();   
  14.         t2.start();   
  15.         t3.start();   
  16.         t4.start();   
  17.         t5.start();   
  18.         t6.start();   
  19.     }   
  20. }   
  21.   
  22. class  MyThread2  extends  Thread {   
  23.      private  User u;   
  24.      private   int  y =  0 ;   
  25.   
  26.     MyThread2(String name, User u,  int  y) {   
  27.          super (name);   
  28.          this .u = u;   
  29.          this .y = y;   
  30.     }   
  31.   
  32.      public   void  run() {   
  33.         u.oper(y);   
  34.     }   
  35. }   
  36.   
  37. class  User {   
  38.      private  String code;   
  39.      private   int  cash;   
  40.   
  41.     User(String code,  int  cash) {   
  42.          this .code = code;   
  43.          this .cash = cash;   
  44.     }   
  45.   
  46.      public  String getCode() {   
  47.          return  code;   
  48.     }   
  49.   
  50.      public   void  setCode(String code) {   
  51.          this .code = code;   
  52.     }   
  53.   
  54.      /**   
  55.      * 业务方法   
  56.      * @param x 添加x万元   
  57.      */    
  58.      public   synchronized   void  oper( int  x) {   
  59.          try  {   
  60.             Thread.sleep(10L);   
  61.              this .cash += x;   
  62.             System.out.println(Thread.currentThread().getName() +  "运行结束,增加“"  + x +  "”,当前用户账户余额为:"  + cash);   
  63.             Thread.sleep(10L);   
  64.         }  catch  (InterruptedException e) {   
  65.             e.printStackTrace();   
  66.         }   
  67.     }   
  68.   
  69.      @Override    
  70.      public  String toString() {   
  71.          return   "User{"  +   
  72.                          "code='"  + code + '\ ''  +   
  73.                          ", cash="  + cash +   
  74.                          '}' ;   
  75.     }   
  76. }  

 

【运行结果】如下:

Java代码   收藏代码
  1. 线程A运行结束,增加“ 10 ”,当前用户账户余额为: 110   
  2. 线程F运行结束,增加“ 28 ”,当前用户账户余额为: 138   
  3. 线程E运行结束,增加“ 20 ”,当前用户账户余额为: 158   
  4. 线程D运行结束,增加“- 40 ”,当前用户账户余额为: 118   
  5. 线程C运行结束,增加“- 60 ”,当前用户账户余额为: 58   
  6. 线程B运行结束,增加“- 50 ”,当前用户账户余额为: 8   
Java代码   收藏代码
  1. 线程A运行结束,增加“ 10 ”,当前用户账户余额为: 110   
  2. 线程F运行结束,增加“ 28 ”,当前用户账户余额为: 138   
  3. 线程E运行结束,增加“ 20 ”,当前用户账户余额为: 158   
  4. 线程D运行结束,增加“- 40 ”,当前用户账户余额为: 118   
  5. 线程C运行结束,增加“- 60 ”,当前用户账户余额为: 58   
  6. 线程B运行结束,增加“- 50 ”,当前用户账户余额为: 8   

 

 

下面是线程不同步的情况,也就是去掉oper(int x)方法的synchronized修饰符,然后再运行程序

【运行结果】如下:

Java代码   收藏代码
  1. 线程F运行结束,增加“ 28 ”,当前用户账户余额为: 128   
  2. 线程D运行结束,增加“- 40 ”,当前用户账户余额为: 88   
  3. 线程B运行结束,增加“- 50 ”,当前用户账户余额为: 38   
  4. 线程E运行结束,增加“ 20 ”,当前用户账户余额为: 58   
  5. 线程C运行结束,增加“- 60 ”,当前用户账户余额为:- 2   
  6. 线程A运行结束,增加“ 10 ”,当前用户账户余额为: 8   
Java代码   收藏代码
  1. 线程F运行结束,增加“ 28 ”,当前用户账户余额为: 128   
  2. 线程D运行结束,增加“- 40 ”,当前用户账户余额为: 88   
  3. 线程B运行结束,增加“- 50 ”,当前用户账户余额为: 38   
  4. 线程E运行结束,增加“ 20 ”,当前用户账户余额为: 58   
  5. 线程C运行结束,增加“- 60 ”,当前用户账户余额为:- 2   
  6. 线程A运行结束,增加“ 10 ”,当前用户账户余额为: 8   

 

很显然,上面的结果是错误的,导致错误的原因是多个线程并发访问了竞争资源u,并对u的属性做了改动。

注意: 当去掉synchronized修饰符后,线程不在同步,每次运行的结果将都不一样, 可见同步的重要性。

 

再把以上实例改为同步代码块方式 

 

     对于同步,除了同步方法外,还可以使用同步代码块,有时候同步代码块会带来比同步方法更好的效果。

追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可,因此Java引入了同步代码快的策略,以提高性能。

 

在上个例子的基础上,对oper方法做了改动,由同步方法改为同步代码块模式。代码如下:

Java代码   收藏代码
  1. package  thread;  
  2. /**  
  3.  * 同步代码块  
  4.  * @author Chu  
  5.  *  
  6.  */   
  7. public   class  SynchronizedThread2 {   
  8.      public   static   void  main(String[] args) {   
  9.             User u =  new  User( "张三" 100 );   
  10.             MyThread3 t1 =  new  MyThread3( "线程A" , u,  10 );   
  11.             MyThread3 t2 =  new  MyThread3( "线程B" , u, - 50 );   
  12.             MyThread3 t3 =  new  MyThread3( "线程C" , u, - 60 );   
  13.             MyThread3 t4 =  new  MyThread3( "线程D" , u, - 40 );   
  14.             MyThread3 t5 =  new  MyThread3( "线程E" , u,  20 );   
  15.             MyThread3 t6 =  new  MyThread3( "线程F" , u,  28 );  
  16.   
  17.             t1.start();   
  18.             t2.start();   
  19.             t3.start();   
  20.             t4.start();   
  21.             t5.start();   
  22.             t6.start();   
  23.     }   
  24. }   
  25.   
  26. class  MyThread3  extends  Thread {   
  27.      private  User u;   
  28.      private   int  y =  0 ;   
  29.   
  30.     MyThread3(String name, User u,  int  y) {   
  31.          super (name);   
  32.          this .u = u;   
  33.          this .y = y;   
  34.     }   
  35.   
  36.      public   void  run() {   
  37.         u.oper(y);   
  38.     }   
  39. }   
  40.   
  41. class  User2 {   
  42.      private  String code;   
  43.      private   int  cash;   
  44.   
  45.     User2(String code,  int  cash) {   
  46.          this .code = code;   
  47.          this .cash = cash;   
  48.     }   
  49.   
  50.      public  String getCode() {   
  51.          return  code;   
  52.     }   
  53.   
  54.      public   void  setCode(String code) {   
  55.          this .code = code;   
  56.     }   
  57.   
  58.      /**   
  59.      * 业务方法   
  60.      * @param x 添加x万元   
  61.      */    
  62.      public   void  oper( int  x) {   
  63.          try  {   
  64.             Thread.sleep(10L);   
  65.              synchronized  ( this ) {   
  66.                      this .cash += x;   
  67.                     System.out.println(Thread.currentThread().getName() +  "运行结束,增加“"  + x +  "”,当前用户账户余额为:"  + cash);   
  68.             }   
  69.             Thread.sleep(10L);   
  70.         }  catch  (InterruptedException e) {   
  71.             e.printStackTrace();   
  72.         }   
  73.     }   
  74.   
  75.      @Override    
  76.      public  String toString() {   
  77.          return   "User{"  +   
  78.                          "code='"  + code + '\ ''  +   
  79.                          ", cash="  + cash +   
  80.                          '}' ;   
  81.     }   
  82. }  
Java代码   收藏代码
  1. package  thread;  
  2. /**  
  3.  * 同步代码块  
  4.  * @author Chu  
  5.  *  
  6.  */   
  7. public   class  SynchronizedThread2 {   
  8.      public   static   void  main(String[] args) {   
  9.             User u =  new  User( "张三" 100 );   
  10.             MyThread3 t1 =  new  MyThread3( "线程A" , u,  10 );   
  11.             MyThread3 t2 =  new  MyThread3( "线程B" , u, - 50 );   
  12.             MyThread3 t3 =  new  MyThread3( "线程C" , u, - 60 );   
  13.             MyThread3 t4 =  new  MyThread3( "线程D" , u, - 40 );   
  14.             MyThread3 t5 =  new  MyThread3( "线程E" , u,  20 );   
  15.             MyThread3 t6 =  new  MyThread3( "线程F" , u,  28 );  
  16.   
  17.             t1.start();   
  18.             t2.start();   
  19.             t3.start();   
  20.             t4.start();   
  21.             t5.start();   
  22.             t6.start();   
  23.     }   
  24. }   
  25.   
  26. class  MyThread3  extends  Thread {   
  27.      private  User u;   
  28.      private   int  y =  0 ;   
  29.   
  30.     MyThread3(String name, User u,  int  y) {   
  31.          super (name);   
  32.          this .u = u;   
  33.          this .y = y;   
  34.     }   
  35.   
  36.      public   void  run() {   
  37.         u.oper(y);   
  38.     }   
  39. }   
  40.   
  41. class  User2 {   
  42.      private  String code;   
  43.      private   int  cash;   
  44.   
  45.     User2(String code,  int  cash) {   
  46.          this .code = code;   
  47.          this .cash = cash;   
  48.     }   
  49.   
  50.      public  String getCode() {   
  51.          return  code;   
  52.     }   
  53.   
  54.      public   void  setCode(String code) {   
  55.          this .code = code;   
  56.     }   
  57.   
  58.      /**   
  59.      * 业务方法   
  60.      * @param x 添加x万元   
  61.      */    
  62.      public   void  oper( int  x) {   
  63.          try  {   
  64.             Thread.sleep(10L);   
  65.              synchronized  ( this ) {   
  66.                      this .cash += x;   
  67.                     System.out.println(Thread.currentThread().getName() +  "运行结束,增加“"  + x +  "”,当前用户账户余额为:"  + cash);   
  68.             }   
  69.             Thread.sleep(10L);   
  70.         }  catch  (InterruptedException e) {   
  71.             e.printStackTrace();   
  72.         }   
  73.     }   
  74.   
  75.      @Override    
  76.      public  String toString() {   
  77.          return   "User{"  +   
  78.                          "code='"  + code + '\ ''  +   
  79.                          ", cash="  + cash +   
  80.                          '}' ;   
  81.     }   
  82. }  

  

【运行结果】如下:

Java代码   收藏代码
  1. 线程A运行结束,增加“ 10 ”,当前用户账户余额为: 110   
  2. 线程F运行结束,增加“ 28 ”,当前用户账户余额为: 138   
  3. 线程D运行结束,增加“- 40 ”,当前用户账户余额为: 98   
  4. 线程E运行结束,增加“ 20 ”,当前用户账户余额为: 118   
  5. 线程C运行结束,增加“- 60 ”,当前用户账户余额为: 58   
  6. 线程B运行结束,增加“- 50 ”,当前用户账户余额为: 8   
Java代码   收藏代码
  1. 线程A运行结束,增加“ 10 ”,当前用户账户余额为: 110   
  2. 线程F运行结束,增加“ 28 ”,当前用户账户余额为: 138   
  3. 线程D运行结束,增加“- 40 ”,当前用户账户余额为: 98   
  4. 线程E运行结束,增加“ 20 ”,当前用户账户余额为: 118   
  5. 线程C运行结束,增加“- 60 ”,当前用户账户余额为: 58   
  6. 线程B运行结束,增加“- 50 ”,当前用户账户余额为: 8   

 

用到线程的同步,随之可能会带来死锁问题。

导致死锁的原因:两个线程互相等待竞争资源,导致两边都无法得到资源,而使自己无法运行。

下面例举一个导致死锁的一个实例,代码如下:

Java代码   收藏代码
  1. package  thread;  
  2.   
  3. class  Demo1{  
  4.      static  Object obj1= new  Object();  
  5.      static  Object obj2= new  Object();  
  6. }  
  7.   
  8. class  Demo2  implements  Runnable{  
  9.      boolean  flag;  
  10.     Demo2( boolean  flag){  
  11.      this .flag=flag;  
  12. }  
  13.      @Override   
  14.      public   void  run(){  
  15.          if (flag){  
  16.              while ( true ){  
  17.                  synchronized (Demo1.obj1){  
  18.                     System.out.println( "1" );  
  19.                      synchronized (Demo1.obj2){  
  20.                         System.out.println( "2" );  
  21.                     }  
  22.                 }  
  23.             }  
  24.         }  
  25.          else {  
  26.              while ( true ){  
  27.                  synchronized (Demo1.obj2){  
  28.                     System.out.println( "2" );  
  29.                      synchronized (Demo1.obj1){  
  30.                         System.out.println( "1" );  
  31.                     }  
  32.                 }  
  33.             }  
  34.         }     
  35.     }     
  36. }  
Java代码   收藏代码
  1. package  thread;  
  2.   
  3. class  Demo1{  
  4.      static  Object obj1= new  Object();  
  5.      static  Object obj2= new  Object();  
  6. }  
  7.   
  8. class  Demo2  implements  Runnable{  
  9.      boolean  flag;  
  10.     Demo2( boolean  flag){  
  11.      this .flag=flag;  
  12. }  
  13.      @Override   
  14.      public   void  run(){  
  15.          if (flag){  
  16.              while ( true ){  
  17.                  synchronized (Demo1.obj1){  
  18.                     System.out.println( "1" );  
  19.                      synchronized (Demo1.obj2){  
  20.                         System.out.println( "2" );  
  21.                     }  
  22.                 }  
  23.             }  
  24.         }  
  25.          else {  
  26.              while ( true ){  
  27.                  synchronized (Demo1.obj2){  
  28.                     System.out.println( "2" );  
  29.                      synchronized (Demo1.obj1){  
  30.                         System.out.println( "1" );  
  31.                     }  
  32.                 }  
  33.             }  
  34.         }     
  35.     }     
  36. }  

 

最后我再说说:生产者消费者的问题

对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的。

实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓储,生产者消费者模型就显得没有说服力了。
对于此模型,应该明确一下几点:
1、生产者仅仅在仓储未满时候生产,仓满则停止生产;
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待;
3、当消费者发现仓储没产品可消费时候会通知生产者生产;
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。

 此模型将要结合java.lang.Object的wait与notify、notifyAll方法来实现以上的需求。这是非常重要的。

 

具体实现代码如下:

Java代码   收藏代码
  1. package  thread;  
  2. /**   
  3. * Java线程:生产者消费者模型   
  4. * @author Chu 2013-06-15 05:32:29   
  5. */    
  6. public   class  ProductTest {   
  7.      public   static   void  main(String[] args) {   
  8.         Godown godown =  new  Godown( 20 );   
  9.         Consumer c1 =  new  Consumer( 80 , godown);   
  10.         Consumer c2 =  new  Consumer( 30 , godown);   
  11.         Consumer c3 =  new  Consumer( 20 , godown);   
  12.         Producer p1 =  new  Producer( 5 , godown);   
  13.         Producer p2 =  new  Producer( 5 , godown);   
  14.         Producer p3 =  new  Producer( 5 , godown);   
  15.         Producer p4 =  new  Producer( 10 , godown);   
  16.         Producer p5 =  new  Producer( 20 , godown);   
  17.         Producer p6 =  new  Producer( 35 , godown);   
  18.         Producer p7 =  new  Producer( 50 , godown);   
  19.   
  20.         c1.start();   
  21.         c2.start();   
  22.         c3.start();   
  23.         p1.start();   
  24.         p2.start();   
  25.         p3.start();   
  26.         p4.start();   
  27.         p5.start();   
  28.         p6.start();   
  29.         p7.start();   
  30.     }   
  31. }   
  32.   
  33. /** 仓库   */    
  34. class  Godown {   
  35.      public   static   final   int  max_size =  100 //最大库存量    
  36.      public   int  curnum;      //当前库存量    
  37.   
  38.     Godown() {   
  39.     }   
  40.   
  41.     Godown( int  curnum) {   
  42.          this .curnum = curnum;   
  43.     }   
  44.      /**   
  45.      * 生产指定数量的产品   
  46.      * @param neednum   
  47.      */    
  48.      public   synchronized   void  produce( int  neednum) {   
  49.          //测试是否需要生产    
  50.          while  (neednum + curnum > max_size) {   
  51.             System.out.println( "要生产的产品数量"  + neednum +  "超过剩余库存量"  + (max_size - curnum) +  ",暂时不能执行生产任务!" );   
  52.              try  {   
  53.                  //当前的生产线程等待    
  54.                 wait();   
  55.             }  catch  (InterruptedException e) {   
  56.                 e.printStackTrace();   
  57.             }   
  58.         }   
  59.          //满足生产条件,则进行生产,这里简单的更改当前库存量    
  60.         curnum += neednum;   
  61.         System.out.println( "已经生产了"  + neednum +  "个产品,现仓储量为"  + curnum);   
  62.          //唤醒在此对象监视器上等待的所有线程    
  63.         notifyAll();   
  64.     }   
  65.   
  66.      /**   
  67.      * 消费指定数量的产品   
  68.      * @param neednum   
  69.      */    
  70.      public   synchronized   void  consume( int  neednum) {   
  71.          //测试是否可消费    
  72.          while  (curnum < neednum) {   
  73.              try  {   
  74.                  //当前的生产线程等待    
  75.                 wait();   
  76.             }  catch  (InterruptedException e) {   
  77.                 e.printStackTrace();   
  78.             }   
  79.         }   
  80.          //满足消费条件,则进行消费,这里简单的更改当前库存量    
  81.         curnum -= neednum;   
  82.         System.out.println( "已经消费了"  + neednum +  "个产品,现仓储量为"  + curnum);   
  83.          //唤醒在此对象监视器上等待的所有线程    
  84.         notifyAll();   
  85.     }   
  86. }   
  87.   
  88. /** 生产者   */    
  89. class  Producer  extends  Thread {   
  90.      //生产产品的数量    
  91.      private   int  neednum;   
  92.      //仓库    
  93.      private  Godown godown;  
  94.   
  95.     Producer( int  neednum, Godown godown) {   
  96.          this .neednum = neednum;   
  97.          this .godown = godown;   
  98.     }   
  99.   
  100.      public   void  run() {   
  101.          //生产指定数量的产品    
  102.         godown.produce(neednum);   
  103.     }   
  104. }   
  105.   
  106. /** 消费者    */    
  107. class  Consumer  extends  Thread {  
  108.      //生产产品的数量   
  109.      private   int  neednum;  
  110.      //仓库    
  111.      private  Godown godown;   
  112.   
  113.     Consumer( int  neednum, Godown godown) {   
  114.          this .neednum = neednum;   
  115.          this .godown = godown;   
  116.     }   
  117.   
  118.      public   void  run() {   
  119.          //消费指定数量的产品    
  120.         godown.consume(neednum);   
  121.     }   
  122. }  
Java代码   收藏代码
  1. package  thread;  
  2. /**   
  3. * Java线程:生产者消费者模型   
  4. * @author Chu 2013-06-15 05:32:29   
  5. */    
  6. public   class  ProductTest {   
  7.      public   static   void  main(String[] args) {   
  8.         Godown godown =  new  Godown( 20 );   
  9.         Consumer c1 =  new  Consumer( 80 , godown);   
  10.         Consumer c2 =  new  Consumer( 30 , godown);   
  11.         Consumer c3 =  new  Consumer( 20 , godown);   
  12.         Producer p1 =  new  Producer( 5 , godown);   
  13.         Producer p2 =  new  Producer( 5 , godown);   
  14.         Producer p3 =  new  Producer( 5 , godown);   
  15.         Producer p4 =  new  Producer( 10 , godown);   
  16.         Producer p5 =  new  Producer( 20 , godown);   
  17.         Producer p6 =  new  Producer( 35 , godown);   
  18.         Producer p7 =  new  Producer( 50 , godown);   
  19.   
  20.         c1.start();   
  21.         c2.start();   
  22.         c3.start();   
  23.         p1.start();   
  24.         p2.start();   
  25.         p3.start();   
  26.         p4.start();   
  27.         p5.start();   
  28.         p6.start();   
  29.         p7.start();   
  30.     }   
  31. }   
  32.   
  33. /** 仓库   */    
  34. class  Godown {   
  35.      public   static   final   int  max_size =  100 //最大库存量    
  36.      public   int  curnum;      //当前库存量    
  37.   
  38.     Godown() {   
  39.     }   
  40.   
  41.     Godown( int  curnum) {   
  42.          this .curnum = curnum;   
  43.     }   
  44.      /**   
  45.      * 生产指定数量的产品   
  46.      * @param neednum   
  47.      */    
  48.      public   synchronized   void  produce( int  neednum) {   
  49.          //测试是否需要生产    
  50.          while  (neednum + curnum > max_size) {   
  51.             System.out.println( "要生产的产品数量"  + neednum +  "超过剩余库存量"  + (max_size - curnum) +  ",暂时不能执行生产任务!" );   
  52.              try  {   
  53.                  //当前的生产线程等待    
  54.                 wait();   
  55.             }  catch  (InterruptedException e) {   
  56.                 e.printStackTrace();   
  57.             }   
  58.         }   
  59.          //满足生产条件,则进行生产,这里简单的更改当前库存量    
  60.         curnum += neednum;   
  61.         System.out.println( "已经生产了"  + neednum +  "个产品,现仓储量为"  + curnum);   
  62.          //唤醒在此对象监视器上等待的所有线程    
  63.         notifyAll();   
  64.     }   
  65.   
  66.      /**   
  67.      * 消费指定数量的产品   
  68.      * @param neednum   
  69.      */    
  70.      public   synchronized   void  consume( int  neednum) {   
  71.          //测试是否可消费    
  72.          while  (curnum < neednum) {   
  73.              try  {   
  74.                  //当前的生产线程等待    
  75.                 wait();   
  76.             }  catch  (InterruptedException e) {   
  77.                 e.printStackTrace();   
  78.             }   
  79.         }   
  80.          //满足消费条件,则进行消费,这里简单的更改当前库存量    
  81.         curnum -= neednum;   
  82.         System.out.println( "已经消费了"  + neednum +  "个产品,现仓储量为"  + curnum);   
  83.          //唤醒在此对象监视器上等待的所有线程    
  84.         notifyAll();   
  85.     }   
  86. }   
  87.   
  88. /** 生产者   */    
  89. class  Producer  extends  Thread {   
  90.      //生产产品的数量    
  91.      private   int  neednum;   
  92.      //仓库    
  93.      private  Godown godown;  
  94.   
  95.     Producer( int  neednum, Godown godown) {   
  96.          this .neednum = neednum;   
  97.          this .godown = godown;   
  98.     }   
  99.   
  100.      public   void  run() {   
  101.          //生产指定数量的产品    
  102.         godown.produce(neednum);   
  103.     }   
  104. }   
  105.   
  106. /** 消费者    */    
  107. class  Consumer  extends  Thread {  
  108.      //生产产品的数量   
  109.      private   int  neednum;  
  110.      //仓库    
  111.      private  Godown godown;   
  112.   
  113.     Consumer( int  neednum, Godown godown) {   
  114.          this .neednum = neednum;   
  115.          this .godown = godown;   
  116.     }   
  117.   
  118.      public   void  run() {   
  119.          //消费指定数量的产品    
  120.         godown.consume(neednum);   
  121.     }   
  122. }  

 

  【运行结果】如下:

Java代码   收藏代码
  1. 已经消费了 20 个产品,现仓储量为 0   
  2. 已经生产了 5 个产品,现仓储量为 5   
  3. 已经生产了 5 个产品,现仓储量为 10   
  4. 已经生产了 5 个产品,现仓储量为 15   
  5. 已经生产了 20 个产品,现仓储量为 35   
  6. 已经生产了 50 个产品,现仓储量为 85   
  7. 已经消费了 80 个产品,现仓储量为 5   
  8. 已经生产了 10 个产品,现仓储量为 15   
  9. 已经生产了 35 个产品,现仓储量为 50   
  10. 已经消费了 30 个产品,现仓储量为 20   
Java代码   收藏代码
  1. 已经消费了 20 个产品,现仓储量为 0   
  2. 已经生产了 5 个产品,现仓储量为 5   
  3. 已经生产了 5 个产品,现仓储量为 10   
  4. 已经生产了 5 个产品,现仓储量为 15   
  5. 已经生产了 20 个产品,现仓储量为 35   
  6. 已经生产了 50 个产品,现仓储量为 85   
  7. 已经消费了 80 个产品,现仓储量为 5   
  8. 已经生产了 10 个产品,现仓储量为 15   
  9. 已经生产了 35 个产品,现仓储量为 50   
  10. 已经消费了 30 个产品,现仓储量为 20   

 

 

说明:
     对于本例,要说明的是当发现不能满足生产或者消费条件的时候,调用对象的wait方法,wait方法的作用是释放当前线程的所获得的锁,并调用对象的notifyAll() 方法,通知(唤醒)该对象上其他等待线程,使得其继续执行。这样,整个生产者、消费者线程得以正确的协作执行。
    notifyAll() 方法,起到的是一个通知作用,不释放锁,也不获取锁。只是告诉该对象上等待的线程可以竞争执行了。

    以上这个例子仅仅是生产者消费者模型中最简单的一种表示,在这个例子中,如果消费者消费的仓储量达不到满足,而又没有生产者,则程序会一直处于等待状态,这当然是不对的。实际上可以将此例进行修改,修改为,根据消费驱动生产,同时生产兼顾仓库,如果仓不满就生产,并对每次最大消费量做个限制,这样就不存在此问题了,当然这样的例子更复杂,更难以说明这样一个简单模型。

Java 多线程总结


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论