در برنامه هایی که نیاز به انجام چند عمل همزمان باشد، از چند Thread جداگانه برای انجام کارهای مختلف استفاده می کنند. به اینگونه برنامه های، برنامه های MultiThread گفته می شود. هرچند استفاده از Thread های جداگانه منجر به بالا رفتن کارایی برنامه گردد ولی در صورت عدم کنترل و یا استفاده نامناسب باعث عدم پایداری سسیتم و بروز مشکلاتی برای آن می شود.
یکی از مواردی مهم در زمان استفاده از Thread ها، ایمن بودن بخش هایی از نرم افزار است که در Thread های جداگانه اجرا می شود. این بخش ها اصطلاحا باید Thread Safe باشند تا در زمان استفاده همزمان توسط Thread های مختلف باعث بروز مشکل نشوند.
کلاسهای Thread-safe بلوک های سازنده برنامه های MutiThread هستند. اما میدانید که چگونه میتوان یک کلاس Thread-Safe ساخت؟
دو خطای زیر، عمده ترین خطاهایی هستند که در برنامه های MultiThread رخ می دهد :
در ادامه یک مثال ساده از رخ دادن این خطا ها را بررسی می کنیم:
Data Race یا Race Condition شرایطی است که خروجی به توالی اجرا و یا زمان اجرای یکسری وقایع غیر قابل کنترل وابسته باشد. به عنوان مثال کلاس زیر بر روی فیلد f حالت Race وجود دارد و دو متد مختلف میتوانند به صورت همزمان مقدار فیلد f را تغییر دهند. با اینکه در متد testAndUse()فیلد f قبل از استفاده بررسی می شود ولی ممکن است پس از موفقیت آمیز بودن بررسی ، متد toggle() مقدار فیلد f را null کرده و فراخوانی متد f.hashCode() منجر به خطای NullPointerException گردد.
ckage tso.examples; import java.util.Random; public class SimpleDataRace { private Object f = new Object(); public void toggle() { if (f == null) f = new Object(); else f = null; } public int testAndUse() { if (f != null) /* Potential NullPointerException: * f may become null before * calling hashCode()*/ java.util.Random; public class SimpleDataRace { private Object f = new Object(); public void toggle() { if (f == null) f = new Object(); else f = null; } public int testAndUse() { if (f != null) /* Potential NullPointerException: * f may become null before * calling hashCode()*/ return new Random().nextInt() * f.hashCode(); else return -1; }age tso.examples; import java.util.Random; public class SimpleDataRace { private Object f = new Object(); public void toggle() { if (f == null) f = new Object(); else f = null; } public int testAndUse() { if (f != null) /* Potential NullPointerException: * f may become null before * calling hashCode()*/ java.util.Random; public class SimpleDataRace { private Object f = new Object(); public void toggle() { if (f == null) f = new Object(); else f = null; } public int testAndUse() { if (f != null) /* Potential NullPointerException: * f may become null before * calling hashCode()*/ return new Random().nextInt() * f.hashCode(); else return -1; } }
Deadlock حالتی است که اجرای برنامه به بن بست رسیده و ادامه کار برنامه ممکن نباشد. کلاس زیر ThreadSafe نیست زیرا استفاده از آن ممکن است به حالت Deadlock برنامه منجر شود. به دلیل اینکه متدهای m1() و m2() اشیای lock1 و lock2 را در اختیار گرفته اند و یا توجه به ترتیب عکس در اختیار گرفتن آنها، تردی که m1 را اجرا می کند lock1 را در اختیار گرفته و منتظر آزاد شدن lock2 است تا آنرا نیز در اختیار بگیرد در حالی که در ترد دیگری که m2() را اجرا می کند، lock2 را در اختیار گرفته و منتظر آزاد شدن lock1 است تا آن را نیز در اختیار بگیرد. در این حالت برنامه در بن بست گیر کرده است.
package tso.examples; public class SimpleDeadlock { private Object lock1 = new Object(); private Object lock2 = new Object(); public void m1() { synchronized (lock1) { synchronized (lock2) { System.out.println("foo"); } } } public void m2() { synchronized (lock2) { synchronized (lock1) { System.out.println("bar"); } } .examples; public class SimpleDeadlock { private Object lock1 = new Object(); private Object lock2 = new Object(); public void m1() { synchronized (lock1) { synchronized (lock2) { System.out.println("foo"); } } } public void m2() { synchronized (lock2) { synchronized (lock1) { System.out.println("bar"); } } } }