I'm trying to create a small class that will allow me to facilitate a communication between two threads.
Those threads most probably will outlive the context in which the above mentioned class was created as they are queued onto a thread pool.
What I have tried so far (on coliru as well):
class A
{
public:
A(int maxVal) : maxValue(maxVal) {}
bool IsOverMax() const { return cur >= maxValue; }
void Increase() { cur++; }
private:
const int maxValue;
atomic_int cur{ 0 };
};
possible usage:
void checking(const shared_ptr<A> counter)
{
while(!counter->IsOverMax())
{
cout<<"Working\n"; // do work
std::this_thread::sleep_for(10ms);
}
}
void counting(shared_ptr<A> counter)
{
while (!counter->IsOverMax())
{
cout<<"Counting\n";
counter->Increase(); // does this fall under `...uses a non-const member function of shared_ptr then a data race will occur`? http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
std::this_thread::sleep_for(9ms);
}
}
int main()
{
unique_ptr<thread> t1Ptr;
unique_ptr<thread> t2Ptr;
{
auto aPtr = make_shared<A>(100); // This might be out of scope before t1 and t2 end
t1Ptr.reset(new thread(checking, aPtr)); // To simbolize that t1,t2 will outlive the scope in which aPtr was originaly created
t2Ptr.reset(new thread(counting, aPtr));
}
t2Ptr->join();
t1Ptr->join();
//cout<< aPtr->IsOverMax();
}
The reason I'm concerned is that the documentation says that:
If multiple threads of execution access the same std::shared_ptr object without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur unless all such access is performed through these functions, which are overloads of the corresponding atomic access functions (std::atomic_load, std::atomic_store, etc.)
So Increase is a non const function, are the copies of aPtr are the same std::shared_ptr for this context or not ?
Is this code thread-safe?
Would this be OK for a non atomic object (say using an std::mutex to lock around reads and writes to a regular int)?
In any case why?
So Increase is a non const function, are the copies of aPtr are the same std::shared_ptr for this context or not ?
At std::thread creation, aPtr is passed by value. Therefore, it is guaranteed that:
You don't introduce a data race since each thread gets its own instance of shared_ptr (although they manage the same object A).
The documentation you are referring to describes a scenario whereby multiple threads operate on the same shared_ptr instance.
In that case, only const member functions can be called (see below), or synchronization is required.
shared_ptr reference-count is incremented before aPtr goes out of scope in main
So yes, this is a correct way to use shared_ptr.
Is this code thread-safe?
Your code does not introduce a data race, neither with access to shared_ptr instances, nor with access to the managed object A.
This means that there are no conflicting, non-atomic, read and write operations to the same memory location performed by multiple threads.
However, keep in mind that, in checking(), the call to IsOverMax() is separated from the actual work that follows
(Increase() could be called by the second thread after IsOverMax() but before 'do work'). Therefore, you could 'do work' while cur has gone over its maximum.
Whether or not that is a problem depends on your specification, but it is called a race condition which is not necessarily a programming error (unlike a data race which causes undefined behavior).
Would this be OK for a non atomic object (say using an std::mutex to lock around reads and writes to a regular int)?
cur can be a regular int (non-atomic) if you protect it with a std::mutex. The mutex must be locked for both write and read access in order to prevent a data race.
One remark on calling const member functions on objects shared by multiple threads.
The use of const alone does not guarantee that no data race is introduced.
In this case, the guarantee applies to shared_ptr const member functions, because the documentation says so.
I cannot find in the C++ standard whether that guarantee applies to all const member functions in the Standard Library
That documentation is talking about the member functions of shared_ptr, not the member functions of your class. Copies of shared_ptr objects are different objects.
I believe the code is thread safe, because the only changing variable written and read on different threads is cur, and that variable is atomic.
If cur was not atomic and access to it in both Increase() and IsOverMax() was protected by locking a std::mutex, that code would also be thread safe.
If I set the value of a variable in one thread and read it in another, I protect it with a lock to ensure that the second thread reads the value most recently set by the first:
Thread 1:
lock();
x=3;
unlock();
Thread 2:
lock();
<use the value of x>
unlock();
So far, so good. However, suppose I have a c++ object that sets the value of x in an initializer:
theClass::theClass() : x(3) ...
theClass theInstance;
Then, I spawn a thread that uses theInstance. Is there any guarantee that the newly spawned thread will see the proper value of x? Or is it necessary to place a lock around the declaration of theInstance? I am interested primarily in c++ on Linux.
Prior to C++11, the C++ standard had nothing to say about multiple threads of execution and so made no guarantees of anything.
C++11 introduced a memory model that defines under what circumstances memory written on one thread is guaranteed to become visible to another thread.
Construction of an object is not inherently synchronized across threads. In your particular case though, you say you first construct the object and then 'spawn a thread'. If you 'spawn a thread' by constructing an std::thread object and you do it after constructing some object x on the same thread then you are guaranteed to see the proper value of x on the newly spawned thread. This is because the completion of the thread constructor synchronizes-with the beginning of your thread function.
The term synchronizes-with is a specific term used in defining the C++ memory model and it's worth understanding exactly what it means to understand more complex synchronization but for the case you outline things 'just work' without needing any additional synchronization.
This is all assuming you're using std::thread. If you're using platform threading APIs directly then the C++ standard has nothing to say about what happens but in practice you can assume it will work without needing a lock on any platform I know of.
You seem to have a misconception on locks:
If I set the value of a variable in one thread and read it in another,
I protect it with a lock to ensure that the second thread reads the
value most recently set by the first.
This is incorrect. Locks are used to prevent data races. Locks do not schedule the instructions of Thread 1 to happen before the instructions of Thread 2. With your lock in place, Thread 2 can still run before Thread 1 and read the value of x before Thread 1 changes the value of x.
As for your question:
If your initialization of theInstance happens-before the initialization/start of a certain thread A, then thread A is guaranteed to see the proper value of x.
Example
#include <thread>
#include <assert.h>
struct C
{
C(int x) : x_{ x } {}
int x_;
};
void f(C const& c)
{
assert(c.x_ == 42);
}
int main()
{
C c{ 42 }; // A
std::thread t{ f, std::ref(c) }; // B
t.join();
}
In the same thread: A is sequenced-before B, therefore A happens-before B. The assert in thread t will thus never fire.
If your initialization of 'theInstance' inter-thread happens-before its usage by a certain thread A, then thread A is guaranteed to see the proper value of x.
Example
#include <thread>
#include <atomic>
#include <assert.h>
struct C
{
int x_;
};
std::atomic<bool> is_init;
void f0(C& c)
{
c.x_ = 37; // B
is_init.store(true); // C
}
void f1(C const& c)
{
while (!is_init.load()); // D
assert(c.x_ == 37); // E
}
int main()
{
is_init.store(false); // A
C c;
std::thread t0{ f0, std::ref(c) };
std::thread t1{ f1, std::ref(c) };
t0.join();
t1.join();
}
The inter-thread happens-before relationship occurs between t0 and t1. As before, A happens-before the creation of threads t0 and t1.
The assignment c.x_ = 37 (B) happens-before the store to the is_init flag (C). The loop in f1 is the source of the inter-thread happens-before relationship: f1 only proceeds once is_init is set, therefore C happens before E. Since these relationships are transitive, B inter-thread happens-before D. Thus, the assert will never fire in f1.
First of all, your example above doesn't warrant any locks. All you need to do is to declare your variable atomic. No locks, no worries.
Second, your question does not really make a lot of sence. Since you can not use your object (instance of the class) before it is constructed, and construction is happening within single thread, there is no need to lock anything which is done in class constructor. You simply can not access non-constructed class from multiple threads, it is impossible.
I have code listed here: Threading and Sockets.
The answer to that question was to modify isListening with volatile. As I remarked, that modifier allowed me to access the variable from another thread. After reading MSDN, I realized that I was reading isListening from the following newly created thread process.
So, my questions now:
Is volatile the preferred method,since I am basically making a non-thread safe request on a variable? I have read about the Interlocked class and wondered if this was something that would be better to use in my code. Interlocked looks similar to what lock(myObj) is doing - but with a little more 'flair' and control. I do know that simply applying a lock(myObj) code block around isListening did not work.
Should I implement the Interlocked class?
Thank you for your time and responses.
If all you are doing is reading and writing a variable across multiple threads in C#, then you do not have to worry about synchronizing access to (locking) that variable providing its type is bool, char, byte, sbyte, short, ushort, int, uint, float, and reference types. See here for details.
In the example from your other post, the reason you have to mark the field as volatile is to ensure that it is not subject to compiler optimizations and that the most current value is present in the field at all times. See here for details on the volatile keyword. Doing this allows that field to be read and written across threads without having to lock (synchronize access to) it. But keep in mind, the volatile keyword can only be used for your field because it is of type bool. Had it been a double, for example, the volatile keyword wouldn't work, and you'd have to use a lock.
The Interlocked class is used for a specialized purpose, namely incrementing, decrementing, and exchanging values of (typically) numeric types. These operations are not atomic. For example, if you are incrementing a value in one thread and trying to read the resulting value in another thread, you would normally have to lock the variable to prevent reading intermediate results. The Interlocked class simply provides some convenience functions so you don't have to lock the variable yourself while the increment operation is performed.
What you are doing with the isListening flag does not require use of the Interlocked class. Marking the field as volatile is sufficient.
Edit due to lunchtime rushed answer..
The lock statement used in your previous code is locking an object instance that is created in the scope of a method so it will have no effect on another thread calling into the same method. Each thread must be able to lock the same instance of an object in order to synchronise access to the given block of code. One way to do this (depending on the semantics you require) is to make the locking object a private static variable of the class that it is used in. This will allow multiple instances of a given object to synchronise access to a block of code or a single shared resource. If synchronisation is required for individual instances of an object or a resource that is instance specific then static should be emitted.
Volatile doesn't guarantee that reads or writes to the given variable will be atomic amongst different threads. It is a compiler hint to preserve ordering of instructions and prevents the variable from being cached inside a register. In general unless you are working on something extremely performance sensitive (low locking / lock free algorithms, data structures etc.) or really know you are doing then I would opt for using Interlocked. The performance difference between using volatile / interlocked / lock in most applications will be neglible, so if you are unsure its best to use what ever gives you the safest guarantee (read Joe Duffy's blog & book).
For example using volatile in the example below is not thread safe and the incremented counter does not reach 10,000,000 (when I ran the test it reached 8848450) . This is because volatile only guarentees reading the latest value (e.g. not cached from a register for example). When using interlocked the operation is thread safe and the counter does reach 10,000,000.
public class Incrementor
{
private volatile int count;
public int Count
{
get { return count; }
}
public void UnsafeIncrement()
{
count++;
}
public void SafeIncrement()
{
Interlocked.Increment(ref count);
}
}
[TestFixture]
public class ThreadingTest
{
private const int fiveMillion = 5000000;
private const int tenMillion = 10000000;
[Test]
public void UnsafeCountShouldNotCountToTenMillion()
{
const int iterations = fiveMillion;
Incrementor incrementor = new Incrementor();
Thread thread1 = new Thread(() => UnsafeIncrement(incrementor, iterations));
Thread thread2 = new Thread(() => UnsafeIncrement(incrementor, iterations));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Assert.AreEqual(tenMillion, incrementor.Count);
}
[Test]
public void SafeIncrementShouldCountToTenMillion()
{
const int iterations = fiveMillion;
Incrementor incrementor = new Incrementor();
Thread thread1 = new Thread(() => SafeIncrement(incrementor, iterations));
Thread thread2 = new Thread(() => SafeIncrement(incrementor, iterations));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Assert.AreEqual(tenMillion, incrementor.Count);
}
private void UnsafeIncrement(Incrementor incrementor, int times)
{
for (int i =0; i < times; ++i)
incrementor.UnsafeIncrement();
}
private void SafeIncrement(Incrementor incrementor, int times)
{
for (int i = 0; i < times; ++i)
incrementor.SafeIncrement();
}
}
If you search for 'interlocked volatile' you will find a number of answers to your question. The one below for example addresses your question:
A simple example below shows
Volatile vs. Interlocked vs. lock
"One way to do this is to make the locking object a private static variable of the class that it is used in."
Why should it be static? You can access the same function from multiple threads as long as they work on different object. I am not saying that it would not work, but would seriously slow the speed of the application without any advantages. Or am I missing something?
And here is what MSDN says about volatiles:
"Also, when optimizing, the compiler must maintain ordering among references to volatile objects as well as references to other global objects. In particular,
A write to a volatile object (volatile write) has Release semantics; a reference to a global or static object that occurs before a write to a volatile object in the instruction sequence will occur before that volatile write in the compiled binary.
A read of a volatile object (volatile read) has Acquire semantics; a reference to a global or static object that occurs after a read of volatile memory in the instruction sequence will occur after that volatile read in the compiled binary.
This allows volatile objects to be used for memory locks and releases in multithreaded applications."
When writing multithreaded applications, one of the most common problems experienced is race conditions.
My questions to the community are:
What is the race condition?
How do you detect them?
How do you handle them?
Finally, how do you prevent them from occurring?
A race condition occurs when two or more threads can access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any time, you don't know the order in which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are "racing" to access/change the data.
Problems often occur when one thread does a "check-then-act" (e.g. "check" if the value is X, then "act" to do something that depends on the value being X) and another thread does something to the value in between the "check" and the "act". E.g:
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
The point being, y could be 10, or it could be anything, depending on whether another thread changed x in between the check and act. You have no real way of knowing.
In order to prevent race conditions from occurring, you would typically put a lock around the shared data to ensure only one thread can access the data at a time. This would mean something like this:
// Obtain lock for x
if (x == 5)
{
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
}
// release lock for x
A "race condition" exists when multithreaded (or otherwise parallel) code that would access a shared resource could do so in such a way as to cause unexpected results.
Take this example:
for ( int i = 0; i < 10000000; i++ )
{
x = x + 1;
}
If you had 5 threads executing this code at once, the value of x WOULD NOT end up being 50,000,000. It would in fact vary with each run.
This is because, in order for each thread to increment the value of x, they have to do the following: (simplified, obviously)
Retrieve the value of x
Add 1 to this value
Store this value to x
Any thread can be at any step in this process at any time, and they can step on each other when a shared resource is involved. The state of x can be changed by another thread during the time between x is being read and when it is written back.
Let's say a thread retrieves the value of x, but hasn't stored it yet. Another thread can also retrieve the same value of x (because no thread has changed it yet) and then they would both be storing the same value (x+1) back in x!
Example:
Thread 1: reads x, value is 7
Thread 1: add 1 to x, value is now 8
Thread 2: reads x, value is 7
Thread 1: stores 8 in x
Thread 2: adds 1 to x, value is now 8
Thread 2: stores 8 in x
Race conditions can be avoided by employing some sort of locking mechanism before the code that accesses the shared resource:
for ( int i = 0; i < 10000000; i++ )
{
//lock x
x = x + 1;
//unlock x
}
Here, the answer comes out as 50,000,000 every time.
For more on locking, search for: mutex, semaphore, critical section, shared resource.
What is a Race Condition?
You are planning to go to a movie at 5 pm. You inquire about the availability of the tickets at 4 pm. The representative says that they are available. You relax and reach the ticket window 5 minutes before the show. I'm sure you can guess what happens: it's a full house. The problem here was in the duration between the check and the action. You inquired at 4 and acted at 5. In the meantime, someone else grabbed the tickets. That's a race condition - specifically a "check-then-act" scenario of race conditions.
How do you detect them?
Religious code review, multi-threaded unit tests. There is no shortcut. There are few Eclipse plugin emerging on this, but nothing stable yet.
How do you handle and prevent them?
The best thing would be to create side-effect free and stateless functions, use immutables as much as possible. But that is not always possible. So using java.util.concurrent.atomic, concurrent data structures, proper synchronization, and actor based concurrency will help.
The best resource for concurrency is JCIP. You can also get some more details on above explanation here.
There is an important technical difference between race conditions and data races. Most answers seem to make the assumption that these terms are equivalent, but they are not.
A data race occurs when 2 instructions access the same memory location, at least one of these accesses is a write and there is no happens before ordering among these accesses. Now what constitutes a happens before ordering is subject to a lot of debate, but in general ulock-lock pairs on the same lock variable and wait-signal pairs on the same condition variable induce a happens-before order.
A race condition is a semantic error. It is a flaw that occurs in the timing or the ordering of events that leads to erroneous program behavior.
Many race conditions can be (and in fact are) caused by data races, but this is not necessary. As a matter of fact, data races and race conditions are neither the necessary, nor the sufficient condition for one another. This blog post also explains the difference very well, with a simple bank transaction example. Here is another simple example that explains the difference.
Now that we nailed down the terminology, let us try to answer the original question.
Given that race conditions are semantic bugs, there is no general way of detecting them. This is because there is no way of having an automated oracle that can distinguish correct vs. incorrect program behavior in the general case. Race detection is an undecidable problem.
On the other hand, data races have a precise definition that does not necessarily relate to correctness, and therefore one can detect them. There are many flavors of data race detectors (static/dynamic data race detection, lockset-based data race detection, happens-before based data race detection, hybrid data race detection). A state of the art dynamic data race detector is ThreadSanitizer which works very well in practice.
Handling data races in general requires some programming discipline to induce happens-before edges between accesses to shared data (either during development, or once they are detected using the above mentioned tools). this can be done through locks, condition variables, semaphores, etc. However, one can also employ different programming paradigms like message passing (instead of shared memory) that avoid data races by construction.
A sort-of-canonical definition is "when two threads access the same location in memory at the same time, and at least one of the accesses is a write." In the situation the "reader" thread may get the old value or the new value, depending on which thread "wins the race." This is not always a bug—in fact, some really hairy low-level algorithms do this on purpose—but it should generally be avoided. #Steve Gury give's a good example of when it might be a problem.
A race condition is a situation on concurrent programming where two concurrent threads or processes compete for a resource and the resulting final state depends on who gets the resource first.
A race condition is a kind of bug, that happens only with certain temporal conditions.
Example:
Imagine you have two threads, A and B.
In Thread A:
if( object.a != 0 )
object.avg = total / object.a
In Thread B:
object.a = 0
If thread A is preempted just after having check that object.a is not null, B will do a = 0, and when thread A will gain the processor, it will do a "divide by zero".
This bug only happen when thread A is preempted just after the if statement, it's very rare, but it can happen.
Many answers in this discussion explains what a race condition is. I try to provide an explaination why this term is called race condition in software industry.
Why is it called race condition?
Race condition is not only related with software but also related with hardware too. Actually the term was initially coined by the hardware industry.
According to wikipedia:
The term originates with the idea of two signals racing each other to
influence the output first.
Race condition in a logic circuit:
Software industry took this term without modification, which makes it a little bit difficult to understand.
You need to do some replacement to map it to the software world:
"two signals" ==> "two threads"/"two processes"
"influence the output" ==> "influence some shared state"
So race condition in software industry means "two threads"/"two processes" racing each other to "influence some shared state", and the final result of the shared state will depend on some subtle timing difference, which could be caused by some specific thread/process launching order, thread/process scheduling, etc.
Race conditions occur in multi-threaded applications or multi-process systems. A race condition, at its most basic, is anything that makes the assumption that two things not in the same thread or process will happen in a particular order, without taking steps to ensure that they do. This happens commonly when two threads are passing messages by setting and checking member variables of a class both can access. There's almost always a race condition when one thread calls sleep to give another thread time to finish a task (unless that sleep is in a loop, with some checking mechanism).
Tools for preventing race conditions are dependent on the language and OS, but some comon ones are mutexes, critical sections, and signals. Mutexes are good when you want to make sure you're the only one doing something. Signals are good when you want to make sure someone else has finished doing something. Minimizing shared resources can also help prevent unexpected behaviors
Detecting race conditions can be difficult, but there are a couple signs. Code which relies heavily on sleeps is prone to race conditions, so first check for calls to sleep in the affected code. Adding particularly long sleeps can also be used for debugging to try and force a particular order of events. This can be useful for reproducing the behavior, seeing if you can make it disappear by changing the timing of things, and for testing solutions put in place. The sleeps should be removed after debugging.
The signature sign that one has a race condition though, is if there's an issue that only occurs intermittently on some machines. Common bugs would be crashes and deadlocks. With logging, you should be able to find the affected area and work back from there.
Microsoft actually have published a really detailed article on this matter of race conditions and deadlocks. The most summarized abstract from it would be the title paragraph:
A race condition occurs when two threads access a shared variable at
the same time. The first thread reads the variable, and the second
thread reads the same value from the variable. Then the first thread
and second thread perform their operations on the value, and they race
to see which thread can write the value last to the shared variable.
The value of the thread that writes its value last is preserved,
because the thread is writing over the value that the previous thread
wrote.
What is a race condition?
The situation when the process is critically dependent on the sequence or timing of other events.
For example,
Processor A and processor B both needs identical resource for their execution.
How do you detect them?
There are tools to detect race condition automatically:
Lockset-Based Race Checker
Happens-Before Race Detection
Hybrid Race Detection
How do you handle them?
Race condition can be handled by Mutex or Semaphores. They act as a lock allows a process to acquire a resource based on certain requirements to prevent race condition.
How do you prevent them from occurring?
There are various ways to prevent race condition, such as Critical Section Avoidance.
No two processes simultaneously inside their critical regions. (Mutual Exclusion)
No assumptions are made about speeds or the number of CPUs.
No process running outside its critical region which blocks other processes.
No process has to wait forever to enter its critical region. (A waits for B resources, B waits for C resources, C waits for A resources)
You can prevent race condition, if you use "Atomic" classes. The reason is just the thread don't separate operation get and set, example is below:
AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);
As a result, you will have 7 in link "ai".
Although you did two actions, but the both operation confirm the same thread and no one other thread will interfere to this, that means no race conditions!
I made a video that explains this.
Essentially it is when you have a state with is shared across multiple threads and before the first execution on a given state is completed, another execution starts and the new thread’s initial state for a given operation is wrong because the previous execution has not completed.
Because the initial state of the second execution is wrong, the resulting computation is also wrong. Because eventually the second execution will update the final state with the wrong result.
You can view it here.
https://youtu.be/RWRicNoWKOY
Here is the classical Bank Account Balance example which will help newbies to understand Threads in Java easily w.r.t. race conditions:
public class BankAccount {
/**
* #param args
*/
int accountNumber;
double accountBalance;
public synchronized boolean Deposit(double amount){
double newAccountBalance=0;
if(amount<=0){
return false;
}
else {
newAccountBalance = accountBalance+amount;
accountBalance=newAccountBalance;
return true;
}
}
public synchronized boolean Withdraw(double amount){
double newAccountBalance=0;
if(amount>accountBalance){
return false;
}
else{
newAccountBalance = accountBalance-amount;
accountBalance=newAccountBalance;
return true;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BankAccount b = new BankAccount();
b.accountBalance=2000;
System.out.println(b.Withdraw(3000));
}
Try this basic example for better understanding of race condition:
public class ThreadRaceCondition {
/**
* #param args
* #throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Account myAccount = new Account(22222222);
// Expected deposit: 250
for (int i = 0; i < 50; i++) {
Transaction t = new Transaction(myAccount,
Transaction.TransactionType.DEPOSIT, 5.00);
t.start();
}
// Expected withdrawal: 50
for (int i = 0; i < 50; i++) {
Transaction t = new Transaction(myAccount,
Transaction.TransactionType.WITHDRAW, 1.00);
t.start();
}
// Temporary sleep to ensure all threads are completed. Don't use in
// realworld :-)
Thread.sleep(1000);
// Expected account balance is 200
System.out.println("Final Account Balance: "
+ myAccount.getAccountBalance());
}
}
class Transaction extends Thread {
public static enum TransactionType {
DEPOSIT(1), WITHDRAW(2);
private int value;
private TransactionType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
};
private TransactionType transactionType;
private Account account;
private double amount;
/*
* If transactionType == 1, deposit else if transactionType == 2 withdraw
*/
public Transaction(Account account, TransactionType transactionType,
double amount) {
this.transactionType = transactionType;
this.account = account;
this.amount = amount;
}
public void run() {
switch (this.transactionType) {
case DEPOSIT:
deposit();
printBalance();
break;
case WITHDRAW:
withdraw();
printBalance();
break;
default:
System.out.println("NOT A VALID TRANSACTION");
}
;
}
public void deposit() {
this.account.deposit(this.amount);
}
public void withdraw() {
this.account.withdraw(amount);
}
public void printBalance() {
System.out.println(Thread.currentThread().getName()
+ " : TransactionType: " + this.transactionType + ", Amount: "
+ this.amount);
System.out.println("Account Balance: "
+ this.account.getAccountBalance());
}
}
class Account {
private int accountNumber;
private double accountBalance;
public int getAccountNumber() {
return accountNumber;
}
public double getAccountBalance() {
return accountBalance;
}
public Account(int accountNumber) {
this.accountNumber = accountNumber;
}
// If this method is not synchronized, you will see race condition on
// Remove syncronized keyword to see race condition
public synchronized boolean deposit(double amount) {
if (amount < 0) {
return false;
} else {
accountBalance = accountBalance + amount;
return true;
}
}
// If this method is not synchronized, you will see race condition on
// Remove syncronized keyword to see race condition
public synchronized boolean withdraw(double amount) {
if (amount > accountBalance) {
return false;
} else {
accountBalance = accountBalance - amount;
return true;
}
}
}
You don't always want to discard a race condition. If you have a flag which can be read and written by multiple threads, and this flag is set to 'done' by one thread so that other thread stop processing when flag is set to 'done', you don't want that "race condition" to be eliminated. In fact, this one can be referred to as a benign race condition.
However, using a tool for detection of race condition, it will be spotted as a harmful race condition.
More details on race condition here, http://msdn.microsoft.com/en-us/magazine/cc546569.aspx.
Consider an operation which has to display the count as soon as the count gets incremented. ie., as soon as CounterThread increments the value DisplayThread needs to display the recently updated value.
int i = 0;
Output
CounterThread -> i = 1
DisplayThread -> i = 1
CounterThread -> i = 2
CounterThread -> i = 3
CounterThread -> i = 4
DisplayThread -> i = 4
Here CounterThread gets the lock frequently and updates the value before DisplayThread displays it. Here exists a Race condition. Race Condition can be solved by using Synchronzation
A race condition is an undesirable situation that occurs when two or more process can access and change the shared data at the same time.It occurred because there were conflicting accesses to a resource . Critical section problem may cause race condition. To solve critical condition among the process we have take out only one process at a time which execute the critical section.