CPL 2016, week 5 Inter-thread collaboration Oleg Batrashev Institute of Computer Science, Tartu, Estonia March 7, 2016
Overview Studied so far: 1. Inter-thread visibility : JMM 2. Inter-thread synchronization : locks and monitors 3. Thread management : executors, tasks, cancelation 4. Inter-thread communication : confinements, queues, back pressure Today: ◮ Inter-thread collaboration : actors, inboxes, state diagrams Next weeks: ◮ Asynchronous execution : callbacks, Pyramid of Doom, Java 8 promises. ◮ Performance considerations : asynchronous IO, Java 8 streams.
Inter-thread collaboration 98/121 - Escaping locking hell How to solve locking hell? ◮ threads travel through code layers (and back using callbacks) ◮ take locks spontaneously – difficult to reason, deadlock danger Net layer Middle GUI ◮ using producer/transducer/concumer model is a solution ◮ not exactly what is needed for an application, because ◮ it is lock-step execution – data stream processing, but ◮ we need to manage spontaneous or unexpected events: user clicks or network failures. ◮ Actor model is the next step.
Inter-thread collaboration 99/121 Actor model - Outline Inter-thread collaboration Actor model Message handling State vs message centric Inboxes Reasoning with actors Actor frameworks
Inter-thread collaboration 100/121 Actor model - Passive and active objects A passive object : ◮ does not have its thread ◮ any thread can enter it ◮ required to synchronized access to its data ◮ 99% of the objects in typical programs An active object : ◮ has its own thread ◮ does not allow external thread to run its methods ◮ does not need to synchronize access to its data ◮ communicates with external parts through messages ◮ even when used, not recognized as such by programmers (Swing)
Inter-thread collaboration 101/121 Actor model - Actor definition Actor is basicly an active object: 1. actor state (data), usually inside an object 2. dedicated thread – total single thread confinement ◮ data is accessed only from this thread 3. dedicated message queue ◮ the thread reads and processes messages – changes its state ◮ other threads put messages to the queue to communicate with the actor Actor take() queue send() process State modify
Inter-thread collaboration 102/121 Actor model - Simple actor example 1. Counter actor holds integer value, start at 0 2. Queue may hold messages inc , reset , print : ◮ inc – increase counter by one , reset – set value to zero public enum MType { INC , RESET , PRINT } private BlockingQueue <MType > queue = new LinkedBlockingQueue < >(); private int value = 0; public Counter () { new Thread(this ). start (); } public void send(MType message) { queue.put(message ); } public void run () { while (true) { // read and process messages MType msg = queue.take (); if (msg == MType.INC) value += 1; else if (msg == MType.RESET) value = 0; else if (msg == MType.PRINT) System.out.println(value ); } } public static void main(String [] args) { Counter cnt = new Counter (); // create the actor cnt.send(MType.INC ); cnt.send(MType.PRINT ); cnt.send(MType.RESET ); cnt.send(MType.PRINT ); }
Inter-thread collaboration 103/121 Actor model - More than a transducer Transducer from previous lecture also 1. runs in its own thread 2. reads messages from a queue 3. may have internal state Actor is the same, but they have different goals ◮ transducer is meant to be more data transforming entity ◮ actor is autonomous communicating entity ◮ e.g., it may tolerate communication problems and failures of other actors in the system For actor it is natural to have 1. communicate with many different actors 2. more complex internal state, e.g. including substates 3. have sophisticated message filtering and handling
Inter-thread collaboration 104/121 Actor model - Another actor example ◮ logging of messages (warnings,errors) in application is often moved to separate thread ◮ it requires writes to disc (or other IO) ◮ it is asynchronous and may be postponed ◮ logging actor ◮ state is ACTIVE and NOTACTIVE ◮ queue may contain LOG, START, and STOP messages ◮ LOG is ignored in NOTACTIVE state, otherwise log is written ◮ START and STOP switch between ACTIVE and NOTACTIVE ◮ any thread may add info with LOG message ◮ messages are put to the queue and written out by the actor thread (code examples will follow)
Inter-thread collaboration 105/121 Message handling - Outline Inter-thread collaboration Actor model Message handling State vs message centric Inboxes Reasoning with actors Actor frameworks
Inter-thread collaboration 106/121 Message handling - State-message matrix ◮ state-message matrix for the logging example messages/states ACTIVE NOTACTIVE START → ACTIVE LOG(text) print(text) STOP → NOTACTIVE ◮ empty cell is ignoring the message ◮ e.g. when service is not active, then STOP and LOG messages are ignored ◮ arrows show state transition ◮ could write if(state==.. && message.type==..) {..} There are two corner cases how to structure your code: 1. state-centric – if(state==) is outer condition 2. message-centric – if(message.type==) is outer condition
Inter-thread collaboration 107/121 Message handling - State vs message centric State centric handling ◮ for each state the messages that are handled can be easily spotted ◮ e.g. ACTIVE state handles LOG and STOP messages ◮ for each message the states where the message is handled cannot be easily spotted ◮ e.g. START is handled on NOTACTIVE only, but in case of longer code we have to go through all of it to verify this if (state == State.NOTACTIVE) { // first check state if (msg.type == MType.START) { state = State.ACTIVE; } } else if (state == State.ACTIVE) { if (msg.type == MType.LOG) { log (( Log)msg ); } else if (msg.type == MType.STOP) { state = State.NOTACTIVE; } }
Inter-thread collaboration 108/121 Message handling - State vs message centric Message centric handling ◮ opposite to the previous, it is easy to see in which states a message is handled ◮ e.g. LOG is handled only in ACTIVE state if (msg.type == MType.START) { if (state == State.NOTACTIVE) { state = State.ACTIVE; } } else if (msg.type == MType.LOG) { if (state == State.ACTIVE) { log (( Log)msg ); } } else if (msg.type == MType.STOP) { if (state == State.ACTIVE) { state = State.NOTACTIVE; } } ◮ the differenfce may be non-obvious on such small example, but it becomes cruicial for larger actors
Inter-thread collaboration 109/121 Message handling - State vs message centric Which one is better? ◮ depends on the particular case, for example S1 S2 S3 M1 e e e M2 a b M3 c ◮ message M1 should result in the same action for all states ◮ message-centric is better, because less code ◮ then M2 is handled in 2 states and M3 in 1 state ◮ mixing – combine both ways ◮ e.g. error message handling is a separate case, but the rest is state-centric
Inter-thread collaboration 110/121 Message handling - State vs message centric Methods as messages rewrite message-centric case into separate methods, e.g. public void startMsg () { send(new RunnableMessage (new Runnable () { public void run () { if (state == State.NOTACTIVE) { state = State.ACTIVE; } } })); } ◮ nicer call syntax, as if method calls ◮ cumbersome handler syntax ◮ loosing ability to do state-centric message handling Swing is a sort of message-centric handling, however without explicit methods.
Inter-thread collaboration 111/121 Message handling - Inboxes Message guards and priorities Inbox is a message queue with more sophisticated message handling (idea from Erlang): ◮ guards – apply additional constraints on what messages are handled ◮ e.g. do not print duplicates: if(lastText!=msg.text) ◮ coneptually nothing new, just another condition on message handling ◮ priorities – as with priority queues, messages are not handled in FIFO order ◮ e.g. START/STOP messages may have higher priority than LOG ◮ implement with priority queue or 2 queues and own synchronizer
Inter-thread collaboration 112/121 Message handling - Inboxes Postponed delivery and timeout Postponed delivery may have two meanings: 1. message is not discarded if cannot be handled right now, e.g. ◮ SEND(data) message during CONNECTING phase ◮ CONNECT message during DISCONNECTING phase 2. message delivery is scheduled in the future ◮ CONNECT is scheduled in 5 seconds after DISCONNECTED event Timeout is set when waiting for a new message in the queue ◮ if matching message has not arrived – execute special code ◮ e.g. waiting for AUTH_SUCCESSFUL for 5 seconds from a child actor ◮ non-matched messages may be left in the queue Make sure actor state is not accessed from timer thread directly!
Inter-thread collaboration 113/121 Reasoning with actors - Outline Inter-thread collaboration Actor model Message handling State vs message centric Inboxes Reasoning with actors Actor frameworks
Recommend
More recommend