Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jürgen Höller on Concurrency

Jürgen Höller on Concurrency

More Decks by Enterprise Java User Group Austria

Other Decks in Technology

Transcript

  1. Common Myths • It's too hard for a regular developer

    to make shared objects thread-safe. • Concurrent programming is all about using the 'synchronized' keyword. • Shared objects are not allowed to contain 'state'. • Setter injection has a problem with visibility of variables. • Pooling is a relevant option for application objects.
  2. Thread Safety (1) • First of all, what does "thread-safe"

    really mean? – What we usually mean is that there are no unintended side effects when reentrant method calls come into the same instance. • What guarantees does the Java runtime provide already? – Local variables within a method are always fully isolated between calls and hence between threads. – Modifications to instance variable state will affect shared state for all callers. • But: the problem of 'visibility' between threads… • Also called "happens-before" relationship…
  3. Thread Safety (2) public class OrderService { private int orderCount;

    private int itemCount; public void processOrder(Order order) { … this.orderCount++; this.itemCount += order.getItemCount(); } }
  4. Thread Safety (3) • Danger: In a concurrent environment, modifications

    to shared variable state will be performed in a processor's register and not in main memory. – Publishing the modification back to main memory will happen eventually • but not immediately due to a processor's memory caching – Other threads will pick up the current state from main memory – once it is there… • might see stale values in the meantime • The application can enforce immediate publication of state changes – through using a memory barrier ('synchronized') – or through using the 'volatile' keyword on the variable – 'final' variables are safely visible to all threads as well
  5. Thread Safety (4) public class OrderService { private final Object

    statisticsMonitor = new Object(); private int orderCount; private int itemCount; public void processOrder(Order order) { … synchronized (this.statisticsMonitor) { this.orderCount++; this.itemCount += order.getItemCount(); } } }
  6. Thread Safety (5) • Some recommendations – Mark instance variables

    as 'final' as far as possible • accept and/or calculate values on construction – When modifying shared instance variables, use either 'volatile' or synchronization. – For modifying complex object state, always use synchronization. • Otherwise the nested contents of your object might not be fully visible when you assign the reference. – Synchronization is quite cheap on modern VMs. • Don't optimize prematurely by trying to avoid synchronization where it doesn't matter. • Of course, don't over-use synchronization where it isn't necessary either.
  7. Thread Safety (6) • Shared Collections or Maps – Plain

    ArrayList, HashSet, HashMap are not thread- safe – Collections.synchronizedList/Set/Map is quite costly • always locks the entire collection, even just for simple retrieval operations • not very recommendable on Java 5 and above anymore – Modern solution in java.util.concurrent: CopyOnWriteArrayList/Set and ConcurrentHashMap • optimized for frequent reads and less frequent writes • ConcurrentHashMap locks specific hash buckets only instead of the entire Map
  8. Thread Safety (7) public class OrderService { private final Map<String,

    Country> countryCache = new ConcurrentHashMap<String, Country>(); public void processOrder(Order order) { String countryCode = order.getCountryCode(); Country country = this.countryCache.get(); if (country == null) { country = new Country(countryCode); this.countryCache.put(countryCode, country); } … } }
  9. Kinds of State (1) • Now, which state to hold

    in shared instance variables in the first place? – e.g. in Spring singletons or in Servlets • NO: – clearly not user-specific state • from the perspective of a user, such shared objects are 'stateless' • user-specific or request-specific state to be passed in as method arguments only – no single-threaded resources either • such as JDBC Connections or Hibernate Sessions
  10. Kinds of State (2) • Now, which state to hold

    in shared instance variables in the first place? – e.g. in Spring singletons or in Servlets • YES: – thread-safe resource factories • such as JDBC DataSources – generally: configuration state • also settings, flags, etc • to be covered in more detail in a minute – also: counters, statistics, etc • use synchronization accordingly
  11. Kinds of State (3) • How to set configuration state

    into a shared object? – constructors ("constructor injection") – bean properties ("setter injection") – annotation-driven method/field injection • Constructors have the advantage of being able to set 'final' fields – state safely visible to any thread – no accidental modification possible – e.g. a final DataSource field in a DAO • Bean properties and config methods/fields can effectively only be populated in a second step
  12. Kinds of State (4) public class OrderService { private final

    OrderRepository orderRepository; public OrderService(OrderRepository repo) { this.orderRepository = repo; } public void processOrder(Order order) { … this.orderRepository.storeOrder(order); } }
  13. Kinds of State (5) • What about setter injection? –

    can't use 'final' variables – unclear visibility situation – potentially repeated calls • Need to differentiate between configuration- time setters and runtime setters – configuration time: will only be called before the object is put into service • i.e. before the object's service methods will be called – runtime: can be called while the object is in service • e.g. through JMX
  14. Kinds of State (6) public class OrderService { private OrderRepository

    orderRepository; public void setRepository(OrderRepository repo) { this.orderRepository = repo; } public void processOrder(Order order) { … this.orderRepository.storeOrder(order); } }
  15. Kinds of State (7) • "Thread-safe publishing" – putting an

    object into service only after its configuration-time setters have been called – only exposing it to the public at that time • Using synchronization for the configuration + publishing step enforces happens-before – between the object's creation phase and the service phase – all of its configuration state guaranteed to be visible in the service phase • This effectively makes setter injection as reliable as constructor injection – since frameworks such as Spring usually perform that level of synchronization under the hood
  16. Kinds of State (8) • Runtime setters – as used

    for JMX and other forms of management – need to use synchronization when modifying the underlying instance variable(s) – consider using 'volatile' for simple types of state • such as String or Integer • Make sure that you're accessing such state in a thread-safe way as well – using synchronization on the same monitor – no problem when using 'volatile' which applies both ways anyway
  17. Kinds of State (9) • Keep it simple for configuration

    state – Constructor injection: use 'final' • no synchronization or 'volatile' necessary – Setter injection: plain instance variables • no synchronization or 'volatile' necessary either • at least when managed by a framework • Use synchronization for runtime state – e.g. caches: use ConcurrentHashMap – e.g. statistics: use 'synchronized' – e.g. attributes through setters: use 'volatile'
  18. Scoped Objects (1) • What about objects in other scopes:

    request, session, conversation? – that's dependent on the specific scope… • Request scope is generally only ever accessed by a single thread at the same time – no precautions to take for concurrent access • Session scope and conversation scope may be accessed from several requests at the same time – consider AJAX and multiple browser windows – same precautions to take as for singleton objects
  19. Scoped Objects (2) • Generally, design your scoped objects such

    that they are fully thread-safe – be prepared for concurrent access – it's usually simple enough • Modern VMs optimize synchronization heavily if an object is proven to never leave the current thread – effectively removing synchronization at runtime if proven to be unnecessary – very useful if the same class will be used in shared as well as non-shared scenarios
  20. Scoped Objects (3) • What about pooling? – Pooled objects

    are different from scoped objects • can't obtain the same instance when performing retrieval in the same contextual 'scope' – Pooling effectively locks the target instance for each method invocation • keep multiple objects in the pool in order to remain scalable • Pooling doesn't make a difference for configuration state – e.g. each pooled object referring to same JDBC DataSource – doesn't make sense for counter, statistics, etc either • You would have individual counters in each pooled object! – possibly keep a JDBC Connection or Hibernate Session in an instance variable? • Prefer the use of DataSource or SessionFactory's getCurrentSession(), also for transaction participation.
  21. Scoped Objects (4) • If you feel that you need

    pooled application objects, reconsider your needs… – For simple resource accessors, use 'singleton' scope and reobtain the current resource (e.g. Hibernate Session) for every call. • Underlying resource factory reuses resources within transactions anyway (using a thread-local). • Underlying physical resources (e.g. JDBC Connections) will get pooled by the resource factory already. – For throwaway objects, consider the use of 'prototype' scope or 'request' scope. • new instance on every retrieval or for every request
  22. Scoped Objects (5) • For longer-lived objects, consider 'session' or

    'conversation' scope – Remember: You'll have to make session-scoped objects thread-safe as well. – can keep user-specific / session-specific state next to a long-lived Hibernate Session, for example • What about pooling for the purpose of limiting access to your component? – that typically happens at the system entry point level • e.g. limited thread pool attached to HTTP socket – or at the resource factory level • e.g. limited amount of JDBC Connections in DataSource – or at the interceptor level • e.g. Spring's ConcurrencyThrottleInterceptor