0
点赞
收藏
分享

微信扫一扫

Java多线程简析——Synchronized(同步锁)、Lock以及线程池

编程练习生J 2023-05-15 阅读 112


Java多线程

Java中,可运行的程序都是有一个或多个进程组成。进程则是由多个线程组成的。

最简单的一个进程,会包括mian线程以及GC线程。

线程的状态

线程状态由以下一张网上图片来说明:

在图中,红框标识的部分方法,可以认为已过时,不再使用。

(1)wait、notify、notifyAll是线程中通信可以使用的方法。线程中调用了wait方法,则进入阻塞状态,只有等另一个线程调用与wait同一个对象的notify方法。这里有个特殊的地方,调用wait或者notify,前提是需要获取锁,也就是说,需要在同步块中做以上操作。

(2)join方法。该方法主要作用是在该线程中的run方法结束后,才往下执行。如以下代码:

 

[html]  view plain  copy

 


1. package com.thread.simple;  
2.   
3. public class ThreadJoin {  
4.   
5.       
6.     public static void main(String[] args) {  
7.   
8. thread= new Thread(new Runnable() {  
9.               
10.             @Override  
11.             public void run() {  
12.                 System.err.println("线程"+Thread.currentThread().getId()+" 打印信息");  
13.             }  
14.         });  
15.         thread.start();  
16.           
17.         try {  
18.             thread.join();  
19.         } catch (InterruptedException e) {  
20.             // TODO Auto-generated catch block  
21.             e.printStackTrace();  
22.         }  
23.           
24.         System.err.println("主线程打印信息");  
25.           
26.     }  
27.   
28. }



该方法显示的信息是:

 

线程8 打印信息

主线程打印信息

如果去掉其中的join方法,则显示如下:

主线程打印信息
线程8 打印信息

(3)yield方法。这个是线程本身的调度方法,使用时你可以在run方法执行完毕时,调用该方法,告知你已可以出让内存资源。

其他的线程方法,基本都会在日常中用到,如start、run、sleep,这里就不再介绍。

Synchronized(同步锁)

在Java中使用多线程,你就不能绕过同步锁这个概念。这在多线程中是十分重要的。

在Java多线程的使用中,你必然会遇到一个问题:多个线程共享一个或者一组资源,这资源包括内存、文件等。

很常见的一个例子是,张三在银行账户存有9999元,经过多次的取100,存100后,账户还有多少钱?

看代码:

以下表示账户信息:

 



