线程同步的方法有哪些代码块和同步方法的区别

让天下没有难学的技术
基本线程同步(五)使用Lock同步代码块
基本线程同步(五)使用Lock同步代码块
声明:本文是《 》的第二章,作者: Javier Fernández González
译者:许巧辉 校对:方腾飞
使用Lock同步代码块
Java提供另外的机制用来同步代码块。它比synchronized关键字更加强大、灵活。它是基于Lock接口和实现它的类(如ReentrantLock)。这种机制有如下优势:
它允许以一种更灵活的方式来构建synchronized块。使用synchronized关键字,你必须以结构化方式得到释放synchronized代码块的控制权。Lock接口允许你获得更复杂的结构来实现你的临界区。
Lock 接口比synchronized关键字提供更多额外的功能。新功能之一是实现的tryLock()方法。这种方法试图获取锁的控制权并且如果它不能获取该锁,是因为其他线程在使用这个锁,它将返回这个锁。使用synchronized关键字,当线程A试图执行synchronized代码块,如果线程B正在执行它,那么线程A将阻塞直到线程B执行完synchronized代码块。使用锁,你可以执行tryLock()方法,这个方法返回一个 Boolean值表示,是否有其他线程正在运行这个锁所保护的代码。
当有多个读者和一个写者时,Lock接口允许读写操作分离。
Lock接口比synchronized关键字提供更好的性能。
在这个指南中,你将学习如何通过锁来同步代码块和通过Lock接口及其实现者ReentrantLock类来创建临界区,实现一个程序来模拟打印队列。
这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。
如何做…
按以下步骤来实现的这个例子:
1.创建PrintQueue类,来实现打印队列。
public class PrintQueue {
2.声明一个Lock对象,并且使用ReentrantLock类的一个新对象来初始化它。
private final Lock queueLock=new ReentrantLock();
3.实现printJob()方法,它将接收Object对象作为参数,并且不会返回任何值。
public void printJob(Object document){
4.在printJob()方法内部,通过调用lock()方法来获取Lock对象的控制权。
queueLock.lock();
5.然后,包含以下代码来模拟文档的打印:
Long duration=(long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName()+ &:
PrintQueue: Printing a Job during &+(duration/1000)+
& seconds&);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
6.最后,通过调用unlock()方法来释放Lock对象的控制。
queueLock.unlock();
7.创建一个Job类,并指定它实现Runnable接口。
public class Job implements Runnable {
8.声明一个PrintQueue类的对象,并通过实现类(Job类)的构造器来初始化这个对象。
private PrintQueue printQ
public Job(PrintQueue printQueue){
this.printQueue=printQ
9.实现run()方法,它使用PrintQueue对象来发送一个打印任务。
public void run() {
System.out.printf(&%s: Going to print a document\n&, Thread.
currentThread().getName());
printQueue.printJob(new Object());
System.out.printf(&%s: The document has been printed\n&,
Thread.currentThread().getName());
10.通过创建类名为Main,且包括main()方法来实现这个示例的主类。
public class Main {
public static void main(String[] args) {
11.创建一个共享的PrintQueue对象。
PrintQueue printQueue=new PrintQueue();
12.创建10个Job对象,并且使用10个线程来运行它们。
Thread thread[]=new Thread[10];
for (int i=0; i&10; i++){
thread[i]=new Thread(new Job(printQueue),&Thread &+ i);
13.启动这10个线程。
for (int i=0; i&10; i++){
thread[i].start();
它是如何工作的…
从以下截图,你可以看到执行这个示例一部分的输出:
在 printJob()中,PrintQueue类是这个示例的关键所在。当我们通过锁来实现一个临界区并且保证只有一个执行线程能运行一个代码块,我们必 须创建一个ReentrantLock对象。在临界区的起始部分,我们必须通过使用lock()方法来获得锁的控制权。当一个线程A调用这个方法时,如果 没有其他线程持有这个锁的控制权,那么这个方法就会给线程A分配这个锁的控制权并且立即返回允许线程A执行这个临界区。否则,如果其他线程B正在执行由这 个锁控制的临界区,lock()方法将会使线程A睡眠直到线程B完成这个临界区的执行。
在临界区的尾部,我们必须使用unlock()方法来释放锁的控制权,允许其他线程运行这个临界区。如果你在临界区的尾部没有调用unlock()方法,那么其他正在等待该代码块的线程将会永远等待,造成 死锁情况。如果你在临界区使用try-catch代码块,别忘了在finally部分的内部包含unlock()方法的代码。
不止这些…
Lock 接口(和ReentrantLock类)包含其他方法来获取锁的控制权,那就是tryLock()方法。这个方法与lock()方法的最大区别是,如果一 个线程调用这个方法不能获取Lock接口的控制权时,将会立即返回并且不会使这个线程进入睡眠。这个方法返回一个boolean值,true表示这个线程 获取了锁的控制权,false则表示没有。
注释:考虑到这个方法的结果,并采取相应的措施,这是程序员的责任。如果这个方法返回false值,预计你的程序不会执行这个临界区。如果是这样,你可能会在你的应用程序中得到错误的结果。
ReentrantLock类也允许递归调用(锁的可重入性,译者注),当一个线程有锁的控制权并且使用递归调用,它延续了锁的控制权,所以调用lock()方法将会立即返回并且继续递归调用的执行。此外,我们也可以调用其他方法。
你必须要非常小心使用锁来避免死锁,这种情况发生在,当两个或两个以上的线程被阻塞等待将永远不会解开的锁。比如,线程A锁定Lock(X)而线程B锁定 Lock(Y)。如果现在,线程A试图锁住Lock(Y)而线程B同时也试图锁住Lock(X),这两个线程将无限期地被阻塞,因为它们等待的锁将不会被解开。请注意,这个问题的发生是因为这两个线程尝试以相反的顺序获取锁(译者注:锁顺序死锁)。在附录中,提供了一些很好的并发编程设计的建议,适当的设计并发应用程序,来避免这些死锁问题。
在第2章,基本线程同步中的同步方法指南
在第2章,基本线程同步中的在锁中使用多条件的指南
在第8章,测试并发应用程序中的监控Lock接口的指南
原创文章,转载请注明: 转载自本文链接地址:
许巧辉,目前在Letv练级,平时喜欢看书,关注Java并发
Latest posts by Snway ()
Related posts:
(4 votes, average: 4.00 out of 5)
Loading...
(window.slotbydup=window.slotbydup || []).push({
id: '980251',
container: s,
size: '300,80',
display: 'inlay-fix'JAVA学习:多线程中的同步问题解析
 作者: ciding 编辑:
  前面说到的第二个例子,通过synchronized块,指定获取对象锁来达到同步的目的。那有没有其它的方法,可以通过synchronized方法来实现呢?  根据同步的原理:如果能获取一个共享对象锁或类锁,及可实现同步。那么我们是不是可以通过共享一个类锁来实现呢?  是的,我们可以使用静态同步方法,根据静态方法的特性,它只允许类对象本身才可以调用,不能通过实例化一个类对象来调用。那么如果获得了这个静态方法的锁,也就是获得这个类锁,而这个类锁都是TestThread类锁,及达到了获取共享类锁的目的。  实现代码如下:<span style="color: #.package thread_&& <span style="color: #.&&<span style="color: #./**&&4. * 测试扩展Thread类实现的多线程程序&& 5. *&& 6. * @author ciding&& 7. * @createTime Dec 7, :25 AM&&8. *&&9. */&&<span style="color: #.public class TestThread extends Thread{&&&&<span style="color: #.&&&&private int&& <span style="color: #.&&&&&& <span style="color: #.&&&&public TestThread(int threadnum) {&&&&<span style="color: #.&&&&&&&&this.threadnum =&&&&<span style="color: #.&&&&}&& <span style="color: #.&&&&&& <span style="color: #.&&&&public static synchronized void staticTest(int threadnum) {&&&&<span style="color: #.&&&&&&&&for(int i = <span style="color: #;i&<span style="color: #00;i++){&&&&<span style="color: #.&&&&&&&&&&&&System.out.println(&NO.& + threadnum + &:& + i );&& <span style="color: #.&&&&&&&&}&&&&<span style="color: #.&&&&}&&&&<span style="color: #.&&<span style="color: #.&&&&public static void main(String[] args) throws Exception {&&&&<span style="color: #.&&&&&&&&for(int i=<span style="color: #; i&<span style="color: #; i++){&& <span style="color: #.&&&&&&&&&&&&new TestThread(i).start();&& <span style="color: #.&&&&&&&&&&&&Thread.sleep(<span style="color: #);&& <span style="color: #.&&&&&&&&}&& <span style="color: #.&&&&}&&&&<span style="color: #.&&&&&& <span style="color: #.&&&&@Override&&<span style="color: #.&&&&public void run(){&& <span style="color: #.&&&&&&&&staticTest(threadnum);&& <span style="color: #.&&&&}&& <span style="color: #.}&&&  运行结果略,与例二中一样。  以上的内容主要是说明两个问题:同步块与同步方法。  ①同步块:获取的对象锁是synchronized(flag)中的flag对象锁。  ②同步方法:获取的是方法所属的类对象,及类对象锁。  静态同步方法,由于多个线程都会共享,所以一定会同步。  而非静态同步方法,只有在单例模式下才会同步。  接下来说一说能在synchronized内部运行的wait方法、notify方法与notifyAll方法。  先看一段JDK API 1.6 文档的原话:  JDK API 1.6 文档 java.lang.Object类中的wait()方法:wait public final void wait() throws InterruptedException在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(<span style="color: #) 调用一样。 当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。 对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用: synchronized (obj) { while (&condition does not hold&) obj.wait(); ... // Perform action appropriate to condition } 此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify 方法。 抛出: IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。 InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。 另请参见: notify(), notifyAll() &  JDK API 1.6 文档 java.lang.Object类中的notify()方法:notify public final void notify() 唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。 此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者: 通过执行此对象的同步实例方法。 通过执行在此对象上进行同步的 synchronized 语句的正文。 对于 Class 类型的对象,可以通过执行该类的同步静态方法。 一次只能有一个线程拥有对象的监视器。 抛出: IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。 另请参见: notifyAll(), wait() &  JDK API 1.6 文档 java.lang.Object类中的notifyAll()方法:notifyAll public final void notifyAll()&&唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。 此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify 方法。 抛出: IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。 另请参见: notify(), wait() &  根据synchronized能够获取对象锁,及获取对象监视器,所以这几个方法在同步中利用也是天衣无缝。
第1页:第2页:
大学生分期购物销量榜
已有条评论
IT168企业级
正在努力加载文档,请稍等…C#多线程问题(从不同步的代码块中调用了对象同步方法。)
[问题点数:80分,结帖人hzhsky1985]
C#多线程问题(从不同步的代码块中调用了对象同步方法。)
[问题点数:80分,结帖人hzhsky1985]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
本帖子已过去太久远了,不再提供回复功能。线程同步代码块和同步方法的区别_百度知道
线程同步代码块和同步方法的区别
单理解,线程同步是多个线程会同时访问一个公共资源,从而产生竞态条件,因此需要用同步机制使每次同时仅有一个或有限个能访问共享资源,其他线程需要等待
知道智能回答机器人
我是知道站内的人工智能,可高效智能地为您解答问题。很高兴为您服务。
其他类似问题
为您推荐:
线程同步的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁23. 多线程(01)-线程与进程的区别、java中多线程的实现方式及区别、java线程的同步与死锁_图文_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
23. 多线程(01)-线程与进程的区别、java中多线程的实现方式及区别、java线程的同步与死锁
上传于||文档简介
&&j&#8203;a&#8203;v&#8203;a
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
下载文档到电脑,查找使用更方便
还剩11页未读,继续阅读
你可能喜欢}

我要回帖

更多关于 线程同步代码块 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信