简体   繁体   English

Java线程 - 同步问题

[英]Java Thread - Synchronization issue

From Sun's tutorial: 来自Sun的教程:

Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors: if an object is visible to more than one thread, all reads or writes to that object's variables are done through synchronized methods. 同步方法启用了一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。 (An important exception: final fields, which cannot be modified after the object is constructed, can be safely read through non-synchronized methods, once the object is constructed) This strategy is effective, but can present problems with liveness, as we'll see later in this lesson. (一个重要的例外:构造对象后无法修改的最终字段,一旦构造了对象,就可以通过非同步方法安全地读取)这种策略是有效的,但是可能会带来活性问题,因为我们将会请参阅本课后面的内容。

Q1. Q1。 Is the above statements mean that if an object of a class is going to be shared among multiple threads, then all instance methods of that class (except getters of final fields) should be made synchronized, since instance methods process instance variables? 上述语句是否意味着如果一个类的对象将在多个线程之间共享,那么该类的所有实例方法 (除了最终字段的getter)都应该同步,因为实例方法处理实例变量?

In order to understand concurrency in Java, I recommend the invaluable Java Concurrency in Practice . 为了理解Java中的并发性,我推荐了非常有价值的Java Concurrency in Practice

In response to your specific question, although synchronizing all methods is a quick-and-dirty way to accomplish thread safety, it does not scale well at all. 在回答您的具体问题时,尽管同步所有方法是实现线程安全的快捷方式,但它根本无法很好地扩展。 Consider the much maligned Vector class. 考虑备受诟病的Vector类。 Every method is synchronized, and it works terribly, because iteration is still not thread safe. 每个方法都是同步的,并且它的工作非常繁琐,因为迭代仍然不是线程安全的。

不是。这意味着同步方法是实现线程安全的一种方法,但它们不是唯一的方法,并且它们本身并不能保证在所有情况下都完全安全。

Not necessarily. 不必要。 You can synchronize (eg place a lock on dedicated object) part of the method where you access object's variables, for example. 例如,您可以同步(例如,锁定专用对象)访问对象变量的方法的一部分。 In other cases, you may delegate job to some inner object(s) which already handles synchronization issues. 在其他情况下,您可以将作业委派给已经处理同步问题的某些内部对象。
There are lots of choices, it all depends on the algorithm you're implementing. 有很多选择,这取决于您正在实施的算法。 Although, 'synchronized' keywords is usually the simplest one. 虽然'synchronized'关键字通常是最简单的。

edit 编辑
There is no comprehensive tutorial on that, each situation is unique. 没有全面的教程,每种情况都是独一无二的。 Learning it is like learning a foreign language: never ends :) 学习它就像学习一门外语:永远不会结束:)

But there are certainly helpful resources. 但肯定有用的资源。 In particular, there is a series of interesting articles on Heinz Kabutz's website. 特别是,Heinz Kabutz的网站上有一系列有趣的文章。
http://www.javaspecialists.eu/archive/Issue152.html (see the full list on the page) http://www.javaspecialists.eu/archive/Issue152.html (参见页面上的完整列表)

If other people have any links I'd be interested to see also. 如果其他人有任何链接,我也有兴趣看到。 I find the whole topic to be quite confusing (and, probably, most difficult part of core java), especially since new concurrency mechanisms were introduced in java 5. 我发现整个主题非常混乱(可能是核心java中最困难的部分),特别是因为java 5中引入了新的并发机制。

Have fun! 玩得开心!

In the most general form yes. 最一般的形式是。

Immutable objects need not be synchronized. 不可变对象不需要同步。

Also, you can use individual monitors/locks for the mutable instance variables (or groups there of) which will help with liveliness. 此外,您可以为可变实例变量(或其中的组)使用单独的监视器/锁定,这将有助于生动。 As well as only synchronize the portions where data is changed, rather than the entire method. 同时只同步数据更改的部分,而不是整个方法。

synchronized methodName vs synchronized( object ) synchronized methodName vs synchronized(object)

That's correct, and is one alternative. 这是正确的,是另一种选择。 I think it would be more efficient to synchronize access to that object only instead synchronize all it's methods. 我认为同步访问该对象只会同步其所有方法会更有效。

While the difference may be subtle, it would be useful if you use that same object in a single thread 虽然差异可能很微小,但如果您在单个线程中使用同一个对象,那将非常有用

ie ( using synchronized keyword on the method ) 即(在方法上使用synchronized关键字)

class SomeClass {
    private int clickCount  = 0;

    public synchronized void click(){
        clickCount++;
    }
 }

When a class is defined like this, only one thread at a time may invoke the click method. 当像这样定义一个类时,一次只有一个线程可以调用click方法。

What happens if this method is invoked too frequently in a single threaded app? 如果在单线程应用程序中过于频繁地调用此方法会发生什么? You'll spend some extra time checking if that thread can get the object lock when it is not needed. 您将花费一些额外的时间来检查该线程是否可以在不需要时获取对象锁定。

class Main {
    public static void main( String  [] args ) {
         SomeClass someObject = new SomeClass();
         for( int i = 0 ; i < Integer.MAX_VALUE ; i++ ) {
             someObject.click();
         }
    }
 }

