java 线程同步的方法_JAVA之线程同步的三种方法

最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下。这三种方法分别是:synchronized代码段、synchronized修饰方法/类、ThreadLocal本地线程变量。

我们通过一个例子来表现这三种方法:一张银行卡里面有300块钱,15个线程从这张银行卡中取钱,每个线程取一次且每次取20块钱;当当前余额不足100元时,则向账户中汇款20元。三种方法每种方法都有5个线程。我们预期的结果是当15个线程都执行完毕,银行卡里面的余额应该显示为80。

准备工作

我们需要一个账户的实体类Account,其中有一个属性money。Account类作为账户类的父类,我们在后面将用三种方法分别生成一个子类来继承这个类。Account类的具体代码如下:

1 public abstract class Account { //抽象类

2 protected static Account account; //Account类的实例,全局只有一个

3 protected static final int DEFAULT_MONEY = 300; //账户中默认有300块钱

4 protected static int money; //记录当前账户中的余额5

6 //获取账户实例的方法,由于是static方法不能定义为抽象方法,所以要在子类中重写这个方法

7 public staticAccount getInstance() {8 return null;9 }10

11 //抽象方法,设置当前账户中的余额,三种方法分别有不同的实现方法

12 public abstract void setMoney(intmoney);13

14 //获取当前账户中的余额

15 public intgetMoney() {16 returnmoney;17 }18 }

我们可以在每种方法中都把线程要进行的工作都进行一遍,但是这样的话代码就会重用,所以我们将这段代码提取出来形成一个工具类MyRunnable(实现自Runnable接口),在这个类的run()方法中进行三种方法的所有线程都应该做的事情,比如取出当前账户中的余额、判断余额是否小于100、设置账户余额(汇款或取款)等。MyRunnable类中的代码如下:

1 public class MyRunnable implementsRunnable {2 private Account account; //账户的父类声明,在构造方法中传入具体子类的引用3

4 //构造方法中传入Account类的子类,即该线程绑定的账户操作的方法

5 publicMyRunnable(Account account) {6 this.account =account;7 }8

9 @Override10 public voidrun() {11 String currentThreadName = Thread.currentThread().getName(); //获取当前线程的名称

12 System.out.println(currentThreadName + " is running...");13 System.out.println(currentThreadName + ":before=" + account.getMoney()); //打印账户当前的余额

14 if (account.getMoney() < 100) { //如果账户当前的余额小于100,则向账户中汇款20元

15 account.setMoney(account.getMoney() + 20);16 } else { //如果账户余额还大于100,则取出20元

17 account.setMoney(account.getMoney() - 20);18 }19 System.out.println(currentThreadName + ":after=" + account.getMoney()); //打印操作后账户的余额

20 }21 }

下面贴出三种方法的代码。

第一种方法:SYNCHRONIZED修饰代码段的方法

1 public class AccountForSyn extendsAccount {2

3 @SuppressWarnings("static-access")4 privateAccountForSyn() {5 account.money =DEFAULT_MONEY;6 }7

8 public staticAccountForSyn getInstance() {9 if (account == null) {10 //这里为了防止两个线程同时访问account实例,所以在同步块的前后分别进行了一次判断

11 synchronized (AccountForSyn.class) {12 if (account == null) {13 account = newAccountForSyn();14 }15 }16 }17 return(AccountForSyn) account;18 }19

20 @SuppressWarnings("static-access")21 @Override22 public void setMoney(int money) { //设置account账户中的余额

23 /**

24 * 核心代码25 */

26 synchronized (AccountForSyn.class) { //SYNCHRONIZED后面的参数是一个Object类型的参数,可以是任意的Object(最好是共享的资源)

27 account.money =money;28 }29 }30 }

第二种方法:SYNCHRONIZED关键字修饰方法的方法

1 public class AccountForSynM extendsAccount {2

3 @SuppressWarnings("static-access")4 privateAccountForSynM() {5 account.money =DEFAULT_MONEY;6 }7

8 public staticAccountForSynM getInstance() {9 if (account == null) {10 synchronized (AccountForSyn.class) {11 if (account == null) {12 account = newAccountForSynM();13 }14 }15 }16 return(AccountForSynM) account;17 }18

19 @SuppressWarnings("static-access")20 @Override21 /**

22 * 核心代码23 */

24 public synchronized void setMoney(intmoney) {25 account.money =money;26 }27 }

第三种方法:ThreadLocal本地线程变量的方法

1 public class AccountForLocal extendsAccount {2 /**

3 * ThreadLocal是本地线程存储变量,里面存储着所有线程共享的资源。4 * ThreadLocal的工作原理与SYNCHRONIZED关键字不同:5 * SYNCHRONIZED关键字是对代码块上锁,使一个线程可以从头到尾一次性的执行其中的代码6 * ThreadLocal是对线程共享的资源进行多次备份,再分发给全部的线程,线程对数据进行修改后提交7 */

8 private static ThreadLocal threadLocal = new ThreadLocal(); //本地线程存储变量ThreadLocal的声明

9

10 @SuppressWarnings("static-access")11 privateAccountForLocal() {12 account.money =DEFAULT_MONEY;13 }14

15 public staticAccountForLocal getInstance() {16 account = threadLocal.get(); //从ThreadLocal中获取线程共享资源

17 if (account == null) {18 account = newAccountForLocal();19 /**

20 * 核心代码21 */

22 threadLocal.set((AccountForLocal) account); //如果资源不存在,则实例化一个之后提交给ThreadLocal

23 }24 return(AccountForLocal) account;25 }26

27 @SuppressWarnings("static-access")28 @Override29 public void setMoney(intmoney) {30 account.money =money;31 /**

32 * 核心代码33 */

34 threadLocal.set((AccountForLocal) account);35 }36 }

主函数的代码如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 public class MainClass { //主函数

2 public static voidmain(String[] args) {3 //定义15个线程4 //SYNCHRONIZED修饰代码段的方法的五个线程

5 Thread thread11 = new Thread(new MyRunnable(AccountForSyn.getInstance()), "Thread A1");6 Thread thread12 = new Thread(new MyRunnable(AccountForSyn.getInstance()), "Thread A2");7 Thread thread13 = new Thread(new MyRunnable(AccountForSyn.getInstance()), "Thread A3");8 Thread thread14 = new Thread(new MyRunnable(AccountForSyn.getInstance()), "Thread A4");9 Thread thread15 = new Thread(new MyRunnable(AccountForSyn.getInstance()), "Thread A5");10 //ThreadLocal本地线程变量的方法的五个线程

11 Thread thread21 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread B1");12 Thread thread22 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread B2");13 Thread thread23 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread B3");14 Thread thread24 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread B4");15 Thread thread25 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread B5");16 //SYNCHRONIZED关键字修饰方法的方法的五个线程

17 Thread thread31 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread C1");18 Thread thread32 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread C2");19 Thread thread33 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread C3");20 Thread thread34 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread C4");21 Thread thread35 = new Thread(new MyRunnable(AccountForLocal.getInstance()), "Thread C5");22

23 //启动15个线程

24 thread11.start();25 thread21.start();26 thread31.start();27 thread12.start();28 thread22.start();29 thread32.start();30 thread13.start();31 thread23.start();32 thread33.start();33 thread14.start();34 thread24.start();35 thread34.start();36 thread15.start();37 thread25.start();38 thread35.start();39 }40 }

主函数代码

运行结果如下:

1d59743594b536ffae2a3af4a19c057c.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值