首页 理论教育 线程同步实现方法-Java程序设计

线程同步实现方法-Java程序设计

时间:2023-11-01 理论教育 版权反馈
【摘要】:Java的同步机制是通过提供“锁”来实现的。图6-19没有正确理解synchronized方法示例测试多线程时依然会出现同步问题。图6-22多线程访问共享资源Save MoneyTask5对象main线程中创建并启动的2个存钱线程t1和t2,需要对共享资源Save Money Task5对象smt进行访问,多次测试结果显示,使用同步代码块可以解决多线程同步问题。

线程同步实现方法-Java程序设计

线程需要考虑同步时,就是要做到共享资源在同一时刻只能被一个线程访问。Java的同步机制是通过提供“锁”来实现的。锁机制要求每个线程在访问共享资源之前都要先取得同一把锁,访问完退出时再释放该锁。

Java的同步机制使用关键字synchronized来实现,具体又分为两种,一种是同步方法,一种是同步代码块。

1.同步方法

在方法声明中加入synchronized关键字来声明同步方法。

【例6-7】

通过同步方法解决例6-6中的线程同步问题。

步骤1:在chapter6工程src文件夹下新建一个包cn.linaw.chapter6.demo05,然后将cn.linaw.chapter6.demo01包里的Bank Account.java拷贝过来。编写一个新的存钱任务Save Money Task4.java,任务是对银行账户存2次钱,每次存1元,同时打印银行账户余额,如图6-17所示。

图6-17 存钱任务Save MoneyTask4类定义同步方法

(1)Save Money Task4类里将对Bank Account账户对象的操作提取出来,形成一个synchronized修饰的方法。

(2)如果一个线程调用一个对象上的同步实例方法,首先给该对象加锁。同步方法的加锁操作是隐式的,对于实例方法,锁即为当前对象this。在解锁之前,其他调用该对象中的同步方法的线程将被阻塞。

步骤2:编写一个正确的测试类ThreadSyn Function Test1,在main线程中创建2个存钱线程,对共享资源Save Money Task4对象进行同步访问,如图6-18所示。

图6-18 正确利用synchronized方法同步线程

(1)两个存钱线程t1和t2共享的是同一个Save Money Task4类的对象smt,这两个线程会对该smt对象的同步方法save Money进行同步调用。也就是说,假设t1线程执行对象smt的run方法时,运行到save Money(1)时,由于save Money是synchronized修饰的同步方法,于是smt对象调用前,先申请加锁,锁对象即为smt对象(this锁)。如果t2线程也通过执行对象smt的run方法到达save Money(1),由于该方法是同步方法,因此需要申请锁(即this锁),由于smt对象已经被线程t1占用,所以t2线程被阻塞,直到t1线程释放。因此,线程t1和t2通过smt对象调用save Money方法时做到了同步。

(2)同步方法针对同一个对象才同步调用。同步后的save Money方法操作银行账户Bank Account对象x,不会出现数据破坏问题。多次运行,结果显示都是正确的。

步骤3:编写一个错误理解同步方法的测试用例ThreadSynFunction Test2类,如图6-19所示。

图6-19 没有正确理解synchronized方法示例

测试多线程时依然会出现同步问题。这是因为:程序第7~8行创建的两个线程对象t1和t2,使用的是不同的Save Money Task4对象。不同的Save Money Task4对象调用同步方法时申请加的是各自的this锁,并不是同一把锁,因此,各自的Save Money Task4对象可以同时调用saveMoney(1)方法,没有达到同步调用的效果。由于这两个Save Money Task4对象又存在操作共享资源Bank Account对象x,因此可能导致数据破坏,存在线程安全问题。

总之,一个同步方法在执行之前需要隐式地加锁。调用一个对象的同步实例方法要求给该对象加锁(锁对象是当前对象this)。调用一个类的同步静态方法要求对该类加锁(锁对象是当前类的Class对象)。

2.同步代码块

将整个方法声明为synchronized会使同步的范围覆盖整个方法,这会大大影响程序的并发效率。为了缩小同步范围,更精确地控制共享资源的操作,Java提供了同步代码块。(www.xing528.com)

通过synchronized关键字可以声明同步代码块,语法格式为:

syn Lock是一个锁对象,不仅可以用this锁,还可以用任何对象作为锁。当线程要执行同步代码块时,首先需要获得锁对象才有资格执行同步代码块中的语句。同步是一种高消耗的操作,在开发中应尽量使用同步代码块同步需要操作共享资源的代码。

【例6-8】

通过同步代码块替代例6-7的同步方法。

步骤1:在chapter6工程src文件夹下新建一个包cn.linaw.chapter6.demo06,然后将cn.linaw.chapter6.demo01包里的Bank Account.java拷贝过来。

步骤2:在包里定义一个MySyn Locks类,在类加载的时候提供若干静态final锁,如图6-20所示。

图6-20 定义MySyn Locks类

步骤3:编写一个新的存钱任务Save Money Task5.java,使用同步代码块操作共享资源,同步需要的锁对象由MySyn Locks类提供,如图6-21所示。

图6-21 存钱任务Save MoneyTask5类定义同步代码块

任何同步的实例方法都可以转换为同步代码块,synchronized(MySyn Locks.lock1)表示线程需要获得MySyn Locks.lock1锁对象才能运行代码块中的代码,否则需要等待。

步骤4:和测试同步方法一样,编写测试用例ThreadSynBlock Test1类,如图6-22所示。

图6-22 多线程访问共享资源Save MoneyTask5对象

main线程中创建并启动的2个存钱线程t1和t2,需要对共享资源Save Money Task5对象smt进行访问,多次测试结果显示,使用同步代码块可以解决多线程同步问题。

步骤5:和测试同步方法一样,编写测试用例ThreadSynBlock Test2类,如图6-23所示。

图6-23 多线程访问共享资源Bank Account对象

在本测试用例中,两个线程对象分别创建了一个Save Money Task5对象,这两个Save Money Task5对象操作同一个Bank Account对象x,存在操作共享资源x对象,需要考虑线程同步问题。本例中两个线程在执行同步代码块时面对的是同一把锁MySyn Locks.lock1,因此线程是安全的,共享数据没有被破坏。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