আমার কখন ThreadLocal
পরিবর্তনশীল ব্যবহার করা উচিত ?
এটি কীভাবে ব্যবহৃত হয়?
RequestUtils.getCurrentRequestThreadLocal()
। এটি যদিও খুব মার্জিত তা না বলা, তবে এটি থ্রেডলোকাল নিজেই বেশিরভাগ ক্ষেত্রে খুব মার্জিত নয় এই সত্যটি থেকে উদ্ভূত হয়।
আমার কখন ThreadLocal
পরিবর্তনশীল ব্যবহার করা উচিত ?
এটি কীভাবে ব্যবহৃত হয়?
RequestUtils.getCurrentRequestThreadLocal()
। এটি যদিও খুব মার্জিত তা না বলা, তবে এটি থ্রেডলোকাল নিজেই বেশিরভাগ ক্ষেত্রে খুব মার্জিত নয় এই সত্যটি থেকে উদ্ভূত হয়।
উত্তর:
একটি সম্ভাব্য (এবং সাধারণ) ব্যবহার হ'ল যখন আপনার কাছে এমন কিছু বস্তু রয়েছে যা থ্রেড-নিরাপদ নয়, তবে আপনি সেই অবজেক্টে সিঙ্ক্রোনাইজ অ্যাক্সেস এড়াতে চান (আমি আপনাকে দেখছি, সিম্পলডেট ফরমেট )। পরিবর্তে, প্রতিটি থ্রেডকে বস্তুর নিজস্ব উদাহরণ দিন।
উদাহরণ স্বরূপ:
public class Foo
{
// SimpleDateFormat is not thread-safe, so give one to each thread
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
SimpleDateFormat
। থ্রেড-নিরাপদ বিকল্প ব্যবহার করা আরও ভাল । যদি আপনি সম্মত হন যে সিলেটলেটগুলি খারাপ তবে ThreadLocal
এটি আরও খারাপ।
যেহেতু একটি প্রদত্তের ThreadLocal
মধ্যে থাকা ডেটাগুলির একটি রেফারেন্স Thread
, তাই ThreadLocal
থ্রেড পুল ব্যবহার করে অ্যাপ্লিকেশন সার্ভারে এস ব্যবহার করার সময় আপনি ক্লাসলোডিং লিক দিয়ে শেষ করতে পারেন । আপনার কোনও পরিষ্কার করার বিষয়ে বা এর পদ্ধতিটি ব্যবহার করে ThreadLocal
আপনার খুব সতর্ক হওয়া দরকার ।get()
set()
ThreadLocal
remove()
আপনার কাজ শেষ হয়ে গেলে আপনি যদি পরিষ্কার না করেন তবে এটি মোতায়েন করা ওয়েব অ্যাপের অংশ হিসাবে লোড হওয়া ক্লাসগুলিতে থাকা কোনও রেফারেন্স স্থায়ী স্তূপে থাকবে এবং কখনই আবর্জনা সংগ্রহ করা যাবে না। ওয়েব অ্যাপ্লিকেশনটিকে পুনরায় প্রচার / অপ্রকাশিত Thread
করা আপনার ওয়েবঅ্যাপের ক্লাসের প্রত্যেকের রেফারেন্স পরিষ্কার করবে না (কারণ) এটি Thread
আপনার ওয়েবঅ্যাপের মালিকানাধীন কিছু নয়। প্রতিটি ক্রমাগত মোতায়েন ক্লাসের একটি নতুন উদাহরণ তৈরি করবে যা কখনই আবর্জনা সংগ্রহ করা হবে না।
আপনার কারণে স্মৃতি ব্যতিক্রমগুলি শেষ হয়ে যাবে java.lang.OutOfMemoryError: PermGen space
এবং কিছু গুগলিং সম্ভবত -XX:MaxPermSize
বাগ ঠিক করার পরিবর্তে বাড়বে ।
যদি আপনি এই সমস্যাগুলির মুখোমুখি না হয়ে থাকেন তবে আপনি নির্ধারণ করতে পারবেন কোন থ্রেড এবং শ্রেণি একটিগ্রহের স্মৃতি বিশ্লেষক এবং / অথবা ফ্রাঙ্ক কিয়েটের গাইড এবং ফলোআপ অনুসরণ করে এই রেফারেন্সগুলি ধরে রেখেছে ।
আপডেট: অ্যালেক্স ভাসিউরের ব্লগ এন্ট্রি পুনরায় আবিষ্কার করে যা আমাকে যা কিছু ThreadLocal
সমস্যা ছিল তা সন্ধান করতে সহায়তা করে।
অনেকগুলি ফ্রেমওয়ার্ক বর্তমান থ্রেড সম্পর্কিত কিছু প্রসঙ্গ বজায় রাখতে থ্রেডলোকাল ব্যবহার করে als উদাহরণস্বরূপ, যখন বর্তমান লেনদেনটি থ্রেডলোকল এ সঞ্চিত থাকে, আপনাকে স্ট্যাকের নিচে কারও কাছে অ্যাক্সেসের প্রয়োজন হলে প্রতিটি পদ্ধতি কলের মাধ্যমে প্যারামিটার হিসাবে পাস করার দরকার নেই। ওয়েব অ্যাপ্লিকেশনগুলি বর্তমান অনুরোধ এবং সেশন সম্পর্কে তথ্য একটি থ্রেডলোকলে সংরক্ষণ করতে পারে যাতে অ্যাপ্লিকেশনটিতে তাদের সহজেই অ্যাক্সেস থাকে। ইনজেকশনের জন্য কাস্টম স্কোপগুলি প্রয়োগ করার সময় গুইসের সাহায্যে আপনি থ্রেডলোকালস ব্যবহার করতে পারেন (গুইসের ডিফল্ট সার্ভলেট স্কোপগুলি সম্ভবত সেগুলি ব্যবহার করে)।
থ্রেডলোকালগুলি এক ধরণের গ্লোবাল ভেরিয়েবল (যদিও কিছুটা কম খারাপ কারণ সেগুলি একটি থ্রেডের মধ্যে সীমাবদ্ধ) তবে অযাচিত পার্শ্ব প্রতিক্রিয়া এবং মেমরি ফাঁস এড়াতে তাদের ব্যবহার করার সময় আপনার সতর্কতা অবলম্বন করা উচিত। আপনার এপিআইগুলি ডিজাইন করুন যাতে থ্রেডলোকাল মানগুলি যখন আর প্রয়োজন হয় না তখন সর্বদা স্বয়ংক্রিয়ভাবে সাফ হয়ে যায় এবং এপিআই-র ভুল ব্যবহার সম্ভব হবে না (উদাহরণস্বরূপ এর মতো )। থ্রেডলোকালস কোড ক্লিনার তৈরি করতে ব্যবহার করা যেতে পারে এবং কিছু বিরল ক্ষেত্রে এগুলি কিছু কাজ করার একমাত্র উপায় (আমার বর্তমান প্রকল্পে এরকম দুটি ক্ষেত্রে ছিল; সেগুলি এখানে "স্ট্যাটিক ফিল্ডস এবং গ্লোবাল ভেরিয়েবলস" এর অধীনে নথিভুক্ত করা হয়েছে )।
জাভাতে, যদি আপনার কাছে কোনও থ্যাটাম থাকে যা প্রতি-থ্রেডে পরিবর্তিত হতে পারে তবে আপনার পছন্দগুলি সেই ডাটামকে প্রয়োজনীয় প্রতিটি পদ্ধতিতে (বা প্রয়োজন হতে পারে) এর কাছাকাছি পৌঁছে দেওয়া বা ডাটামকে থ্রেডের সাথে যুক্ত করতে হবে। আপনার সমস্ত পদ্ধতি যদি ইতিমধ্যে একটি সাধারণ "প্রসঙ্গ" ভেরিয়েবলের চারপাশে পাস করার প্রয়োজন হয় তবে সর্বত্র সর্বত্র ডেটাম পাস করা কার্যকর হবে।
যদি এটি না হয়, আপনি অতিরিক্ত প্যারামিটার দিয়ে আপনার পদ্ধতি স্বাক্ষরগুলি বিশৃঙ্খলা করতে না চাইতে পারেন। একটি থ্রেডবিহীন বিশ্বে আপনি বৈশ্বিক ভেরিয়েবলের জাভা সমতুল্য সমস্যাটি সমাধান করতে পারেন। একটি থ্রেড শব্দের সাথে, বিশ্বব্যাপী ভেরিয়েবলের সমতুল্য হ'ল থ্রেড-লোকাল ভেরিয়েবল।
অনুশীলনে জাভা কনকুরেন্সি বইয়ের খুব ভাল উদাহরণ রয়েছে । যেখানে লেখক ( জোশুয়া ব্লচ ) ব্যাখ্যা করেছেন যে কীভাবে থ্রেড কারাবাস থ্রেড সুরক্ষা এবং থ্রেডলোকাল অর্জনের অন্যতম সহজ উপায় is বজায় রাখার আরও আনুষ্ঠানিক উপায়। শেষ পর্যন্ত তিনি আরও ব্যাখ্যা করেছেন যে কীভাবে লোকেরা এটির বৈশ্বিক চলক হিসাবে এটি ব্যবহার করতে পারে abuse
আমি উল্লিখিত বই থেকে পাঠ্যটি অনুলিপি করেছি তবে কোড 3.10 অনুপস্থিত কারণ থ্রেডলোকালটি কোথায় ব্যবহার করা উচিত তা বোঝা খুব বেশি গুরুত্বপূর্ণ নয়।
থ্রেড-লোকাল ভেরিয়েবলগুলি প্রায়শই পরিবর্তিত সিঙ্গললেট বা গ্লোবাল ভেরিয়েবলের উপর ভিত্তি করে ডিজাইনে ভাগ করা রোধ করতে ব্যবহৃত হয়। উদাহরণস্বরূপ, একক থ্রেডযুক্ত অ্যাপ্লিকেশনটি কোনও গ্লোবাল ডাটাবেস সংযোগ বজায় রাখতে পারে যা প্রতিটি পদ্ধতিতে সংযোগটি পাস না করে এড়াতে প্রারম্ভকালে শুরু করা হয়। যেহেতু জেডিবিসি সংযোগগুলি থ্রেড-নিরাপদ নাও হতে পারে, অতিরিক্ত সমন্বয় ছাড়াই বিশ্বব্যাপী সংযোগ ব্যবহার করে এমন একাধিক সংযুক্ত অ্যাপ্লিকেশন থ্রেড-নিরাপদ নয়। জেডিবিসি সংযোগ সঞ্চয় করার জন্য একটি থ্রেডলোকাল ব্যবহার করে, যেমন 3.10 তালিকাতে সংযোগহোল্ডার হিসাবে, প্রতিটি থ্রেডের নিজস্ব সংযোগ থাকবে।
থ্রেডলোকাল অ্যাপ্লিকেশন ফ্রেমওয়ার্ক কার্যকর করতে ব্যাপকভাবে ব্যবহৃত হয়। উদাহরণস্বরূপ, জে 2 ই ই ধারকগুলি একটি ইজেবি কলটির সময়কালের জন্য একটি এক্সিকিউটিভ থ্রেডের সাথে লেনদেনের প্রসঙ্গে যুক্ত করে। এটি লেনদেনের প্রসঙ্গটি ধরে রেখে একটি স্ট্যাটিক থ্রেড-লোকাল ব্যবহার করে সহজেই প্রয়োগ করা হয়: যখন ফ্রেমওয়ার্ক কোডটিতে বর্তমানে কোন লেনদেন চলছে তা নির্ধারণ করা দরকার, এটি এই থ্রেডলোকাল থেকে লেনদেনের প্রসঙ্গটি এনেছে। এটি সুবিধাজনক যে এটি প্রতিটি পদ্ধতির মধ্যে কার্যকরকরণ প্রসঙ্গে তথ্য প্রেরণের প্রয়োজনীয়তা হ্রাস করে তবে ফ্রেমওয়ার্কে এই প্রক্রিয়াটি ব্যবহার করে এমন কোনও কোড যুক্ত করে।
থ্রেডলোকালকে গ্লোবাল ভেরিয়েবলগুলি ব্যবহারের লাইসেন্স হিসাবে বা "লুকানো" পদ্ধতির যুক্তি তৈরির মাধ্যম হিসাবে ব্যবহার করে তার থ্রেড বন্দি সম্পত্তি হিসাবে ব্যবহার করা সহজ। গ্লোবাল ভেরিয়েবলের মতো, থ্রেড-লোকাল ভেরিয়েবলগুলি পুনরায় ব্যবহারযোগ্যতা থেকে বিরত করতে পারে এবং ক্লাসগুলির মধ্যে লুকানো কাপলিংগুলি প্রবর্তন করতে পারে এবং তাই যত্ন সহ ব্যবহার করা উচিত।
মূলত, আপনি একটি প্রয়োজন যখন পরিবর্তনশীল এর মান বর্তমান থ্রেড উপর নির্ভর করে এবং এটি সুবিধাজনক আপনি অন্য কোন উপায়ে থ্রেড মান সংযুক্ত করতে জন্য নয় (উদাহরণস্বরূপ, থ্রেড subclassing)।
একটি সাধারণ কেস যেখানে অন্য কিছু কাঠামো থ্রেড তৈরি করেছে যা আপনার কোডটি চালু রয়েছে, যেমন একটি সার্লেট পাত্রে, অথবা যেখানে থ্রেডলোকালটি ব্যবহার করা আরও বেশি অর্থবোধ করে কারণ আপনার ভেরিয়েবলটি "তার যৌক্তিক স্থানে" (পরিবর্তকের পরিবর্তে) একটি থ্রেড সাবক্লাস থেকে বা অন্য কোনও হ্যাশ মানচিত্রে ঝুলানো)।
আমার ওয়েবসাইটে আমার থ্রেডলোকাল কখন ব্যবহার করতে হবে সে সম্পর্কে আরও কিছু আলোচনা এবং উদাহরণ রয়েছে যা আগ্রহীও হতে পারে।
কিছু লোক থ্রডলোকালকে নির্দিষ্ট থ্রিজি সংযুক্ত অ্যালগরিদমগুলিতে প্রতিটি থ্রেডের সাথে একটি "থ্রেড আইডি" সংযুক্ত করার উপায় হিসাবে সমর্থন করে যেখানে আপনার থ্রেড নম্বর প্রয়োজন (উদাহরণস্বরূপ হার্লিহী এবং শভিট)। এই ক্ষেত্রে, আপনি সত্যিই একটি সুবিধা পাচ্ছেন তা পরীক্ষা করে দেখুন!
জাভাতে থ্রেডলোকালটি জেডিকে ১.২ এ প্রবর্তিত হয়েছিল তবে থ্রেডলোকাল ভেরিয়েবলের ধরণের সুরক্ষা প্রবর্তনের জন্য পরে জেডিকে ২.৩ এ জেনারাইড করা হয়েছিল।
থ্রেডলোকাল থ্রেড স্কোপের সাথে যুক্ত হতে পারে, থ্রেড দ্বারা সম্পাদিত সমস্ত কোডের থ্রেডলোকাল ভেরিয়েবলের অ্যাক্সেস রয়েছে তবে দুটি থ্রেড একে অপরকে থ্রেডলোকাল ভেরিয়েবল দেখতে পাবে না।
প্রতিটি থ্রেডে থ্রেডলোকাল ভেরিয়েবলের একচেটিয়া অনুলিপি থাকে যা থ্রেড শেষ হয়ে গেলে বা মারা যায়, সাধারণত বা কোনও ব্যতিক্রমের কারণে, থ্রেডলোকাল ভেরিয়েবলের কোনও অন্য লাইভ উল্লেখ নেই।
জাভাতে থ্রেডলোকাল ভেরিয়েবলগুলি সাধারণত ক্লাসে ব্যক্তিগত স্ট্যাটিক ক্ষেত্র এবং থ্রেডের অভ্যন্তরে এর রাজ্য বজায় রাখে।
আরও পড়ুন: জাভাতে থ্রেডলোকাল - উদাহরণ প্রোগ্রাম এবং টিউটোরিয়াল
ডকুমেন্টেশনটি এটি খুব ভালভাবে বলেছে: "প্রতিটি থ্রেড যা [একটি থ্রেড-লোকাল ভেরিয়েবল] অ্যাক্সেস করে (তার গেট বা সেট পদ্ধতির মাধ্যমে) এর নিজস্ব, ভেরিয়েবলের স্বতন্ত্রভাবে আরম্ভকৃত কপি থাকে" "
প্রতিটি থ্রেডের কোনও কিছুর নিজস্ব অনুলিপি থাকা উচিত আপনি একটি ব্যবহার করুন। ডিফল্টরূপে, ডেটা থ্রেডগুলির মধ্যে ভাগ করা হয়।
থ্রেডলোকাল ভেরিয়েবল ব্যবহার করা যেতে পারে এমন দুটি ব্যবহারের ক্ষেত্রে -
1- যখন আমাদের একটি থ্রেডের সাথে রাষ্ট্রের সংযুক্তি প্রয়োজন (যেমন, কোনও ব্যবহারকারী আইডি বা লেনদেন আইডি)। এটি সাধারণত কোনও ওয়েব অ্যাপ্লিকেশনটির সাথে ঘটে থাকে যে কোনও সার্লেলে যাওয়ার প্রতিটি অনুরোধের সাথে এটির একটি অনন্য ট্রানজেকশন আইডি যুক্ত থাকে।
// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
নোট করুন যে এখানেInitial সহ পদ্ধতিটি ল্যাম্বডা এক্সপ্রেশন ব্যবহার করে প্রয়োগ করা হয়।
2- আর একটি ব্যবহারের ক্ষেত্রে হ'ল যখন আমরা কোনও থ্রেড নিরাপদ দৃষ্টান্ত পেতে চাই এবং সিঙ্ক্রোনাইজেশনের সাথে পারফরম্যান্স ব্যয় বেশি হওয়ায় আমরা সিঙ্ক্রোনাইজেশন ব্যবহার করতে চাই না। সিম্পলডেট ফরমেট ব্যবহার করা হয় এমন একটি ক্ষেত্রে। যেহেতু সিম্পলডিটফর্ম্যাটটি থ্রেড নিরাপদ নয় তাই এটিকে থ্রেডটি নিরাপদ করার জন্য আমাদের ব্যবস্থা দিতে হবে।
public class ThreadLocalDemo1 implements Runnable {
// threadlocal variable is created
private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue(){
System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
return new SimpleDateFormat("dd/MM/yyyy");
}
};
public static void main(String[] args) {
ThreadLocalDemo1 td = new ThreadLocalDemo1();
// Two threads are created
Thread t1 = new Thread(td, "Thread-1");
Thread t2 = new Thread(td, "Thread-2");
t1.start();
t2.start();
}
@Override
public void run() {
System.out.println("Thread run execution started for " + Thread.currentThread().getName());
System.out.println("Date formatter pattern is " + dateFormat.get().toPattern());
System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
}
}
জাভা 8 রিলিজ হওয়ার পরে, আরম্ভ করার আরও ঘোষিত উপায় রয়েছে ThreadLocal
:
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
জাভা 8 রিলিজ হওয়া পর্যন্ত আপনাকে নিম্নলিখিতগুলি করতে হয়েছিল:
ThreadLocal<String> local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "init value";
}
};
তদুপরি, যদি শ্রেণীর ইনস্ট্যান্টেশন পদ্ধতি (কনস্ট্রাক্টর, ফ্যাক্টরি পদ্ধতি) ব্যবহার করা ThreadLocal
হয় তবে কোনও প্যারামিটার না নিলে আপনি কেবল পদ্ধতি রেফারেন্স (জাভা 8 তে প্রবর্তিত) ব্যবহার করতে পারেন:
class NotThreadSafe {
// no parameters
public NotThreadSafe(){}
}
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
দ্রষ্টব্য:
মূল্যায়ন অলস কারণ যেহেতু আপনি java.util.function.Supplier
ল্যাম্বদা পাশ করছেন কেবল তখনই ThreadLocal#get
ডাকা হয় তবে মানটির আগে মূল্যায়ন হয় না evalu
থ্রেডলোকাল প্যাটার্নটি সম্পর্কে আপনাকে খুব সাবধানতা অবলম্বন করতে হবে। ফিলের মতো উল্লেখযোগ্য কিছু নীচের দিক রয়েছে, তবে একটি যা উল্লেখ করা হয়নি তা হ'ল থ্রডলোকাল প্রসঙ্গটি সেট করে এমন কোডটি "পুনরায় প্রবেশকারী" নয় তা নিশ্চিত করা।
খারাপ জিনিসগুলি ঘটতে পারে যখন তথ্য সেট করে কোডটি দ্বিতীয় বা তৃতীয় বার চালিত হয় কারণ আপনার থ্রেডের তথ্যটি যখন আপনি প্রত্যাশা করেননি তখন তা পরিবর্তন করতে শুরু করতে পারে। সুতরাং থ্রেডলোকাল তথ্যটি পুনরায় সেট করার আগে সেট করা হয়নি তা নিশ্চিত হয়ে নিন।
ThreadLocal
নিদর্শন নয়। যদি আপনি করেন F(){ member=random(); F2(); write(member); }
এবং এফ 2 কোনও নতুন মান সহ সদস্যকে ওভাররাইড করে, তবে অবশ্যই write(member)
আপনি সম্পাদনা করা নম্বরটি আর লিখবেন না random()
। এটি আক্ষরিক অর্থেই সাধারণ জ্ঞান। একইভাবে, আপনি যদি করেন F(){ F(); }
, তবে আপনার অসীম লুপের সাথে gd ভাগ্য! এটি সর্বত্র সত্য এবং এটি নির্দিষ্ট নয় ThreadLocal
।
কখন?
যখন কোনও বস্তু থ্রেড-নিরাপদ নয়, সিঙ্ক্রোনাইজেশনের পরিবর্তে যা স্কেলিবিলিটি বাধাগ্রস্থ করে, প্রতিটি থ্রেডকে একটি করে বস্তু দিন এবং এটি থ্রেডের সুযোগ রাখুন, যা থ্রেডলোকাল। বেশিরভাগ ক্ষেত্রে ব্যবহৃত তবে থ্রেড-নিরাপদ অবজেক্টগুলির মধ্যে একটি হ'ল ডেটাবেস সংযোগ এবং জেএমএস সংযোগ।
কীভাবে?
একটি উদাহরণ হ'ল বসন্ত কাঠামো থ্রেডলোকাল ভেরিয়েবলগুলিতে এই সংযোগের জিনিসগুলি রেখে পর্দার আড়ালে লেনদেন পরিচালনার জন্য থ্রেডলোকালকে প্রচুর ব্যবহার করে। উচ্চ স্তরে, যখন কোনও লেনদেন শুরু হয় এটি সংযোগ পায় (এবং স্বয়ংক্রিয় প্রতিশ্রুতি অক্ষম করে) এবং থ্রেডলোকলে রাখে। পরবর্তী ডিবি কল এ এটি ডিবি সাথে যোগাযোগের জন্য একই সংযোগ ব্যবহার করে। শেষে, এটি থ্রেডলোকাল থেকে সংযোগ গ্রহণ করে এবং লেনদেনের প্রতিশ্রুতি দেয় (বা রোলব্যাক) এবং সংযোগটি প্রকাশ করে।
আমি মনে করি লগ 4 জে এমডিসি বজায় রাখার জন্য থ্রেডলোকালও ব্যবহার করে।
ThreadLocal
দরকারী, যখন আপনি কিছু স্থিতি রাখতে চান যা বিভিন্ন থ্রেডের মধ্যে ভাগ করা উচিত নয় তবে এটি পুরো জীবনকালে প্রতিটি থ্রেড থেকে অ্যাক্সেসযোগ্য হওয়া উচিত।
উদাহরণস্বরূপ, একটি ওয়েব অ্যাপ্লিকেশনটি কল্পনা করুন, যেখানে প্রতিটি অনুরোধটি আলাদা থ্রেড দ্বারা পরিবেশন করা হয়। কল্পনা করুন যে প্রতিটি অনুরোধের জন্য আপনার একাধিকবার ডেটা টুকরো দরকার যা গণনা করা বেশ ব্যয়বহুল। তবে, প্রতিটি ইনকামিং অনুরোধের জন্য সেই ডেটা পরিবর্তিত হতে পারে, যার অর্থ আপনি একটি সরল ক্যাশে ব্যবহার করতে পারবেন না। এই সমস্যার একটি সহজ, দ্রুত সমাধান ThreadLocal
হ'ল এই ডেটাতে একটি ভেরিয়েবল হোল্ড অ্যাক্সেস থাকতে হবে, যাতে প্রতিটি অনুরোধের জন্য আপনাকে একবারেই এটি গণনা করতে হবে। অবশ্যই, এই সমস্যাটি ব্যবহার না করেও সমাধান করা যেতে পারে ThreadLocal
, তবে আমি এটি উদাহরণের উদ্দেশ্যে তৈরি করেছি।
এটি বলেছিল, মনে রাখবেন যে ThreadLocal
এসগুলি মূলত বৈশ্বিক রাষ্ট্রের একটি রূপ। ফলস্বরূপ, এটির অন্যান্য অনেকগুলি প্রভাব রয়েছে এবং অন্যান্য সমস্ত সম্ভাব্য সমাধান বিবেচনা করার পরেই এটি ব্যবহার করা উচিত।
ThreadLocal
অবজেক্ট ক্ষেত্র হিসাবে ব্যবহার করতে পারেন ...
ThreadLocal
, যেখানে এটি বিশ্বব্যাপী অ্যাক্সেসযোগ্য ছিল। যেমনটি আপনি বলেছেন, এটি দৃশ্যমানতা সীমাবদ্ধ করে এখনও শ্রেণিকক্ষ হিসাবে ব্যবহার করা যেতে পারে।
এখানে সত্যিই নতুন কিছু নয়, তবে আমি আজ আবিষ্কার করেছি যে ThreadLocal
কোনও ওয়েব অ্যাপ্লিকেশনটিতে বিনের বৈধকরণ ব্যবহার করার সময় এটি খুব কার্যকর। বৈধতা বার্তা স্থানীয়, কিন্তু ডিফল্ট ব্যবহার দ্বারা Locale.getDefault()
। আপনি এটির Validator
সাথে অন্যটি কনফিগার করতে পারেন MessageInterpolator
, তবে Locale
কখন ফোন করবেন তা নির্দিষ্ট করার কোনও উপায় নেই validate
। সুতরাং আপনি একটি স্ট্যাটিক তৈরি করতে পারেন ThreadLocal<Locale>
(বা এখনও আরও ভাল, আপনার প্রয়োজন হতে পারে এমন অন্যান্য জিনিসগুলির সাথে একটি সাধারণ ধারক ThreadLocal
এবং তারপরে আপনার কাস্টমটি MessageInterpolator
বেছে নিতে পারেন Locale
। পরবর্তী পদক্ষেপটি ServletFilter
একটি সেশন মান ব্যবহার করে এমন একটি লিখন বা request.getLocale()
লোকেল এবং স্টোর সংগ্রহ করতে হবে এটা আপনার ThreadLocal
রেফারেন্সে।
@ অজ্ঞাত (গুগল) দ্বারা যেমন উল্লেখ করা হয়েছিল, এর ব্যবহার হ'ল একটি বৈশ্বিক পরিবর্তনশীল সংজ্ঞায়িত করতে যেখানে রেফারেন্সকৃত মান প্রতিটি থ্রেডে অনন্য হতে পারে। এটি ব্যবহারের ক্ষেত্রে সাধারণত প্রবাহের বর্তমান থ্রেডের সাথে যুক্ত কিছু প্রাসঙ্গিক তথ্য সংরক্ষণ করা হয়।
জাভা EE সচেতন নয় এমন ক্লাসগুলিতে ব্যবহারকারীর পরিচয় দেওয়ার জন্য আমরা এটি একটি জাভা EE পরিবেশে ব্যবহার করি (এইচটিটিপিএসশন, বা ইজেবি সেশনকন্টেক্সট অ্যাক্সেস নেই)। এই পদ্ধতিটি, যা সুরক্ষা ভিত্তিক ক্রিয়াকলাপগুলির জন্য পরিচয় ব্যবহার করে, প্রতিটি পদ্ধতি কলটিতে স্পষ্টতই এটি পাস না করে যে কোনও জায়গা থেকে পরিচয়টি অ্যাক্সেস করতে পারে।
বেশিরভাগ জাভা EE কলগুলির ক্রিয়াকলাপের অনুরোধ / প্রতিক্রিয়া চক্র এই ধরণের ব্যবহারকে সহজ করে তোলে কারণ এটি থ্রেডলোকাল সেট এবং আনসেট করতে ভাল সংজ্ঞায়িত এন্ট্রি এবং প্রস্থান পয়েন্ট দেয়।
থ্রেডলোকাল নন সিঙ্ক্রোনাইজ পদ্ধতিতে একাধিক থ্রেড দ্বারা বিভাজ্য অবজেক্টটি অ্যাক্সেস নিশ্চিত করবে, যার অর্থ মিউটলেবল অবজেক্টটি পদ্ধতির মধ্যে অপরিবর্তনীয় হতে পারে।
প্রতিটি থ্রেডে এটির অ্যাক্সেসের চেষ্টা করার জন্য পরিবর্তনীয় অবজেক্টের নতুন উদাহরণ দিয়ে এটি অর্জন করা হয়েছে। সুতরাং এটি প্রতিটি থ্রেডে স্থানীয় অনুলিপি। এটি স্থানীয় ভেরিয়েবলের মতো অ্যাক্সেস করার পদ্ধতিতে উদাহরণ পরিবর্তনযোগ্য তৈরির ক্ষেত্রে কিছুটা হ্যাক। আপনি যেমন সচেতন পদ্ধতি হিসাবে স্থানীয় পরিবর্তনশীল কেবল থ্রেডের জন্য উপলব্ধ, তবে একটি পার্থক্য; থ্রেডলোকালের সাথে ভাগ করা মিউটটেবল অবজেক্টটি যতক্ষণ না আমরা এটি পরিষ্কার করি ততক্ষণ একাধিক পদ্ধতিতে থ্রিডলোকলের সাথে ভাগ করা পরিবর্তনীয় অবজেক্টটি উপস্থিত হয়ে গেলে পদ্ধতি স্থানীয় ভেরিয়েবলগুলি থ্রেডে উপলভ্য হবে না।
সংজ্ঞানুসারে:
জাভার থ্রেডলোকাল ক্লাস আপনাকে এমন ভেরিয়েবলগুলি তৈরি করতে সক্ষম করে যা কেবল একই থ্রেড দ্বারা পড়া এবং লেখা যায় written সুতরাং, এমনকি যদি দুটি থ্রেড একই কোডটি চালাচ্ছে এবং কোডটিতে একটি থ্রেডলোকাল ভেরিয়েবলের উল্লেখ রয়েছে, তবে দুটি থ্রেড একে অপরের থ্রেডলোকাল ভেরিয়েবলগুলি দেখতে পাবে না।
Thread
জাভা প্রতিটি এটিতে থাকে ThreadLocalMap
।
কোথায়
Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
থ্রেডলোকাল অর্জন করা:
এখন থ্রেডলোকালের জন্য একটি মোড়কের ক্লাস তৈরি করুন যা নীচের (যেমন বা বাইরে initialValue()
) মত পরিবর্তনীয় বস্তুটি ধরে রাখতে চলেছে ।
এখন এই র্যাপারটির গেটর এবং সেটার মিউটেটেবল অবজেক্টের পরিবর্তে থ্রেডলোকাল ইনসেস্টে কাজ করবে।
থ্রেডলোকালের গেটার যদি () এর থ্রেডলোক্যাল্যাপের সাথে কোনও মান খুঁজে না পায় Thread
; তারপরে এটি থ্রেডের সাথে সম্মতভাবে তার ব্যক্তিগত অনুলিপি পেতে প্রাথমিক মান () কে অনুরোধ করবে।
class SimpleDateFormatInstancePerThread {
private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
UUID id = UUID.randomUUID();
@Override
public String toString() {
return id.toString();
};
};
System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
return dateFormat;
}
};
/*
* Every time there is a call for DateFormat, ThreadLocal will return calling
* Thread's copy of SimpleDateFormat
*/
public static DateFormat getDateFormatter() {
return dateFormatHolder.get();
}
public static void cleanup() {
dateFormatHolder.remove();
}
}
এখন wrapper.getDateFormatter()
ডাকব threadlocal.get()
এবং যে চেক করবে currentThread.threadLocalMap
ধারণ করে এই (threadlocal) উদাহরণস্বরূপ।
যদি হ্যাঁ সংশ্লিষ্ট থ্রেডলোকাল দৃষ্টান্তের জন্য মান (সিম্পলডেট ফরমেট) ফেরান
তবে অন্যথায় এই থ্রডলোকাল উদাহরণ, ইনিশিয়ালভ্যালু () সহ মানচিত্র যুক্ত করুন।
এর সাথে এই পরিবর্তনীয় শ্রেণিতে থ্রেড সুরক্ষা অর্জন করা হয়েছে; প্রতিটি থ্রেড তার নিজস্ব পরিবর্তনীয় উদাহরণ সহ একই থ্রেডলোকাল উদাহরণ সহ কাজ করছে। মানে সমস্ত থ্রেড কী হিসাবে একই থ্রেডলোকাল উদাহরণটি ভাগ করে নেবে, তবে মান হিসাবে বিভিন্ন সরলডেট ফরমেট উদাহরণটি।
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
থ্রেড-লোকাল ভেরিয়েবলগুলি প্রায়শই পরিবর্তিত সিঙ্গললেট বা গ্লোবাল ভেরিয়েবলের উপর ভিত্তি করে ডিজাইনে ভাগ করা রোধ করতে ব্যবহৃত হয়।
আপনি যখন কোনও সংযোগ পুল ব্যবহার করছেন না তখন এটি প্রতিটি থ্রেডের জন্য পৃথক জেডিবিসি সংযোগ তৈরি করার মতো পরিস্থিতিতে ব্যবহার করা যেতে পারে।
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
আপনি যখন getConnection কল করবেন, তখন এটি সেই থ্রেডের সাথে যুক্ত একটি সংযোগ ফিরিয়ে দেবে date এটি ডেট ফর্ম্যাট, লেনদেন প্রসঙ্গে যেমন অন্য বৈশিষ্ট্যগুলির সাথেও করা যেতে পারে যা আপনি থ্রেডগুলির মধ্যে ভাগ করতে চান না।
আপনি এটির জন্য স্থানীয় ভেরিয়েবলগুলিও ব্যবহার করতে পারতেন তবে এই সংস্থানগুলি সাধারণত সৃষ্টিতে সময় নেয়, তাই আপনি যখনই তাদের সাথে কিছু ব্যবসায়িক যুক্তি সম্পাদন করেন আপনি বার বার এগুলি তৈরি করতে চান না। তবে থ্রেডলোকাল মানগুলি থ্রেড অবজেক্টে নিজেই সংরক্ষণ করা হয় এবং থ্রেডটি আবর্জনা সংগ্রহ করার সাথে সাথে এই মানগুলিও চলে যায়।
এই লিঙ্কটি থ্রেডলোকালের ব্যবহার খুব ভালভাবে ব্যাখ্যা করে।
ThreadLocal জাভা শ্রেণী আপনি ভেরিয়েবল যে শুধুমাত্র পাঠযোগ্য এবং একই থ্রেড লিখতে পারবেন তৈরি করতে সক্ষম করে। সুতরাং, এমনকি যদি দুটি থ্রেড একই কোডটি চালাচ্ছে এবং কোডটিতে একটি থ্রেডলোকাল ভেরিয়েবলের উল্লেখ রয়েছে, তবে দুটি থ্রেড একে অপরের থ্রেডলোকাল ভেরিয়েবলগুলি দেখতে পাবে না।
মাল্ট্রিথ্রেড কোডে সিম্পলডেট ফরমেটের মতো শ্রেণি সহায়ক ব্যবহার করার জন্য 3 টি পরিস্থিতি রয়েছে , যা থ্রেডলোকাল সেরা ব্যবহার করে
প্রেক্ষাপটে
1- লক বা সিঙ্ক্রোনাইজেশন প্রক্রিয়ার সাহায্যে লাইক শেয়ার অবজেক্ট ব্যবহার করা যা অ্যাপ্লিকেশনটিকে ধীর করে তোলে
2- একটি পদ্ধতির অভ্যন্তরে স্থানীয় অবজেক্ট হিসাবে ব্যবহার করা
এই দৃশ্যে , যদি আমাদের 4 টি থ্রেড থাকে তবে প্রত্যেকে 1000 বার একটি পদ্ধতি কল করে তারপরে আমাদের
4000 সিম্পলডেটফর্ম্যাট অবজেক্ট তৈরি হয়েছে এবং জিসি সেগুলি মুছে ফেলার জন্য অপেক্ষা করছে
3- থ্রেডলোকাল ব্যবহার করে
যদি আমাদের 4 টি থ্রেড থাকে এবং আমরা প্রতিটি থ্রেডকে একটি করে সিম্পলডেট ফরমেট উদাহরণ দিয়েছি
যাতে আমাদের 4 টি থ্রেড , সরলডেট ফরমেটের 4 টি অবজেক্ট রয়েছে।
লক প্রক্রিয়া এবং অবজেক্ট তৈরি এবং ধ্বংসের প্রয়োজন নেই। (শুভ সময় জটিলতা এবং স্পেস জটিলতা)
[রেফারেন্সের জন্য] থ্রেডলোকল শেয়ার্ড অবজেক্টের আপডেট সমস্যাগুলি সমাধান করতে পারে না। এটি একটি স্ট্যাটিক ট্র্যাডলোকাল অবজেক্ট ব্যবহার করার পরামর্শ দেওয়া হয় যা একই থ্রেডে সমস্ত ক্রিয়াকলাপ দ্বারা ভাগ করা হয়। [বাধ্যতামূলক] অপসারণ () পদ্ধতিটি অবশ্যই থ্রেডলোকাল ভেরিয়েবল দ্বারা প্রয়োগ করা উচিত, বিশেষত থ্রেড পুল ব্যবহার করার সময় যেখানে থ্রেড প্রায়শই পুনরায় ব্যবহার করা হয়। অন্যথায়, এটি পরবর্তী ব্যবসায়ের যুক্তিগুলিকে প্রভাবিত করতে পারে এবং মেমরি ফাঁস হওয়ার মতো অপ্রত্যাশিত সমস্যার কারণ হতে পারে।
ক্যাশিং, কোনও সময় আপনাকে একই মান প্রচুর পরিমাণে গণনা করতে হবে যাতে কোনও পদ্ধতিতে ইনপুটগুলির শেষ সেটটি সংরক্ষণ করে ফলাফলটি আপনি কোডটি গতিতে করতে পারেন। থ্রেড লোকাল স্টোরেজ ব্যবহার করে আপনি লক করার বিষয়ে ভাবেন না avoid
থ্রেডলোকালটি কেবল থ্রেডগুলির জন্য পৃথক পৃথক স্টোরেজ স্পেস সরবরাহের জন্য জেভিএম দ্বারা একটি বিশেষভাবে সরবরাহিত কার্যকারিতা। উদাহরণস্বরূপ স্কোপড ভেরিয়েবলের মানটি কেবল একটি শ্রেণীর প্রদত্ত উদাহরণের সাথে আবদ্ধ। প্রতিটি বস্তুর একমাত্র মান রয়েছে এবং তারা একে অপরের মান দেখতে পারে না। থ্রেডলোকাল ভেরিয়েবলের ধারণাটিও একই, তারা বস্তুর দৃষ্টিকোণে থ্রেডে স্থানীয়, অন্য থ্রেড যা তৈরি করেছে, তা দেখতে পারা যায় না। এখানে দেখো
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
public static void main(String[] args) {
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
new Thread(() -> IntStream.range(1, 3).forEach(i -> {
System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
})).start();
}
}
থ্রেডলোকাল শূন্য ব্যয় সহ বস্তুর পুনঃব্যবহারযোগ্যতা অর্জনের জন্য খুব সহজ উপায় সরবরাহ করে।
আমার এমন এক পরিস্থিতি ছিল যেখানে প্রতিটি আপডেটের বিজ্ঞপ্তিতে একাধিক থ্রেড মিউটেটেবল ক্যাশে একটি চিত্র তৈরি করে।
আমি প্রতিটি থ্রেডে একটি থ্রেডলোকাল ব্যবহার করেছি এবং তারপরে প্রতিটি থ্রেডকে পুরানো চিত্রটি পুনরায় সেট করতে হবে এবং তারপরে প্রতিটি আপডেটের বিজ্ঞপ্তিতে ক্যাশে থেকে আবার এটি আপডেট করতে হবে।
অবজেক্ট পুলগুলি থেকে সাধারণ পুনরায় ব্যবহারযোগ্য বস্তুর সাথে থ্রেড সুরক্ষা ব্যয় যুক্ত থাকে, যদিও এই পদ্ধতির কোনও নেই।
থ্রেডলোকাল ভেরিয়েবলের অনুভূতি পেতে এই ছোট্ট উদাহরণটি ব্যবহার করে দেখুন:
public class Book implements Runnable {
private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);
private final String bookName; // It is also the thread's name
private final List<String> words;
public Book(String bookName, List<String> words) {
this.bookName = bookName;
this.words = Collections.unmodifiableList(words);
}
public void run() {
WORDS.get().addAll(words);
System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
}
public static void main(String[] args) {
Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
t1.start();
t2.start();
}
}
কনসোল আউটপুট, থ্রেড BookA প্রথমে সম্পন্ন হলে:
ফলাফল BookA: 'wordA1, wordA2, wordA3'।
ফলাফল বুকবি: 'ওয়ার্ডবি 1, ওয়ার্ডবি 2'।
কনসোল আউটপুট, থ্রেড বুকবি যদি প্রথমে করা হয়:
ফলাফল বুকবি: 'ওয়ার্ডবি 1, ওয়ার্ডবি 2'।
ফলাফল BookA: 'wordA1, wordA2, wordA3'।