[html]  view plain  copy

    1. package com.thread.simple;  
    2.   
    3. import java.sql.Time;  
    4. import java.util.concurrent.TimeUnit;  
    5.   
    6. public class Account {  
    7.   
    8.     private String name;  
    9.     private float amt;  
    10.     public Account(String name,float amt) {  
    11. this.name=name;  
    12. this.amt=amt;  
    13.     }  
    14.   
    15.     public  void  increaseAmt(float increaseAmt){  
    16.         try {  
    17.             TimeUnit.SECONDS.sleep(1);  
    18.         } catch (InterruptedException e) {  
    19.             // TODO Auto-generated catch block  
    20.             e.printStackTrace();  
    21.         }  
    22.         amt+=increaseAmt;  
    23.     }  
    24.       
    25.     public  void decreaseAmt(float decreaseAmt){  
    26.         try {  
    27.             TimeUnit.SECONDS.sleep(1);  
    28.         } catch (InterruptedException e) {  
    29.             // TODO Auto-generated catch block  
    30.             e.printStackTrace();  
    31.         }  
    32. amt-=decreaseAmt;  
    33.     }  
    34.       
    35.     public void printMsg(){  
    36.         System.out.println(name+"账户现有金额为:"+amt);  
    37.     }  
    38. }



    以下是我们操作账户的方法:

     

     



    [html]  view plain  copy

    1. <span style="white-space:pre">        </span>final int NUM=100;  
    2.           
    3. threads=new Thread[NUM];  
    4. i=0;i<NUM;i++){  
    5.             if(threads[i]==null){  
    6.                 threads[i]=new Thread(new Runnable() {  
    7.                       
    8.                     @Override  
    9.                     public void run() {  
    10.                         account.increaseAmt(100f);  
    11.                         account.decreaseAmt(100f);  
    12.                     }  
    13.                 });  
    14.                 threads[i].start();  
    15.             }  
    16.         }  
    17.           
    18. i=0;i<NUM;i++){  
    19.             try {  
    20.                 threads[i].join();  
    21.             } catch (InterruptedException e) {  
    22.                 // TODO Auto-generated catch block  
    23.                 e.printStackTrace();  
    24.             }  
    25.         }  
    26.           
    27.         account.printMsg();



    你会发现,每次打印出来的账户余额都不一定是一样的。这就是同步锁的必要性。

     

    java中,提供了多种使用同步锁的方式。

    (1)对动态方法的修饰。

    作用的是调用该方法的对象(或者说对象引用)。

     



    [html]  view plain  copy

    1. public synchronized void doSomething(){}

    作用的是调用该方法的对象(或者说对象引用)。

    [html]  view plain  copy


    1. public  void  increaseAmt(float increaseAmt){  
    2.           
    3.         try {  
    4.             TimeUnit.SECONDS.sleep(1);  
    5.         } catch (InterruptedException e) {  
    6.             // TODO Auto-generated catch block  
    7.             e.printStackTrace();  
    8.         }  
    9.         synchronized (this) {  
    10.             System.out.println(this);  
    11.             amt+=increaseAmt;  
    12.         }  
    13.     }



     

    (3)对静态方法的修饰。

     

    作用的是静态方法所在类的所有对象(或者说对象引用)。

     



    [html]  view plain  copy


      1. public synchronized static  void  increaseAmt(float increaseAmt){  
      2.         try {  
      3.             TimeUnit.SECONDS.sleep(1);  
      4.         } catch (InterruptedException e) {  
      5.             // TODO Auto-generated catch block  
      6.             e.printStackTrace();  
      7.         }  
      8.         amt+=increaseAmt;  
      9.     }



       

       

      (4)对类的修饰。

       

      作用的是静态方法所在类的所有对象(或者说对象引用)。

       



      [html]  view plain  copy


        1. synchronized (AccountSynchronizedClass.class) {  
        2. amt-=decreaseAmt;  
        3.     }



        以修饰代码块的方式为例,我们重新运行以上代码后,得到了正确的结果。代码如下:

         

         



        [html]  view plain  copy


        1. package com.thread.simple;  
        2.   
        3. import java.util.concurrent.TimeUnit;  
        4. /**  
        5.  * Synchronized 代码块  
        6.  * @author 战国  
        7.  *  
        8.  */  
        9. public class AccountSynchronizedBlock {  
        10.   
        11.     private String name;  
        12.     private float amt;  
        13.     public AccountSynchronizedBlock(String name,float amt) {  
        14. this.name=name;  
        15. this.amt=amt;  
        16.     }  
        17.   
        18.     public  void  increaseAmt(float increaseAmt){  
        19.           
        20.         try {  
        21.             TimeUnit.SECONDS.sleep(1);  
        22.         } catch (InterruptedException e) {  
        23.             // TODO Auto-generated catch block  
        24.             e.printStackTrace();  
        25.         }  
        26.         synchronized (this) {  
        27.             System.out.println(this);  
        28.             amt+=increaseAmt;  
        29.         }  
        30.     }  
        31.       
        32.     public  void decreaseAmt(float decreaseAmt){  
        33.         try {  
        34.             TimeUnit.SECONDS.sleep(1);  
        35.         } catch (InterruptedException e) {  
        36.             // TODO Auto-generated catch block  
        37.             e.printStackTrace();  
        38.         }  
        39.         synchronized (this) {  
        40.             System.out.println(this);  
        41. amt-=decreaseAmt;  
        42.         }  
        43.           
        44.     }  
        45.       
        46.     public void printMsg(){  
        47.         System.out.println(name+"账户现有金额为:"+amt);  
        48.     }  
        49. }



         

         



        [html]  view plain  copy

        1. //多线程synchronized修饰代码块 ,每次计算的值都一样  
        2.           
        3. account=new AccountSynchronizedBlock("张三", 9999.0f);  
        4. NUM=50;  
        5.           
        6. threads=new Thread[NUM];  
        7. i=0;i<NUM;i++){  
        8.             if(threads[i]==null){  
        9.                 threads[i]=new Thread(new Runnable() {  
        10.                       
        11.                     @Override  
        12.                     public void run() {  
        13.                         account.increaseAmt(100f);  
        14.                         account.decreaseAmt(100f);  
        15.                     }  
        16.                 });  
        17.                 threads[i].start();  
        18.             }  
        19.         }  
        20.           
        21. i=0;i<NUM;i++){  
        22.             try {  
        23.                 threads[i].join();  
        24.             } catch (InterruptedException e) {  
        25.                 // TODO Auto-generated catch block  
        26.                 e.printStackTrace();  
        27.             }  
        28.         }  
        29.         account.printMsg();


        以上是同步锁的简单说明。

        在JDK5中,Java又引入了一个相似的概念Lock,也就是锁。功能与synchronized是类似的。

        Lock

        Lock对比synchronized有高手总结的差异如下:

         

        总结来说,Lock和synchronized有以下几点不同:

          1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

          2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

          3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

          4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

          5)Lock可以提高多个线程进行读操作的效率。

          在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

        (参考http://www.cnblogs.com/dolphin0520/p/3923167.html)。

        Lock的操作与synchronized相比,灵活性更高,而且Lock提供多种方式获取锁,有Lock、ReadWriteLock接口,以及实现这两个接口的ReentrantLock类、ReentrantReadWriteLock类。

        对Lock的简单操作代码如下:

         



        [html]  view plain  copy


        1. package com.thread.simple;  
        2.   
        3. import java.util.ArrayList;  
        4. import java.util.List;  
        5. import java.util.concurrent.locks.Lock;  
        6. import java.util.concurrent.locks.ReadWriteLock;  
        7. import java.util.concurrent.locks.ReentrantLock;  
        8. import java.util.concurrent.locks.ReentrantReadWriteLock;  
        9.   
        10. public class LockImp {  
        11.   
        12.       
        13. lock=new ReentrantLock();  
        14. rwLock=new ReentrantReadWriteLock();  
        15.       
        16. <Integer> list=new ArrayList<Integer>();  
        17.       
        18.     public void doReentrantLock(Thread thread){  
        19.         lock.lock();  
        20.         System.out.println(thread.getName()+"获取锁");  
        21.         try {  
        22. i=0;i<10;i++){  
        23.                     list.add(i);  
        24.                 }  
        25.         } catch (Exception e) {  
        26.               
        27.         }finally{  
        28.             lock.unlock();  
        29.             System.out.println(thread.getName()+"释放锁");  
        30.         }  
        31.           
        32.     }  
        33.     public void doReentrantReadLock(Thread thread){  
        34.         rwLock.readLock().lock();  
        35.         System.out.println(thread.getName()+"获取读锁");  
        36.         try {  
        37. i=0;i<10;i++){  
        38.                 list.add(i);  
        39.             }  
        40.         } catch (Exception e) {  
        41.               
        42.         }finally{  
        43.             rwLock.readLock().unlock();  
        44.             System.out.println(thread.getName()+"释放读锁");  
        45.         }  
        46.           
        47.     }  
        48.     public void doReentrantWriteLock(Thread thread){  
        49.         rwLock.writeLock().lock();  
        50.         System.out.println(thread.getName()+"获取写锁");  
        51.         try {  
        52. i=0;i<10;i++){  
        53.                 list.add(i);  
        54.             }  
        55.         } catch (Exception e) {  
        56.               
        57.         }finally{  
        58.             rwLock.writeLock().unlock();  
        59.             System.out.println(thread.getName()+"释放写锁");  
        60.         }  
        61.           
        62.     }  
        63.       
        64.       
        65.       
        66.     /**  
        67.      * @param args  
        68.      */  
        69.     public static void main(String[] args) {  
        70.   
        71. lockImp=new LockImp();  
        72.           
        73. thread1=new Thread();  
        74. thread2=new Thread();  
        75. thread3=new Thread();  
        76.           
        77.         new Thread(new Runnable() {  
        78.               
        79.             @Override  
        80.             public void run() {  
        81.                 lockImp.doReentrantLock(thread1);  
        82.             }  
        83.         }).start();  
        84.           
        85.         new Thread(new Runnable() {  
        86.                       
        87.                     @Override  
        88.                     public void run() {  
        89.                         lockImp.doReentrantLock(thread2);  
        90.                     }  
        91.                 }).start();  
        92.           
        93.         new Thread(new Runnable() {  
        94.               
        95.             @Override  
        96.             public void run() {  
        97.                 lockImp.doReentrantLock(thread3);  
        98.             }  
        99.         }).start();  
        100.       
        101.           
        102.         lockImp.doReentrantReadLock(thread1);  
        103.         lockImp.doReentrantReadLock(thread2);  
        104.         lockImp.doReentrantReadLock(thread3);  
        105.           
        106.         lockImp.doReentrantWriteLock(thread1);  
        107.         lockImp.doReentrantWriteLock(thread2);  
        108.         lockImp.doReentrantWriteLock(thread3);  
        109.     }  
        110.   
        111. }



        Lock的使用中,务必需要lock、unlock同时使用,避免死锁。

         

         

        线程池的使用

        为什么使用线程池?

         

        因为使用它有好处:(1)在界面上,简化了写法,代码更简洁(2)对程序中的线程可以进行适度的管理(3)有效较低了多个线程的内存占有率等。

        这是一篇讲述线程池非常好的文章:http://www.cnblogs.com/dolphin0520/p/3932921.html

        如果对线程池有不了解的同学,可以参考链接中的文章,讲的深入浅出。

        在这里只是简单的封装一个线程池的工具类,仅供参考:

         



        [html]  view plain  copy


        1. package com.thread.simple;  
        2.   
        3. import java.util.concurrent.ExecutorService;  
        4. import java.util.concurrent.Executors;  
        5.   
        6. public class ThreadPoolUtil {  
        7.   
        8.      private volatile static ThreadPoolUtil instance;  
        9.      private ThreadPoolUtil(){}  
        10.      private static ExecutorService threadPool;  
        11.        
        12.        
        13.      public static ThreadPoolUtil getInstance(){  
        14. instance==null){  
        15.              synchronized (ThreadPoolUtil.class) {  
        16. instance=new ThreadPoolUtil();  
        17. threadPool=Executors.newCachedThreadPool();  
        18.             }  
        19.          }  
        20.          return instance;  
        21.      }  
        22.        
        23.     public void excute(Runnable runnable){  
        24.         threadPool.execute(runnable);  
        25.     }  
        26.       
        27.     public void shutdown(){  
        28.         threadPool.shutdown();  
        29.     }  
        30.       
        31.     public boolean isActive(){  
        32.         if(threadPool.isTerminated()){  
        33.             return false;  
        34.         }  
        35.         return true;  
        36.     }  
        37. }


        举报

        相关推荐

        0 条评论