In this case, the check to see if the thread can lock the object will be invoked unnecessarily Integer.MAX_VALUE ( 2 147 483 647 ) times. 在这种情况下,检查线程是否可以锁定对象将被不必要地调用Integer.MAX_VALUE (2 147 483 647)次。

So removing the synchronized keyword in this situation will run much faster. 因此,在这种情况下删除synchronized关键字将运行得更快。

So, how would you do that in a multithread application? 那么,您将如何在多线程应用程序中执行此操作?

You just synchronize the object: 您只需同步对象:

synchronized ( someObject ) {
    someObject.click();
}

Vector vs ArrayList 向量与ArrayList

As an additional note, this usage ( syncrhonized methodName vs. syncrhonized( object ) ) is, by the way, one of the reasons why java.util.Vector is now replaced by java.util.ArrayList . 另外请注意,这种用法( syncrhonized methodName与syncrhonized(object) )顺便说一下, java.util.Vector现在被java.util.ArrayList替换的原因之一。 Many of the Vector methods are synchronized. 许多Vector方法都是同步的。

Most of the times a list is used in a single threaded app or piece of code ( ie code inside jsp/servlets is executed in a single thread ), and the extra synchronization of Vector doesn't help to performance. 大多数情况下,列表用于单线程应用程序或代码段(即jsp / servlet中的代码在单个线程中执行),Vector的额外同步对性能没有帮助。

Same goes for Hashtable being replaced by HashMap HashtableHashMap取代也是如此

In fact getters a should be synchronized too or fields are to be made volatile . 事实上,getter a也应该同步,或者字段要变得volatile That is because when you get some value, you're probably interested in a most recent version of the value. 那是因为当你获得一些价值时,你可能会对这个价值的最新版本感兴趣。 You see, synchronized block semantics provides not only atomicity of execution (eg it guarantees that only one thread executes this block at one time), but also a visibility. 你看, synchronized块语义不仅提供了执行的原子性(例如,它保证了一次只有一个线程执行这个块),而且还提供了可见性。 It means that when thread enters synchronized block it invalidates its local cache and when it goes out it dumps any variables that have been modified back to main memory. 这意味着当线程进入synchronized块时,它会使其本地缓存无效,当它熄灭时,它会将已修改的所有变量转储回主内存。 volatile variables has the same visibility semantics. volatile变量具有相同的可见性语义。

No. Even getters have to be synchronized, except when they access only final fields. 不可以。即使是吸气剂也必须同步,除非他们只访问最终字段。 The reason is, that, for example, when accessing a long value, there is a tiny change that another thread currently writes it, and you read it while just the first 4 bytes have been written while the other 4 bytes remain the old value. 原因是,例如,当访问一个long值时,另一个线程当前正在写入一个微小的变化,并且只读了前4个字节而其他4个字节仍然是旧值时读取它。

Yes, that's correct. 对,那是正确的。 All methods that modify data or access data that may be modified by a different thread need to be synchronized on the same monitor. 修改数据或访问可能由不同线程修改的数据的所有方法都需要在同一监视器上同步。

The easy way is to mark the methods as synchronized. 简单的方法是将方法标记为同步。 If these are long-running methods, you may want to only synchronize that parts that the the reading/writing. 如果这些是长时间运行的方法,您可能只想同步读/写的那些部分。 In this case you would definie the monitor, along with wait() and notify(). 在这种情况下,您将定义监视器,以及wait()和notify()。

The simple answer is yes. 简单的答案是肯定的。 If an object of the class is going to be shared by multiple threads, you need to syncronize the getters and setters to prevent data inconsistency. 如果该类的对象将由多个线程共享,则需要同步getter和setter以防止数据不一致。 If all the threads would have seperate copy of object, then there is no need to syncronize the methods. 如果所有线程都具有单独的对象副本,则不需要同步方法。 If your instance methods are more than mere set and get, you must analyze the threat of threads waiting for a long running getter/setter to finish. 如果您的实例方法不仅仅是设置和获取,您必须分析等待长时间运行的getter / setter完成的线程的威胁。

You could use synchronized methods, synchronized blocks, concurrency tools such as Semaphore or if you really want to get down and dirty you could use Atomic References. 您可以使用同步方法,同步块,并发工具(如Semaphore或者如果您真的想要使用,可以使用Atomic References。 Other options include declaring member variables as volatile and using classes like AtomicInteger instead of Integer . 其他选项包括将成员变量声明为volatile并使用类AtomicInteger而不是Integer

It all depends on the situation, but there are a wide range of concurrency tools available - these are just some of them. 这一切都取决于具体情况,但有很多可用的并发工具 - 这些只是其中的一部分。

Synchronization can result in hold-wait deadlock where two threads each have the lock of an object, and are trying to acquire the lock of the other thread's object. 同步可能导致hold-wait死锁,其中两个线程各自拥有一个对象的锁,并试图获取另一个线程对象的锁。

Synchronization must also be global for a class, and an easy mistake to make is to forget to synchronize a method. 同步对于类来说也必须是全局的,并且容易犯的错误是忘记同步方法。 When a thread holds the lock for an object, other threads can still access non synchronized methods of that object. 当一个线程持有一个对象的锁时,其他线程仍然可以访问该对象的非同步方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM