Writing multi-threaded and concurrent programs is not easy, not even in Java. Even senior developers, including myself, make mistakes while writing concurrent Java applications. This is also one of the trickiest area of Java programming language, where misconceptions outnumbers concepts. Considering amount of misconception an average Java programmers has about multi-threading and concurrency, I thought to start a new series about common multi-threading mistakes done by Java programmers; what is better way to learn from common real word mistakes. Learning from mistakes has another name Experience, but if you only learn from your mistakes then there is only limited things you can learn, but if you learn from other peoples mistake, you can learn much more in short span of time. Have you ever thought, Why writing multi-threaded code is difficult? IMHO, primarily reason for this is that it multi-threading makes it hard for a code to speak for itself. Programmer read code sequentially to understand how it's executed, but it is only correct if one and only one thread is executing it. That's why Single threaded code are easy to read and debug. As soon as two threads comes into picture, It become very difficult to make prediction about how your code behave, especially in the absent of any synchronization rules e.g. rules enforced by Java Memory Model. Without JMM you can not make correct prediction about your code in a multi-threaded environment, because it's possible for one thread to stop at arbitrary point and another thread at different point. Situation becomes even more tricky if those threads are sharing data between them e.g. in form of objects, a poorly written multi-threaded program can cause deadlock, race condition and responsiveness issues, which will prevent a Java application to fulfil it's promise. I hope, in this series we can learn from each other's mistake and take a step forward on writing correct multi-threaded application in Java.
Using Run Instead of Start
I am starting with one of the simplest example, this is very common mistakes by junior programmers and caused by half knowledge. They know that anything written in run() method of Runnable interface or Thread class will execute in another thread, but doesn't know how to create another thread in JVM. Consider following code :
class KingKong {
public static synchronized void main(String[] args) {
Thread t = new Thread() {
public void run() {
kong();
}
};
t.run();
System.out.print("King");
}
public static synchronized void kong() {
System.out.print("Kong");
}
}
What Does It Print?
(a) KingKong
(b) KongKing
(c) It varies
(d) Compile time error
We had this question in our Java written test and you will be surprised by the percentage of answers, whopping 50% answers It varies, 10% says compile time error, another 15% picks answer a, KingKong and rest of 25% chooses KongKing. We also ask to write explanation of why they choose a particular answer, just to avoid picking someone who is guessing their way. The 50% developer, who chooses It varies, mentioned that there is no guarantee when a thread will start, so it possible that if main thread finishes first it will print KongKing and if new thread executes before main thread. Wow, what do you say about these developers, seems a decent lot of programmer who knows some part of multi-threading but overlooked critical detail. The next 10% programmer, who chose Compile time error were unsure whether main method can be synchronized or not and thought that compiler will not like. Next 15% says because "King" comes first in code, it will be printed first and "Kong" will be printed later. The Last 25% who chose "KongKing" are the people who got it correct. We were literally disappointed with these numbers because it wasn't such a difficult of tricky question, but I agree some time it's difficult to spot a typo and that's what makes this error very hard to debug.
Why Code Print KongKing and not KingKong?
Correct answer is "KongKing" and this is because of one typo in code. Intention of this code is to create a multi-threaded program, but because of t.run() it actually turned into a single threaded program. In Java, though it is true that calling Thread.start() will call Runnable.run() method but complete truth is that calling start() actually creates a new thread, and that new thread executes the run() method. If you directly call the run() method then no new thread will be created and the thread which is running the code will go to run() and execute it fist and then comeback to it's previous point. Like in this case, main thread will execute run() method first, and thus print "Kong" before coming back and printing "King", that's why output is "KongKing". When I quizzed about these to some programmer who were otherwise good but got this answer incorrect insisted that run() will call on new thread because they are calling as t.run() where t is new thread object. So apart from typo, this is the key misconception some Java programmer has. This is even more fundamental in nature because it highlight difference between code and thread. Here definitely run() is called on t, which is a new thread, but the thread which is executing code is not thread t, but main thread. t is not yet started because you have not called the start() method. If you copy past above code in Eclipse IDE and debug it you will see the truth, as shown below.
You can see that we have put the breakpoint right at the point where run() method is called i.e. t.run(). When you step Into this method, you will see that main thread is executing run() method and not the new thread. Now if we just changed the t.run() to t.start(), your program will become multi-threaded and a new thread will be created when main thread will execute line t.start(), later run() method will be called in this new thread, here is the screenshot of that.
That's all in first post of my new series of common Java Multi-threading mistakes. Always use start() method to start new threads and make your program multi-threaded, don't call run() method directly. Compiler doesn't prevent you but it create subtle bugs. By the way, difference between start() and run() method is also very common question on Java interview. Let me know how do you find this article and don't forget to share what multi-threading issues you have faced and what lessons you have learned from them. On closing note, I would share one important tip to understand multi-threading better, debug it. Yes debugging will tell you how many threads are currently executing your code, you can see their stack trace, values of variables they are holding and on which lock they are locking. Debugging multi-threaded program is not easy, but once you do it couple of times, you will find it immensely useful.
Tidak ada komentar:
Posting Komentar