அறிமுகம்
எனவே, ஜாவாவில் நூல்கள் இருப்பதை நாம் அறிவோம். சிறந்த ஒன்றாக: ஜாவா மற்றும் நூல் வகுப்பு என்ற தலைப்பில் நீங்கள் அதைப் பற்றி படிக்கலாம் . பகுதி I - மரணதண்டனையின் நூல்கள் . வழக்கமான குறியீட்டை மீண்டும் பார்ப்போம்:
public static void main(String[] args) throws Exception {
Runnable task = () -> {
System.out.println("Task executed");
};
Thread thread = new Thread(task);
thread.start();
}
நீங்கள் பார்க்க முடியும் என, ஒரு பணியைத் தொடங்குவதற்கான குறியீடு மிகவும் பொதுவானது, ஆனால் புதிய பணிக்காக அதை மீண்டும் செய்ய வேண்டும். ஒரு தீர்வு அதை ஒரு தனி முறையில் வைக்க வேண்டும், execute(Runnable runnable)
எ.கா. ஆனால் ஜாவாவின் படைப்பாளிகள் எங்கள் அவல நிலையைக் கருத்தில் கொண்டு இடைமுகத்தைக் கொண்டு வந்தனர் Executor
:
public static void main(String[] args) throws Exception {
Runnable task = () -> System.out.println("Task executed");
Executor executor = (runnable) -> {
new Thread(runnable).start();
};
executor.execute(task);
}
இந்த குறியீடு தெளிவாக மிகவும் சுருக்கமானது: இப்போது நாம் Runnable
நூலில் தொடங்க குறியீட்டை எழுதுகிறோம். நன்றாக இருக்கிறது, இல்லையா? ஆனால் இது ஆரம்பம் மட்டுமே:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html
Executor
இடைமுகம் ஒரு ExecutorService
துணை இடைமுகம் உள்ளது. இந்த இடைமுகத்திற்கான Javadoc கூறுகிறது, இது ExecutorService
ஒரு குறிப்பிட்ட குறிப்பை Executor
மூடுவதற்கான வழிமுறைகளை வழங்குகிறது Executor
. java.util.concurrent.Future
இது செயல்படுத்தும் செயல்முறையை கண்காணிக்கும் பொருட்டு ஒரு பெறுவதை சாத்தியமாக்குகிறது . முன்பு, சிறந்த ஒன்றாக: ஜாவா மற்றும் த்ரெட் வகுப்பு. பகுதி IV - அழைக்கக்கூடிய, எதிர்காலம் மற்றும் நண்பர்கள் , இன் திறன்களை சுருக்கமாக மதிப்பாய்வு செய்தோம் Future
. நீங்கள் அதை மறந்துவிட்டால் அல்லது படிக்கவே இல்லை என்றால், உங்கள் நினைவகத்தைப் புதுப்பிக்குமாறு நான் பரிந்துரைக்கிறேன்;) ஜாவாடோக் வேறு என்ன சொல்கிறது? எங்களிடம் ஒரு சிறப்புத் java.util.concurrent.Executors
தொழிற்சாலை உள்ளது என்று அது நமக்குச் சொல்கிறது ExecutorService
.
எக்ஸிகியூட்டர் சர்வீஸ்
மதிப்பாய்வு செய்வோம். ஒரு நூலில் ஒரு குறிப்பிட்ட பணியை நாம்Executor
இயக்க வேண்டும் (அதாவது அழைக்க வேண்டும் execute()
), மேலும் நூலை உருவாக்கும் குறியீடு நம்மிடமிருந்து மறைக்கப்பட்டுள்ளது. எங்களிடம் உள்ளது ExecutorService
- Executor
முன்னேற்றத்தைக் கட்டுப்படுத்த பல விருப்பங்களைக் கொண்ட ஒரு குறிப்பிட்டது. எங்களிடம் ஒரு Executors
தொழிற்சாலை உள்ளது, அது நம்மை உருவாக்க உதவுகிறது ExecutorService
. இப்போது அதை நாமே செய்வோம்:
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> task = () -> Thread.currentThread().getName();
ExecutorService service = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
Future result = service.submit(task);
System.out.println(result.get());
}
service.shutdown();
}
2 அளவுள்ள நிலையான நூல் குளத்தை நாங்கள் குறிப்பிட்டுள்ளதை நீங்கள் பார்க்கலாம். பிறகு, பணிகளை ஒவ்வொன்றாக குளத்தில் சமர்ப்பிக்கிறோம். ஒவ்வொரு பணியும் String
நூலின் பெயரை ( currentThread().GetName()
) கொண்டிருக்கும். ExecutorService
கடைசியில் அதை மூடுவது முக்கியம் , இல்லையெனில் எங்கள் நிரல் முடிவடையாது. தொழிற்சாலை Executors
கூடுதல் தொழிற்சாலை முறைகளைக் கொண்டுள்ளது. எடுத்துக்காட்டாக, ஒரு த்ரெட் ( newSingleThreadExecutor
) அல்லது கேச் ( newCachedThreadPool
) ஐ உள்ளடக்கிய ஒரு குளத்தை நாம் உருவாக்கலாம், அதில் இருந்து 1 நிமிடம் செயலற்ற நிலையில் இருக்கும் நூல்கள் அகற்றப்படும். உண்மையில், இவை தடுக்கும் வரிசையால்ExecutorService
ஆதரிக்கப்படுகின்றன , அதில் பணிகள் வைக்கப்படுகின்றன மற்றும் எந்தப் பணிகள் செயல்படுத்தப்படுகின்றன. வரிசைகளைத் தடுப்பது பற்றிய கூடுதல் தகவல்களை இந்த வீடியோவில் காணலாம் . இதையும் படிக்கலாம்BlockingQueue பற்றிய மதிப்பாய்வு . மேலும் "ArayBlockingQueue ஐ விட LinkedBlockingQueue ஐ எப்போது விரும்புவது?" என்ற கேள்விக்கான பதிலைப் பார்க்கவும். எளிமையான சொற்களில், ஒரு BlockingQueue
நூலை இரண்டு சந்தர்ப்பங்களில் தடுக்கிறது:
- நூல் ஒரு வெற்று வரிசையில் இருந்து பொருட்களைப் பெற முயற்சிக்கிறது
- நூல் முழு வரிசையில் பொருட்களை வைக்க முயற்சிக்கிறது
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
அல்லது
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
நாம் பார்க்க முடியும் என, செயல்படுத்தல் ExecutorService
தொழிற்சாலை முறைகள் உள்ளே உருவாக்கப்படுகின்றன. மற்றும் பெரும்பாலும், நாங்கள் பற்றி பேசுகிறோம் ThreadPoolExecutor
. வேலையை பாதிக்கும் அளவுருக்கள் மட்டுமே மாற்றப்படுகின்றன.
https://en.wikipedia.org/wiki/Thread_pool#/media/File:Thread_pool.svg
ThreadPoolExecutor
நாம் முன்பு பார்த்தது போல்,ThreadPoolExecutor
பொதுவாக தொழிற்சாலை முறைகளுக்குள் உருவாக்கப்படும். அதிகபட்ச மற்றும் குறைந்தபட்ச எண்ணிக்கையிலான த்ரெட்கள் மற்றும் எந்த வகையான வரிசை பயன்படுத்தப்படுகிறது என நாம் அனுப்பும் வாதங்களால் செயல்பாடு பாதிக்கப்படுகிறது. ஆனால் இடைமுகத்தின் எந்த செயலாக்கமும் java.util.concurrent.BlockingQueue
பயன்படுத்தப்படலாம். பேசுகையில் ThreadPoolExecutor
, சில சுவாரஸ்யமான அம்சங்களைக் குறிப்பிட வேண்டும். எடுத்துக்காட்டாக, ThreadPoolExecutor
இடம் இல்லை என்றால், நீங்கள் பணிகளைச் சமர்ப்பிக்க முடியாது:
public static void main(String[] args) throws ExecutionException, InterruptedException {
int threadBound = 2;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, threadBound,
0L, TimeUnit.SECONDS, new SynchronousQueue<>());
Callable<String> task = () -> {
Thread.sleep(1000);
return Thread.currentThread().getName();
};
for (int i = 0; i < threadBound + 1; i++) {
threadPoolExecutor.submit(task);
}
threadPoolExecutor.shutdown();
}
இது போன்ற பிழையுடன் இந்தக் குறியீடு செயலிழக்கும்:
Task java.util.concurrent.FutureTask@7cca494b rejected from java.util.concurrent.ThreadPoolExecutor@7ba4f24f[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
வேறு வார்த்தைகளில் கூறுவதானால், task
சமர்ப்பிக்க முடியாது, ஏனெனில் SynchronousQueue
அது உண்மையில் ஒரு தனிமத்தைக் கொண்டிருக்கும் வகையில் வடிவமைக்கப்பட்டுள்ளது மற்றும் அதில் மேலும் எதையும் வைக்க அனுமதிக்காது. இங்கே பூஜ்ஜியம் ("வரிசைப்படுத்தப்பட்ட பணிகள் = 0") இருப்பதைக் காணலாம் queued tasks
. ஆனால் இதில் விசித்திரமான ஒன்றும் இல்லை, ஏனெனில் இது ஒரு சிறப்பு அம்சமாகும் SynchronousQueue
, உண்மையில் இது 1-உறுப்பு வரிசையில் எப்போதும் காலியாக இருக்கும்! ஒரு இழை ஒரு உறுப்பை வரிசையில் வைக்கும் போது, மற்றொரு நூல் வரிசையிலிருந்து உறுப்பை எடுக்கும் வரை காத்திருக்கும். அதன்படி, நாம் அதை மாற்றலாம் new LinkedBlockingQueue<>(1)
மற்றும் பிழை இப்போது காண்பிக்கு மாற்றப்படும் queued tasks = 1
. வரிசையில் 1 உறுப்பு மட்டுமே இருப்பதால், இரண்டாவது உறுப்பைச் சேர்க்க முடியாது. அதுதான் நிரல் தோல்வியடையும். வரிசை பற்றிய எங்கள் விவாதத்தைத் தொடர்ந்து, அது கவனிக்கத்தக்கதுThreadPoolExecutor
வரிசைக்கு சேவை செய்வதற்கான கூடுதல் முறைகள் வகுப்பில் உள்ளன. எடுத்துக்காட்டாக, threadPoolExecutor.purge()
வரிசையில் இடத்தைக் காலியாக்க, ரத்துசெய்யப்பட்ட அனைத்து பணிகளையும் வரிசையிலிருந்து அகற்றும் முறை. மற்றொரு சுவாரஸ்யமான வரிசை தொடர்பான செயல்பாடு நிராகரிக்கப்பட்ட பணிகளுக்கான கையாளுதல் ஆகும்:
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.SECONDS, new SynchronousQueue());
Callable<String> task = () -> Thread.currentThread().getName();
threadPoolExecutor.setRejectedExecutionHandler((runnable, executor) -> System.out.println("Rejected"));
for (int i = 0; i < 5; i++) {
threadPoolExecutor.submit(task);
}
threadPoolExecutor.shutdown();
}
இந்த எடுத்துக்காட்டில், Rejected
வரிசையில் உள்ள பணி நிராகரிக்கப்படும் ஒவ்வொரு முறையும் எங்கள் கையாளுபவர் வெறுமனே காண்பிக்கும். வசதியானது, இல்லையா? கூடுதலாக, ThreadPoolExecutor
ஒரு சுவாரஸ்யமான துணைப்பிரிவு உள்ளது: ScheduledThreadPoolExecutor
, இது ஒரு ScheduledExecutorService
. இது ஒரு டைமரின் அடிப்படையில் ஒரு பணியைச் செய்யும் திறனை வழங்குகிறது.
திட்டமிடப்பட்ட நிறைவேற்று சேவை
ScheduledExecutorService
(இது ஒரு வகை ExecutorService
) ஒரு அட்டவணையில் பணிகளை இயக்க அனுமதிக்கிறது. ஒரு உதாரணத்தைப் பார்ப்போம்:
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
Callable<String> task = () -> {
System.out.println(Thread.currentThread().getName());
return Thread.currentThread().getName();
};
scheduledExecutorService.schedule(task, 1, TimeUnit.MINUTES);
scheduledExecutorService.shutdown();
}
இங்கே எல்லாம் எளிது. பணிகள் சமர்ப்பிக்கப்பட்டு பின்னர் நமக்கு ஒரு கிடைக்கும் java.util.concurrent.ScheduledFuture
. பின்வரும் சூழ்நிலையிலும் ஒரு அட்டவணை உதவியாக இருக்கும்:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
Runnable task = () -> {
System.out.println(Thread.currentThread().getName());
};
scheduledExecutorService.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);
Runnable
ஒரு குறிப்பிட்ட ஆரம்ப தாமதத்துடன் ஒரு நிலையான அதிர்வெண்ணில் ("FixedRate") செயல்படுத்துவதற்கான பணியை இங்கே சமர்ப்பிக்கிறோம் . இந்த வழக்கில், 1 வினாடிக்குப் பிறகு, ஒவ்வொரு 2 வினாடிக்கும் பணி செயல்படுத்தப்படும். இதேபோன்ற விருப்பம் உள்ளது:
scheduledExecutorService.scheduleWithFixedDelay(task, 1, 2, TimeUnit.SECONDS);
ஆனால் இந்த விஷயத்தில், ஒவ்வொரு செயல்பாட்டிற்கும் இடையில் ஒரு குறிப்பிட்ட இடைவெளியுடன் பணிகள் செய்யப்படுகின்றன. அதாவது, task
1 வினாடிக்குப் பிறகு செயல்படுத்தப்படும். பின்னர், அது முடிந்தவுடன், 2 வினாடிகள் கடந்துவிடும், பின்னர் ஒரு புதிய பணி தொடங்கப்படும். இந்த தலைப்பில் சில கூடுதல் ஆதாரங்கள் இங்கே:
- ஜாவாவில் த்ரெட் பூல்களுக்கு ஒரு அறிமுகம்
- ஜாவாவில் த்ரெட் பூல்களுக்கு அறிமுகம்
- ஜாவா மல்டித்ரெடிங் ஸ்டீபிள்சேஸ்: எக்ஸிகியூட்டர்களில் பணிகளை ரத்து செய்தல்
- பின்னணி பணிகளுக்கு ஜாவா எக்ஸிகியூட்டர்களைப் பயன்படுத்துதல்
https://dzone.com/articles/diving-into-java-8s-newworkstealingpools
வொர்க்ஸ்டீலிங் பூல்
மேலே உள்ள நூல் குளங்கள் தவிர, மேலும் ஒன்று உள்ளது. கொஞ்சம் ஸ்பெஷல் என்று நேர்மையாகச் சொல்லலாம். இது வேலை திருடும் குளம் என்று அழைக்கப்படுகிறது. சுருக்கமாக, வேலை-திருடுதல் என்பது ஒரு அல்காரிதம் ஆகும், இதில் செயலற்ற நூல்கள் மற்ற த்ரெட்களில் இருந்து பணிகளை எடுக்கத் தொடங்குகின்றன அல்லது பகிரப்பட்ட வரிசையில் இருந்து பணிகளை எடுக்கத் தொடங்குகின்றன. ஒரு உதாரணத்தைப் பார்ப்போம்:
public static void main(String[] args) {
Object lock = new Object();
ExecutorService executorService = Executors.newCachedThreadPool();
Callable<String> task = () -> {
System.out.println(Thread.currentThread().getName());
lock.wait(2000);
System.out.println("Finished");
return "result";
};
for (int i = 0; i < 5; i++) {
executorService.submit(task);
}
executorService.shutdown();
}
இந்தக் குறியீட்டை இயக்கினால், ExecutorService
நமக்கு 5 த்ரெட்களை உருவாக்கும், ஏனெனில் ஒவ்வொரு த்ரெட்டும் பூட்டுப் பொருளுக்கான காத்திருப்பு வரிசையில் வைக்கப்படும். நாங்கள் ஏற்கனவே மானிட்டர்கள் மற்றும் லாக்குகளை பெட்டரில் ஒன்றாகக் கண்டுபிடித்துள்ளோம் : ஜாவா மற்றும் த்ரெட் கிளாஸ். பகுதி II - ஒத்திசைவு . Executors.newCachedThreadPool()
இப்போது உடன் மாற்றுவோம் Executors.newWorkStealingPool()
. என்ன மாறும்? எங்கள் பணிகள் 5 இழைகளுக்குக் குறைவாகச் செயல்படுத்தப்படுவதைக் காண்போம். CachedThreadPool
ஒவ்வொரு பணிக்கும் ஒரு நூலை உருவாக்குவது நினைவிருக்கிறதா ? ஏனென்றால், wait()
த்ரெட் தடுக்கப்பட்டது, அடுத்தடுத்த பணிகள் முடிக்கப்பட வேண்டும், மேலும் அவர்களுக்காக புதிய இழைகள் குளத்தில் உருவாக்கப்பட்டன. திருடும் குளத்துடன், நூல்கள் என்றென்றும் சும்மா நிற்காது. அவர்கள் தங்கள் அண்டை வீட்டு வேலைகளைச் செய்யத் தொடங்குகிறார்கள். WorkStealingPool
மற்ற நூல் குளங்களிலிருந்து மிகவும் வித்தியாசமானது எது ? மந்திரம் என்பது உண்மைForkJoinPool
அதன் உள்ளே வாழ்கிறது:
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
உண்மையில், இன்னும் ஒரு வித்தியாசம் உள்ளது. முன்னிருப்பாக, a க்காக உருவாக்கப்பட்ட இழைகள் ForkJoinPool
டீமான் இழைகள் ஆகும், இது ஒரு சாதாரண த்ரெட் மூலம் உருவாக்கப்பட்ட நூல்களைப் போலல்லாமல் ThreadPool
. பொதுவாக, நீங்கள் டீமான் த்ரெட்களை நினைவில் வைத்திருக்க வேண்டும், ஏனெனில், எடுத்துக்காட்டாக, டீமான் அல்லாத இழைகளை உருவாக்கும் CompletableFuture
உங்கள் சொந்தத்தைக் குறிப்பிடும் வரை டீமான் த்ரெட்களையும் பயன்படுத்துகிறது . ThreadFactory
எதிர்பாராத இடங்களில் பதுங்கியிருக்கும் ஆச்சரியங்கள் இவை! :)
ForkJoinPool
இந்த பகுதியில், நாம் மீண்டும் பேசுவோம்ForkJoinPool
(முட்கரண்டி/சேர்தல் கட்டமைப்பு என்றும் அழைக்கப்படுகிறது), இது "ஹூட் கீழ்" வாழ்கிறது WorkStealingPool
. பொதுவாக, ஜாவா 1.7 இல் ஃபோர்க்/சேர்ன் ஃப்ரேம்வொர்க் மீண்டும் தோன்றியது. ஜாவா 11 கைக்கு அருகில் இருந்தாலும், அது இன்னும் நினைவில் கொள்ளத்தக்கது. இது மிகவும் பொதுவான செயலாக்கம் அல்ல, ஆனால் இது மிகவும் சுவாரஸ்யமானது. இணையத்தில் இதைப் பற்றி ஒரு நல்ல மதிப்புரை உள்ளது: ஜாவா ஃபோர்க்-ஜாயின் ஃப்ரேம்வொர்க்கை எடுத்துக்காட்டுகளுடன் புரிந்துகொள்வது . நம்பியிருக்கிறது . ForkJoinPool
_ java.util.concurrent.RecursiveTask
கூட இருக்கிறது java.util.concurrent.RecursiveAction
. RecursiveAction
முடிவைத் தருவதில்லை. இவ்வாறு, RecursiveTask
ஒத்திருக்கிறது Callable
, மற்றும் RecursiveAction
ஒத்திருக்கிறது unnable
. பெயர் இரண்டு முக்கியமான முறைகளின் பெயர்களை உள்ளடக்கியிருப்பதைக் காணலாம்: fork
மற்றும் join
. திfork
முறை ஒரு தனி நூலில் ஒத்திசைவற்ற முறையில் சில பணிகளைத் தொடங்குகிறது. மேலும் join
வேலை முடிவடையும் வரை காத்திருக்க இந்த முறை உங்களை அனுமதிக்கிறது. சிறந்த புரிதலைப் பெற, நீங்கள் ஜாவா 8 இல் இம்பரேட்டிவ் புரோகிராமிங்கிலிருந்து ஃபோர்க் வரை/ இணைவதற்கு இணையான ஸ்ட்ரீம்களைப் படிக்க வேண்டும் .
சுருக்கம்
சரி, இது மதிப்பாய்வின் இந்த பகுதியை முடிக்கிறது.Executor
இது முதலில் நூல்களை இயக்குவதற்காக கண்டுபிடிக்கப்பட்டது என்பதை அறிந்தோம் . பின்னர் ஜாவாவின் படைப்பாளிகள் யோசனையைத் தொடர முடிவு செய்து கொண்டு வந்தனர் ExecutorService
. மற்றும் ஐப் ExecutorService
பயன்படுத்தி செயல்படுத்துவதற்கான பணிகளைச் சமர்ப்பிக்கலாம் , மேலும் சேவையை முடக்கலாம். செயலாக்கங்கள் தேவைப்படுவதால் , அவர்கள் தொழிற்சாலை முறைகளுடன் ஒரு வகுப்பை எழுதி அதை அழைத்தனர் . இது நூல் குளங்களை உருவாக்க உங்களை அனுமதிக்கிறது ( ). கூடுதலாக, செயல்படுத்தும் அட்டவணையை குறிப்பிட அனுமதிக்கும் நூல் குளங்கள் உள்ளன. மற்றும் ஒரு பின்னால் மறைகிறது . நான் மேலே எழுதியது சுவாரஸ்யமாக மட்டுமல்ல, புரிந்துகொள்ளக்கூடியதாகவும் இருப்பதை நீங்கள் கண்டீர்கள் என்று நம்புகிறேன் :) உங்கள் பரிந்துரைகள் மற்றும் கருத்துகளைக் கேட்பதில் நான் எப்போதும் மகிழ்ச்சியடைகிறேன். submit()
invoke()
ExecutorService
Executors
ThreadPoolExecutor
ForkJoinPool
WorkStealingPool
ஒன்றாகச் சிறந்தது: ஜாவா மற்றும் நூல் வகுப்பு. பகுதி I — செயல்படுத்தும் நூல்கள் ஒன்றாகச் சிறப்பாகச் செயல்படுகின்றன: ஜாவா மற்றும் நூல் வகுப்பு. பகுதி II - ஒன்றாக ஒத்திசைத்தல் சிறந்தது: ஜாவா மற்றும் நூல் வகுப்பு. பகுதி III — ஒன்றாகச் செயல்படுவது சிறந்தது: ஜாவா மற்றும் த்ரெட் வகுப்பு. பகுதி IV - அழைக்கக்கூடிய, எதிர்காலம் மற்றும் நண்பர்கள் ஒன்றாக இருப்பது சிறந்தது: ஜாவா மற்றும் த்ரெட் வகுப்பு. பகுதி VI - நெருப்பு!
GO TO FULL VERSION