多线程同步
Posted fangzhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程同步相关的知识,希望对你有一定的参考价值。
多线程同步的实现方式有多种:
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+" "); 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()+" 账户余额不足。。。 "); 35 return; 36 } 37 //取钱 38 account -= money; 39 } 40 System.out.println(System.currentTimeMillis()+" 取出了:"+money+",账户余额:"+account+" "); 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+" "); 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()+" 账户余额不足。。。 "); 47 return; 48 } 49 //取钱 50 account -= money; 51 }finally{ 52 //释放锁 53 lock.unlock(); 54 } 55 System.out.println(System.currentTimeMillis()+" 取出了:"+money+",账户余额:"+account+" "); 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()+" "); 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()+" 账户余额不足。。。 "); 36 return; 37 } 38 //取钱 39 account.set(account.get() - money); 40 System.out.println(System.currentTimeMillis()+" 取出了:"+money+",账户余额:"+account.get()+" "); 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
以上是关于多线程同步的主要内容,如果未能解决你的问题,请参考以下文章