多线程同步

多线程同步的实现方式有多种:

1、同步方法:即由synchronized修饰的同步方法

   由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

2、同步代码块:即由synchronized修饰的语句块

   被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。

   注:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

3、使用volatile

   每次要线程要访问volatile修饰的变量时都是从内存中读取,每个线程访问到的变量值都是一样的。这样就保证了同步。volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。

4、重入锁实现线程同步:lock()

      ReentrantLock类是可重入、互斥、实现了Lock接口的锁。通常在finally代码释放锁。

5、使用局部变量实现线程同步(ThreadLocal修饰的变量)

   使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

一个银行账户同时被两个线程操作,一个取100块,一个存钱100块。假设账户余额为0:

示例代码:

1、synchroized代码块示例

 1 package com.thread;
 2 
 3 
 4 /** 
 5 * @author 作者:方o丈
 6 * @version 创建时间:2018年6月10日 下午3:46:03 
 7 * 类说明:账户操作。
 8 * 目的:练习多线程同步。使用Synchroized同步块实现线程同步
 9 */
10 public class TestSynchroized {
11 
12     //账户
13     private int account = 0;
14     
15     /**
16      * 存钱
17      * @param money
18      */
19     public void addMoney(int money){
20         //存钱
21         synchronized(this){
22             account += money;
23         }
24         System.out.println(System.currentTimeMillis()+" 存入了:"+money+",账户余额:"+account+"\n");
25     }
26     
27     /**
28      * 取钱
29      * @param money
30      */
31     public void subTractMoney(int money){
32         synchronized(this){
33             if(account - money < 0){
34                 System.out.println(System.currentTimeMillis()+" 账户余额不足。。。\n");
35                 return;
36             }
37             //取钱
38             account -= money;
39         }
40         System.out.println(System.currentTimeMillis()+" 取出了:"+money+",账户余额:"+account+"\n");
41     }
42     
43     /**
44      * 查询余额
45      */
46     public void selectMoney(){
47         System.out.println(account);
48     }
49     
50 }
 1 package com.thread;
 2 
 3 
 4 /**
 5  * @author 作者:方o丈
 6  * @version 创建时间:2018年6月10日 下午4:21:13 
 7  * 多线程实现方式:1:继承Thread类、2:实现 Runnable接口
 8  * 类说明:测试多线程同步。
 9  */
10 public class Test{
11 
12     private static int number = 10;
13     
14     public static void main(String[] args) {
15         final TestSynchroized demo1 = new TestSynchroized();
16         //创建存钱线程
17         Thread addThread = new Thread(new Runnable() {
18             
19             @Override
20             public void run() {
21                 while(number >0){
22                     try {
23                         Thread.sleep(1000);
24                         demo1.addMoney(100);
25                         number--;
26                     } catch (InterruptedException e) {
27                         e.printStackTrace();
28                     }
29                 }
30             }
31         });
32         
33         //创建取钱线程
34         Thread subTractThread = new Thread(new Runnable() {
35             
36             @Override
37             public void run() {
38                 while (number >0) {
39                     try {
40                         demo1.subTractMoney(100);
41                         number--;
42                         Thread.sleep(1000);
43                     } catch (InterruptedException e) {
44                         e.printStackTrace();
45                     }
46                 }
47             }
48         });
49         
50         //启动存钱线程
51         addThread.start();
52         //启动取钱线程
53         subTractThread.start();
54     }
55     
56 }

执行结果:

 

2、可重入锁代码示例

 1 package com.thread;
 2 
 3 import java.util.concurrent.locks.Lock;
 4 import java.util.concurrent.locks.ReentrantLock;
 5 
 6 /** 
 7 * @author 作者:方o丈
 8 * @version 创建时间:2018年6月10日 下午9:36:47 
 9 * 类说明:账户操作。
10 * 目的:练习多线程同步。使用Lock同步块实现线程同步
11 */
12 public class TestLock {
13 
14     //声明一个 可重入锁
15     private Lock lock = new ReentrantLock();
16     
17     //账户
18     private int account = 0;
19     
20     /**
21      * 存钱
22      * @param money
23      */
24     public void addMoney(int money){
25         
26         //获得锁
27         lock.lock();
28         try{
29             account += money;
30             System.out.println(System.currentTimeMillis()+" 存入了:"+money+",账户余额:"+account+"\n");
31         }finally{
32             //释放锁
33             lock.unlock();
34         }
35     }
36     
37     /**
38      * 取钱
39      * @param money
40      */
41     public void subTractMoney(int money){
42         //获得锁
43         lock.lock();
44         try{
45             if(account - money < 0){
46                 System.out.println(System.currentTimeMillis()+" 账户余额不足。。。\n");
47                 return;
48             }
49             //取钱
50             account -= money;
51         }finally{
52             //释放锁
53             lock.unlock();
54         }
55         System.out.println(System.currentTimeMillis()+" 取出了:"+money+",账户余额:"+account+"\n");
56     }
57     
58     /**
59      * 查询余额
60      */
61     public void selectMoney(){
62         System.out.println(account);
63     }
64     
65 }

执行结果:

 

3、局部变量(ThreadLocal修饰的变量)

 

 1 package com.thread;
 2 
 3 
 4 /** 
 5 * @author 作者:方o丈
 6 * @version 创建时间:2018年6月10日 下午10:46:03 
 7 * 类说明:账户操作。
 8 * 目的:练习多线程同步。使用ThreadLocal局部变量来实现
 9 */
10 public class TestThreadLocal {
11     
12     //账户
13     private static ThreadLocal<Integer> account = new ThreadLocal<Integer>() {
14         @Override
15         protected Integer initialValue() {
16             return 0;
17         }
18     };
19     
20     /**
21      * 存钱
22      * @param money
23      */
24     public void addMoney(int money){
25         account.set(account.get() + money);
26         System.out.println(System.currentTimeMillis()+" 存入了:"+money+",账户余额:"+account.get()+"\n");
27     }
28     
29     /**
30      * 取钱
31      * @param money
32      */
33     public void subTractMoney(int money){
34         if(account.get() - money < 0){
35             System.out.println(System.currentTimeMillis()+" 账户余额不足。。。\n");
36             return;
37         }
38         //取钱
39         account.set(account.get() - money);
40         System.out.println(System.currentTimeMillis()+" 取出了:"+money+",账户余额:"+account.get()+"\n");
41     }
42     
43 }

执行结果:

 

从上面三段示例代码可以看出:synchroized和ReentrantLock都能实现同步,ThreadLocal则是不适合本场景,最常见的ThreadLocal使用场景为 用来解决 数据库连接、Session管理等

synchronized跟ReentrantLock相比,有几点局限性:

  • 加锁的时候不能设置超时。ReentrantLock有提供tryLock方法,可以设置超时时间,如果超过了这个时间并且没有获取到锁,就会放弃,而synchronized却没有这种功能
  • ReentrantLock可以选择公平锁和非公平锁,synchronized只是简单的一个独占锁
  • ReentrantLock可以使用多个Condition,而synchronized却只能有1个
  • 不能中断一个试图获得锁的线程
  • ReentrantLock可以获得正在等待线程的个数,计数器等

条件对象的意义在于,对于一个已经获取锁的线程,如果还需要等待其他条件才能继续执行的情况下,才会使用Condition条件对象。

 

参考网址:

https://blog.csdn.net/liuao107329/article/details/53214378

https://blog.csdn.net/pdw2009/article/details/52373947

http://www.cnblogs.com/dolphin0520/p/3920407.html

 

posted on 2018-06-10 22:43  方o丈  阅读(221)  评论(0编辑  收藏  举报

导航