النياسب و التزامن في جافا
ملاحظة :
نيسب Thread
اجرائية method
متصنت Listener
وعد جافا بأن تكون مستقلة عن قاعدة العمل ليس صحيح إطلاقاً
إذ إن أغلب البرامج التي تحوي واجهة مستخدم هي برامج متعددة النياسب ,و
النياسب في جافا للأسف ليس كما يعتقد أغلب المبرمجين مستقلة عن قاعدة العمل,وبالتالي جافا ليست مستقلة عن
منصة العمل.
حيث في الواجهات كل متصنت هو عبارة عن نيسب, وكذلك البرنامج الذي يحوي الطريقة
Main
هو نيسب أيضاً,وبالتالي أبسط البرامج يحوي نيسبين على الأقل
ولكن إن عدم استخدام النياسب بالشكل الصحيح يؤدي إلى بطئ شديد في الإستجابة , و هذا ما يكرهه أكثر المبرمجين
التزامن عملية مكلفة إذ إن تطبيق التزامن عملياًهو تطبيق سيمافور
و من استرتيجيات تنفيذ التزامن في جافا هي استخدام تعليمة
Assemply test_and_set which is an atomic operation
و هي تتم على مستوى نظام التشغيل ,
و هي تتم كما يلي : في حال عدم وجود تنافس على
Object
معين فإن الآلة الافتراضية في جافا تغوص في نظام التشغيل في البيئة المضيفة لتفحص بت معين يشير فيماإذا كان معلم أم لا ,في حال عدم تعليمه تقوم هي بتعليم هذا البت (أي إعطائه قيمة ما)
ولكن في حال وجود تنافس على
Object
معين فإن تغوص في نظام التشغيل في البيئة المضيفة لتفحص بت معين يشير فيماإذا كان معلم أم لا ,في حال كونه فعندها يتوجب على الآلة الافتراضية الخروج من نظام التشغيل لتعود لاحقاً و تفحص هذا البت
السيء في هذا الأمر هو إن عملية النفاذ لنظام التشغيل المضيف و الخروج منه هي عملية مكلفة جداً
مثال في
Windows NT it takes 600 machine cycles just to enter the operating system kernel
و إليكم المثال التالي الذي يوضح الفكرة السابقة :
package taming_java_threads;
import java.text.NumberFormat;
import java.util.*;
/**
*
* a benchmark to test the overhead of synchronization
* on a simple method invocation , Benchmarking java,paricularly
* when HotSpot is in the equation,is tricky.
* ther's a good tech note on this subject at http://...
**/
public class Synch {
private static long[] locking_time =new long[100];
private static long[] not_locking_time =new long[100];
private static final int ITERATIONS =1000000;
public Synch() {
}//END of constructor
/**
* this is synchronized method
* ***/
synchronized long locking (long a, long B){
return a+b;
}
/**
* this is nonsynchronized method
* ***/
long not_locking (long a, long B){
return a+b;
}
private void test(int id){
long start =System.currentTimeMillis();
for (long i=ITERATIONS;--i>=0;){
locking(i,i);//there is no contention
}
locking_time[id]=System.currentTimeMillis()-start;
start =System.currentTimeMillis();
for (long i=ITERATIONS;--i>=0;){
not_locking(i,i);//there is no synchronization
}
not_locking_time[id]=System.currentTimeMillis()-start;
}//end of test method
static void print_result(int id){
NumberFormat compositor =NumberFormat.getInstance();
compositor.setMaximumFractionDigits(2);
double timeInSynchronization =locking_time[id]-not_locking_time[id];
System.out.println("pass"+id+"\t:Time lost:"
+compositor.format(timeInSynchronization)+" ms\t"+
compositor.format((double)locking_time[id]/not_locking_time[id]*100)
+"% increase");
}//end of print_result
public static void main(String strrr []){
//first with no contention;
final Synch tester=new Synch();
tester.test(0);print_result(0);
tester.test(1);print_result(1);
tester.test(2);print_result(2);
tester.test(3);print_result(3);
tester.test(4);print_result(4);
tester.test(5);print_result(5);
tester.test(6);print_result(6);
/*
now let's do it again with contention . I'm assuming that
hotspot has optimized the test method by now , so am only calling it once
**/
final Object start_gate=new Object();
Thread t1=new Thread("number 1"){
public void run(){
try{
System.out.println("thread name : "+this.getName()+"\n");
synchronized(start_gate){start_gate.wait();
System.out.println(this.getName()+"\t after waiting\n");
}
}catch (InterruptedException e){}
System.out.println(this.getName()+"\t befor berforming test\n");
tester.test(7);
}//end of run
};//end of thread t1
Thread t2=new Thread ("number 2"){
public void run(){
try{
System.out.println("thread name : "+this.getName()+"\n");
synchronized(start_gate){start_gate.wait();
System.out.println(this.getName()+"\t after waiting\n");
}
}catch (InterruptedException e){}
System.out.println(this.getName()+"\t befor berforming test\n");
tester.test(8);
}
};
Thread .currentThread().setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
synchronized(start_gate){start_gate.notifyAll();}
try{
t1.join();
t2.join();
}catch (InterruptedException e){}
print_result(7);
print_result(8);
}
}
إذن يجب أن نكون حذرين عند استخدام التزامن وألا نستخدمه إلا عند الضرورة
فإذا كان لدينا اجرائية معينة تجري عملياتها فقط على متحولاتها الداخلية , و لا تنفذ للمتحولات العامة أو السكونية تبع الصف عندها يجب إلا تكون هذه الإجرائية متزامنة و إلا فإننا نضيع وقتنا على الفاضي...
أرجو أن تكونوا قد استفدتم من هذه الفكرة , و إذا أعجبكم الموضوع بإمكاننا متابعة النقاش فيه , و إلى اللقاء