6. Cancarenzi چیست؟
Concurrency یک کتابخانه کلاس در جاوا است که شامل کلاس های ویژه ای است که برای کار در چندین رشته بهینه شده است. این کلاس ها در یک بسته جمع آوری می شوندjava.util.concurrent
. آنها را می توان به صورت شماتیک بر اساس عملکرد به شرح زیر تقسیم کرد: مجموعه های همزمان - مجموعه ای از مجموعه ها که در یک محیط چند رشته ای کارآمدتر از مجموعه های جهانی استاندارد از java.util
بسته کار می کنند. به جای یک پوشش اولیه Collections.synchronizedList
با مسدود کردن دسترسی به کل مجموعه، از قفلهای بخشهای داده استفاده میشود، یا کار برای خواندن موازی دادهها با استفاده از الگوریتمهای بدون انتظار بهینهسازی میشود. صف - غیر مسدود کردن و مسدود کردن صف با پشتیبانی از چند رشته. صف های غیر مسدود کننده برای سرعت و عملکرد بدون مسدود کردن رشته ها طراحی شده اند. در صورت عدم رعایت برخی شرایط، به عنوان مثال، صف خالی یا سرریز شده یا عدم وجود «مصرفکننده» رایگان، از صفهای مسدود کردن استفاده میشود. همگام سازها ابزارهای کمکی برای همگام سازی رشته ها هستند. آنها یک سلاح قدرتمند در محاسبات "موازی" هستند. Executors - شامل چارچوب های عالی برای ایجاد استخرهای نخ، زمان بندی وظایف ناهمزمان و به دست آوردن نتایج است. قفل - نشان دهنده مکانیسم های جایگزین و انعطاف پذیرتر همگام سازی نخ در مقایسه با موارد synchronized
پایه wait
است notify
. Atomics - کلاس هایی با پشتیبانی از عملیات اتمی بر روی منابع اولیه و منابع. منبع:notifyAll
7. چه کلاس هایی از کانکارنسی را می شناسید؟
پاسخ به این سوال کاملا در این مقاله بیان شده است . من اهمیتی در چاپ مجدد همه آن در اینجا نمی بینم، بنابراین فقط در مورد کلاس هایی که افتخار آشنایی مختصر با آنها را داشتم، توضیحاتی ارائه خواهم کرد. ConcurrentHashMap<K, V> - بر خلافHashtable
و بلوکهای synhronized
روی HashMap
، دادهها به شکل بخشهایی ارائه میشوند که به هش کلیدها تقسیم میشوند. در نتیجه، دادهها توسط بخشها بهجای یک شیء منفرد قابل دسترسی هستند. علاوه بر این، تکرار کننده ها داده ها را برای یک دوره زمانی خاص نشان می دهند و پرتاب نمی کنند ConcurrentModificationException
. AtomicBoolean، AtomicInteger، AtomicLong، AtomicIntegerArray، AtomicLongArray - اگر در یک کلاس نیاز به همگام سازی دسترسی به یک متغیر ساده از نوع داشته باشید، چه int
؟ می توانید از ساختارها با synchronized
و در هنگام استفاده از عملیات اتمی استفاده کنید set/get
. volatile
اما می توانید با استفاده از کلاس های جدید حتی بهتر عمل کنید Atomic*
. به دلیل استفاده از CAS، عملیات با این کلاسها سریعتر از همگامسازی از طریق است synchronized/volatile
. به علاوه، روشهایی برای افزودن اتمی با مقدار معین و همچنین افزایش/کاهش وجود دارد.
8. کلاس ConcurrentHashMap چگونه کار می کند؟
تا زمان معرفی،ConcurrentHashMap
توسعه دهندگان جاوا به پیاده سازی نقشه هش زیر نیاز داشتند:
- ایمنی نخ
- بدون قفل در کل میز هنگام دسترسی به آن
- مطلوب است که هنگام انجام عملیات خواندن هیچ قفل جدولی وجود نداشته باشد
ConcurrentHashMap
به شرح زیر است:
-
عناصر نقشه
برخلاف عناصر
HashMap
،Entry
inConcurrentHashMap
به عنوان اعلان می شوندvolatile
. این یک ویژگی مهم است، همچنین به دلیل تغییرات در JMM .static final class HashEntry<K, V> { final K key; final int hash; volatile V value; final HashEntry<K, V> next; HashEntry(K key, int hash, HashEntry<K, V> next, V value) { this .key = key; this .hash = hash; this .next = next; this .value = value; } @SuppressWarnings("unchecked") static final <K, V> HashEntry<K, V>[] newArray(int i) { return new HashEntry[i]; } }
-
تابع هش
ConcurrentHashMap
یک عملکرد هش بهبود یافته نیز استفاده می شود.HashMap
اجازه دهید به شما یادآوری کنم که در JDK 1.2 چگونه بود :static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
نسخه از ConcurrentHashMap JDK 1.5:
private static int hash(int h) { h += (h << 15) ^ 0xffffcd7d; h ^= (h >>> 10); h += (h << 3); h ^= (h >>> 6); h += (h << 2) + (h << 14); return h ^ (h >>> 16); }
چرا نیاز به پیچیده تر کردن تابع هش وجود دارد؟ طول جداول در نقشه هش با توان دو تعیین می شود. برای کدهای هش که نمایش های باینری آنها در موقعیت کم و بالا تفاوتی ندارند، برخورد خواهیم داشت. افزایش پیچیدگی تابع هش فقط این مشکل را حل می کند و احتمال برخورد در نقشه را کاهش می دهد.
-
بخش ها
نقشه به N بخش مختلف تقسیم می شود (16 به طور پیش فرض، حداکثر مقدار می تواند 16 بیت باشد و توان دو است). هر بخش یک جدول امن از عناصر نقشه است. افزایش تعداد بخشها، عملیات اصلاح را تشویق میکند تا چندین بخش را در بر بگیرد و احتمال مسدود شدن در زمان اجرا را کاهش دهد.
-
سطح همزمانی
این پارامتر بر میزان استفاده از کارت حافظه و تعداد بخش های کارت تأثیر می گذارد.
تعداد بخشها بهعنوان نزدیکترین توان دو بزرگتر از ConcurrencyLevel انتخاب میشود. کاهش سطح همزمانی این احتمال را افزایش می دهد که رشته ها هنگام نوشتن، بخش های نقشه را مسدود کنند. برآورد بیش از حد شاخص منجر به استفاده ناکارآمد از حافظه می شود. اگر فقط یک رشته نقشه را تغییر می دهد و بقیه می خوانند، توصیه می شود از مقدار 1 استفاده کنید.
-
جمع
بنابراین، مزایا و ویژگی های اجرایی اصلی
ConcurrentHashMap
:hashmap
نقشه دارای یک رابط تعاملی مشابه است- عملیات خواندن نیازی به قفل ندارد و به صورت موازی انجام می شود
- عملیات نوشتن اغلب می تواند به صورت موازی و بدون مسدود کردن نیز انجام شود
- هنگام ایجاد، مورد مورد نیاز نشان داده می شود
concurrencyLevel
که با خواندن و نوشتن آمار تعیین می شود - عناصر نقشه دارای مقداری هستند
value
که به صورت اعلام شده استvolatile
9. کلاس Lock چیست؟
برای کنترل دسترسی به یک منبع مشترک، می توانیم از قفل ها به عنوان جایگزینی برای اپراتور همگام استفاده کنیم. عملکرد قفل در بسته بندی شده استjava.util.concurrent.locks
. ابتدا، موضوع سعی می کند به منبع مشترک دسترسی پیدا کند. اگر آزاد باشد، یک قفل روی نخ قرار می گیرد. پس از اتمام کار، قفل منبع مشترک آزاد می شود. اگر منبع رایگان نباشد و قفلی از قبل روی آن قرار داده شده باشد، نخ تا زمانی که این قفل آزاد شود صبر می کند. کلاس های Lock رابطی را پیاده سازی می کنند Lock
که متدهای زیر را تعریف می کند:
void lock():
صبر می کند تا قفل بدست آیدboolean tryLock():
سعی می کند یک قفل به دست آورد؛ اگر قفل به دست آید، true را برمی گرداند . اگر قفل به دست نیامد، false برمیگرداند . بر خلاف روش،lock()
در صورت در دسترس نبودن قفل، منتظر نمی ماند تا قفلی به دست آوردvoid unlock():
قفل را برمی داردCondition newCondition():
شیءCondition
مرتبط با قفل فعلی را برمی گرداند
lock()
و پس از پایان کار با منابع مشترک، روش نامیده می شود unlock()
که قفل را آزاد می کند. شی Condition
به شما امکان می دهد تا بلاک کردن را مدیریت کنید. به عنوان یک قاعده، برای کار با قفل ها، یک کلاس ReentrantLock
از بسته استفاده می شود.این java.util.concurrent.locks.
کلاس رابط را پیاده سازی می کند Lock
. Resource
بیایید به استفاده از Java Lock API با استفاده از یک برنامه کوچک به عنوان مثال نگاهی بیندازیم: بنابراین، فرض کنید یک کلاس با چند روش و روش ایمن برای رشتهای داریم که در آن ایمنی رشته مورد نیاز نیست.
public class Resource {
public void doSomething(){
// пусть здесь происходит работа с базой данных
}
public void doLogging(){
// потокобезопасность для логгирования нам не требуется
}
}
حالا بیایید کلاسی را انتخاب کنیم که رابط را پیاده سازی کرده Runnable
و از متدهای کلاس استفاده می کند Resource
.
public class SynchronizedLockExample implements Runnable{
// экземпляр класса Resource для работы с методами
private Resource resource;
public SynchronizedLockExample(Resource r){
this.resource = r;
}
@Override
public void run() {
synchronized (resource) {
resource.doSomething();
}
resource.doLogging();
}
}
حالا بیایید برنامه فوق را با استفاده از Lock API به جای synchronized
.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// класс для работы с Lock API. Переписан с приведенной выше программы,
// но уже без использования ключевого слова synchronized
public class ConcurrencyLockExample implements Runnable{
private Resource resource;
private Lock lock;
public ConcurrencyLockExample(Resource r){
this.resource = r;
this.lock = new ReentrantLock();
}
@Override
public void run() {
try {
// лочим на 10 секунд
if(lock.tryLock(10, TimeUnit.SECONDS)){
resource.doSomething();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//убираем лок
lock.unlock();
}
// Для логгирования не требуется потокобезопасность
resource.doLogging();
}
}
همانطور که از برنامه می بینید، ما از روشی استفاده می کنیم tryLock()
تا مطمئن شویم که thread فقط برای مدت زمان مشخصی منتظر می ماند. اگر قفلی روی شیء بدست نیاورد، به سادگی وارد و خارج می شود. نکته مهم دیگر. شما باید از یک بلوک استفاده کنید try-finally
تا مطمئن شوید که قفل آزاد می شود حتی اگر روش doSomething()
استثنایی ایجاد کند. منابع:
11. موتکس چیست؟
mutex یک شی خاص برای همگام سازی موضوعات/فرآیندها است. می تواند دو حالت داشته باشد - شلوغ و رایگان. برای ساده تر، mutex یک متغیر بولی است که دو مقدار اشغال (true) و free (false) را می گیرد. هنگامی که یک رشته می خواهد مالکیت انحصاری یک شی را داشته باشد، mutex خود را به عنوان مشغول علامت گذاری می کند و پس از پایان کار با آن، mutex خود را به عنوان رایگان علامت گذاری می کند. یک mutex به هر شی در جاوا متصل است. فقط ماشین جاوا دسترسی مستقیم به mutex دارد. از برنامه نویس پنهان است.12. مانیتور چیست؟
مانیتور یک مکانیسم ویژه (یک قطعه کد) است - یک افزونه بر روی mutex که عملکرد صحیح با آن را تضمین می کند. از این گذشته، علامت گذاری که شی مشغول است کافی نیست، بلکه باید اطمینان حاصل کنیم که رشته های دیگر سعی نمی کنند از شی اشغال شده استفاده کنند. در جاوا، مانیتور با استفاده از کلمه کلیدی پیاده سازی می شودsynchronized
. هنگامی که یک بلوک همگام می نویسیم، کامپایلر جاوا آن را با سه قطعه کد جایگزین می کند:
- در ابتدای بلوک
synchronized
، کدی اضافه می شود که mutex را به عنوان مشغول علامت گذاری می کند. - در انتهای بلوک،
synchronized
یک کد اضافه می شود که mutex را به عنوان رایگان علامت گذاری می کند. - قبل از بلوک،
synchronized
کدی اضافه میشود که بررسی میکند آیا mutex مشغول است یا نه، سپس رشته باید منتظر باشد تا آزاد شود.
GO TO FULL VERSION