java - জাভা একটি সিঙ্গল্ট প্যাটার্ন বাস্তবায়ন করার একটি কার্যকর উপায় কি?




singleton design-patterns (20)

অনুসরণ 3 বিভিন্ন পদ্ধতির

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) ডবল চেক লকিং / Lazy লোড হচ্ছে

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) স্ট্যাটিক কারখানা পদ্ধতি

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

জাভা একটি সিঙ্গল্ট প্যাটার্ন বাস্তবায়ন করার একটি কার্যকর উপায় কি?


আপনি অলস লোডিং প্রয়োজন না হলে সহজভাবে চেষ্টা করুন

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

আপনি যদি অলস লোডিং চান এবং আপনি আপনার সিঙ্গলটনকে থ্রেড-নিরাপদ হতে চান তবে ডবল পরীক্ষণের প্যাটার্নটি চেষ্টা করুন

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() { 
              if(null == instance) {
                  synchronized(Singleton.class) {
                      if(null == instance) {
                          instance = new Singleton();
                      }
                  }
               }
               return instance;
        }

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

দ্বিগুণ প্যাটার্ন প্যাটার্ন কাজ করার নিশ্চয়তা দেয় না (কম্পাইলারগুলির সাথে কিছু সমস্যা থাকার কারণে, আমি তার সম্পর্কে আরো কিছু জানি না।), আপনি সম্পূর্ণ GetInstance- পদ্ধতিটি সিঙ্ক্রোনাইজ করার চেষ্টা করতে পারেন বা আপনার সমস্ত সিলেটের জন্য একটি রেজিস্ট্রি তৈরি করতে পারেন।


আমি Enum সিঙ্গলটন বলতে হবে

জাভা এ Enum ব্যবহার করে Singleton সাধারণত enum সিঙ্গলটন ঘোষণা করার উপায়। Enum সিঙ্গলটন ইনস্ট্যান্স পরিবর্তনশীল এবং উদাহরণ পদ্ধতি থাকতে পারে। সাদৃশ্যের জন্য, এছাড়াও নোট করুন যে যদি আপনি যে পদ্ধতির থ্রেড সুরক্ষা নিশ্চিত করতে চান তার চেয়ে কোনও ইনস্ট্যান্স পদ্ধতি ব্যবহার করেন তবে এটি বস্তুর অবস্থাটিকে প্রভাবিত করে।

একটি enum ব্যবহার বাস্তবায়ন করা খুব সহজ এবং serializable বস্তু সম্পর্কিত কোন ত্রুটি আছে, যা অন্যান্য উপায়ে circumvented আছে।

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

আপনি Singleton.INSTANCE মাধ্যমে এটি অ্যাক্সেস করতে পারেন, Singleton.INSTANCE getInstance() পদ্ধতি কল করার চেয়ে অনেক সহজ।

1.12 Enum কনস্ট্যান্ট সিরিয়ালাইজেশন

Enum constants সাধারণ serializable বা বহিরাগত বস্তুর চেয়ে ভিন্ন serialized হয়। একটি enum ধ্রুবক ধারাবাহিক ফর্ম তার নামের সম্পূর্ণরূপে গঠিত; ধ্রুবক ক্ষেত্রের মান ফর্ম উপস্থিত হয় না। একটি ObjectOutputStream ধ্রুবক serialize করতে, ObjectOutputStream ধ্রুবকের নাম পদ্ধতি দ্বারা ফেরত মান লিখে। একটি ObjectInputStream ধ্রুবক ObjectInputStream করতে, ObjectInputStream স্ট্রিম থেকে ধ্রুবক নাম পড়তে; deserialized ধ্রুবক তারপর java.lang.Enum.valueOf পদ্ধতি কল করে, ধ্রুবক এর enum টাইপ পাশাপাশি প্রাপ্ত ধ্রুবক নাম বরাবর আর্গুমেন্ট হিসাবে পাস করে। অন্যান্য ধারাবাহিক বা বহির্মুখী বস্তুর মতো, এনম স্টোনেন্টগুলি ধারাবাহিকভাবে স্ট্রিমিয়াল স্ট্রিমের পরে প্রদর্শিত রেফারেন্সগুলির লক্ষ্য হিসাবে কাজ করতে পারে।

যে প্রক্রিয়া দ্বারা enum constants সিরিয়ালাইজ করা হয় তা কাস্টমাইজ করা যায় না: যেকোনো ক্লাস-নির্দিষ্ট writeObject , readObject , readObjectNoData , writeReplace , এবং readResolve প্রকারের দ্বারা সংজ্ঞায়িত readResolve পদ্ধতিগুলি সিরিয়ালাইজেশন এবং ডেসারিয়ালাইজেশনের সময় উপেক্ষা করা হয়। একইভাবে, কোনও serialPersistentFields বা serialVersionUID ফিল্ড ঘোষণাগুলি উপেক্ষা করা হয় - সমস্ত serialVersionUID প্রকারের একটি নির্দিষ্ট serialVersionUID এর 0L । সিরিয়ালাইজযোগ্য ক্ষেত্রগুলি এবং এননাম ধরনগুলির জন্য তথ্য নথিভুক্ত করা অপ্রয়োজনীয়, কারণ প্রেরিত ডেটাতে কোনও বৈচিত্র নেই।

ওরাকল ডক্স থেকে উদ্ধৃত

প্রচলিত readObject() সাথে আরেকটি সমস্যা হল যে একবার আপনি Serializable ইন্টারফেসটি বাস্তবায়ন করলে, তারা আর সিঙ্গলটন থাকবে না কারণ readObject() পদ্ধতিটি জাভাতে কন্সট্রকটারের মতো একটি নতুন উদাহরণ সবসময় প্রদান করে। এটি readResolve() ব্যবহার করে এবং নীচের মত readResolve() দ্বারা প্রতিস্থাপিত করে নতুন তৈরি হওয়া উদাহরণটি readResolve() এড়ানো যেতে পারে

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

আপনার সিঙ্গল্টন ক্লাসটি যদি বজায় রাখতে পারে তবে এটি আরও জটিল হয়ে উঠতে পারে, যেমনটি আপনাকে তাদের ক্ষণস্থায়ী করতে হবে, কিন্তু Enum Singleton এ, সিরিয়ালাইজেশান JVM দ্বারা নিশ্চিত করা হয়।

ভাল পড়ুন

  1. সিঙ্গলটন প্যাটার্ন
  2. Enums, Singletons এবং Deserialization
  3. ডবল চেক লকিং এবং Singleton প্যাটার্ন

আমি আমার singletons পরিচালনা করার জন্য স্প্রিং ফ্রেমওয়ার্ক ব্যবহার। এটি ক্লাসের "সিঙ্গলটন-নেস" প্রয়োগ করে না (যা আপনি যদি সত্যিই একাধিক বর্গ লোডার জড়িত থাকে তবে তা করতে পারেন না) তবে বিভিন্ন ধরণের বস্তু তৈরির জন্য বিভিন্ন কারখানাগুলি তৈরি ও কনফিগার করার একটি সহজ উপায় সরবরাহ করে।


উইকিপিডিয়াতে জাভাতে এককটি examples রয়েছে। জাভা 5 বাস্তবায়নটি বেশ সুন্দর দেখায় এবং থ্রেড-নিরাপদ (ডাবল-চেক করা লকিং প্রয়োগ করা হয়)।


একটি enum ব্যবহার করুন:

public enum Foo {
    INSTANCE;
}

গুগল আই / ও ২008 এ তার কার্যকরী জাভা রিলোডলোড কথোপকথনে যিহোশূয় ব্লোক এই পদ্ধতিটি ব্যাখ্যা করেছেন: ভিডিও লিঙ্ক করুন । তার উপস্থাপনার 30-32 স্লাইডগুলিও দেখুন (কার্যকর_java_reloaded.pdf):

একটি Serializable Singleton বাস্তবায়ন করার অধিকার উপায়

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

সম্পাদনা: "কার্যকর জাভা" এর একটি অনলাইন অংশ বলে:

"এই পদ্ধতিটি সরকারী ক্ষেত্রের পদ্ধতির সমতুল্য সমতুল্য, ব্যতীত এটি আরও সংক্ষিপ্ত, বিনামূল্যে সিরিয়ালাইজেশন যন্ত্রপাতি সরবরাহ করে এবং অত্যাধুনিক সিরিয়ালাইজেশন বা প্রতিফলন আক্রমণের মুখেও একাধিক তাত্ক্ষণিকতার বিরুদ্ধে লোহারক্ল্যাড গ্যারান্টি প্রদান করে। এই পদ্ধতিতে এখনও ব্যাপকভাবে গ্রহণ করা, একটি একক-উপাদান enum টাইপ একটি একক্টন বাস্তবায়ন করার সেরা উপায় । "


ব্যবহারের উপর নির্ভর করে, অনেক "সঠিক" উত্তর আছে।

যেহেতু java5 এটি করার সর্বোত্তম উপায় হল একটি enum ব্যবহার করা:

public enum Foo {
   INSTANCE;
}

প্রাক java5, সবচেয়ে সহজ ক্ষেত্রে হয়:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

চলুন কোড উপর যান। প্রথম, আপনি ক্লাস চূড়ান্ত হতে চান। এই ক্ষেত্রে, আমি ব্যবহারকারীদের এটি final জানাতে final কীওয়ার্ড ব্যবহার করেছি। তারপরে ব্যবহারকারীকে তাদের নিজস্ব Foo তৈরি করতে বাধা দেওয়ার জন্য আপনাকে কন্সট্রকটরকে ব্যক্তিগত করতে হবে। কনস্ট্রাক্টর থেকে ব্যতিক্রম হ্রাস ব্যবহারকারীদের দ্বিতীয় Foo তৈরি প্রতিফলন ব্যবহার করতে বাধা দেয়। তারপরে আপনি একটি private static final Foo ক্ষেত্রটি তৈরি করতে পারেন যা কেবলমাত্র উদাহরণটি ধরে রাখতে এবং public static Foo getInstance() ফিরে যাওয়ার জন্য একটি public static Foo getInstance() পদ্ধতি তৈরি করে। জাভা স্পেসিফিকেশনটি নিশ্চিত করে যে বর্গটি প্রথমে ব্যবহৃত হলে কেবল কন্সট্রাকটরকে বলা হয়।

যখন আপনার কাছে একটি খুব বড় বস্তু বা ভারী নির্মাণ কোড থাকে এবং এটিতে অন্য অ্যাক্সেসযোগ্য স্ট্যাটিক পদ্ধতি বা ক্ষেত্র থাকে যা একটি উদাহরণের আগে ব্যবহার করা যেতে পারে, তখন কেবল তখনই আপনাকে অলস সূচনাটি ব্যবহার করতে হবে।

আপনি ইনস্ট্যান্স লোড করতে একটি private static class ব্যবহার করতে পারেন। কোড তারপর দেখতে হবে:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

যেহেতু লাইন private static final Foo INSTANCE = new Foo(); ক্লাস FooLoader আসলে ব্যবহৃত হয় যখন শুধুমাত্র মৃত্যুদন্ড কার্যকর করা হয়, এই অলস তাত্ক্ষণিক যত্ন নেয়, এবং এটা থ্রেড নিরাপদ নিশ্চিত করা হয়।

যখন আপনি আপনার অবজেক্টটি সিরিয়ালাইজ করতে সক্ষম হবেন তখন আপনি নিশ্চিত করতে হবে যে deserialization একটি অনুলিপি তৈরি করবে না।

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

পদ্ধতিটি readResolve() নিশ্চিত করবে যে কেবলমাত্র ইনস্ট্যানশনটি ফেরত দেওয়া হবে, এমনকি যখন আপনার প্রোগ্রামের পূর্ববর্তী চালানে বস্তুটি ক্রমিক করা হয়েছিল।


সত্যিই এটি কেনার আগে আপনি একটি singleton প্রয়োজন কেন বিবেচনা। আপনি যদি জাভাতে একক গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল গুগল এ গুগল গুগল গুগল গুগল গুগল

ব্যক্তিগতভাবে আমি অনেক কারণের জন্য যতটা সম্ভব একটানা এড়িয়ে চলার চেষ্টা করি, আবার বেশিরভাগ ক্ষেত্রেই একক গুগলগ্লগের মাধ্যমে পাওয়া যায়। আমি মনে করি বেশিরভাগ ক্ষেত্রেই এককভাবে নির্যাতন করা হয় কারণ তারা সবাই বোঝে সহজ, তারা একটি ওও নকশাতে "বিশ্বব্যাপী" তথ্য পাওয়ার জন্য একটি প্রক্রিয়া হিসাবে ব্যবহার করা হয় এবং তারা ব্যবহার করা হয় কারণ এটি বস্তুর জীবনচক্র পরিচালনাকে বাধাগ্রস্ত করা সহজ (অথবা সত্যিই আপনি ভেতরের থেকে একটি কিভাবে করতে পারেন সম্পর্কে চিন্তা)। চমৎকার মিডলগাউডের জন্য ইনভারসন অফ কন্ট্রোল (আইওসি) বা নির্ভরতা ইনজেকশন (ডিআই) মত বিষয়গুলি দেখুন।

আপনি যদি সত্যিই একটি প্রয়োজন তবে উইকিপিডিয়া একটি সিঙ্গলটন সঠিক বাস্তবায়ন একটি ভাল উদাহরণ আছে।


স্টু থম্পসন পোস্ট করেছেন জাভা 5.0 এবং পরে বৈধ। কিন্তু আমি এটা ব্যবহার করতে পছন্দ করি না কারণ আমি মনে করি এটি ত্রুটিযুক্ত।

অস্থির বিবৃতি ভুলে যাওয়া সহজ এবং এটি কেন প্রয়োজনীয় তা বোঝা কঠিন। অস্থিরতা ছাড়াই এই কোডটি ডাবল-চেক করা লকিং অ্যান্টিপ্টার্টারের কারণে আর সুরক্ষিত হবে না। প্র্যাকটিস জাভা কনকুরেন্সের অনুচ্ছেদ 16.2.4 এর মধ্যে আরও দেখুন। সংক্ষেপে: এই প্যাটার্নটি (জাভা 5.0 এর আগে বা উদ্বায়ী বিবৃতি ছাড়া) বার বস্তুর একটি রেফারেন্স ফেরত দিতে পারে যা এখনও একটি ভুল অবস্থায় রয়েছে।

এই প্যাটার্ন কর্মক্ষমতা অপ্টিমাইজেশান জন্য উদ্ভাবিত হয়েছিল। কিন্তু এই সত্যিই একটি বাস্তব উদ্বেগের আর হয় না। নিম্নলিখিত অলস প্রারম্ভিককরণ কোড দ্রুত এবং -আমরা গুরুত্বপূর্ণ- পড়তে সহজ।

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

অলস সূচনা ভুলে যান, এটি খুব সমস্যাযুক্ত। এই সহজ সমাধান:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

Enum সিঙ্গলটন

থ্রেড-নিরাপদ যে সিঙ্গলটন বাস্তবায়ন করার সবচেয়ে সহজ উপায় Enum ব্যবহার করা হয়

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

এই কোড জাভা 1.5 এ Enum প্রবর্তনের পর কাজ করে

ডবল চেক লকিং

যদি আপনি একটি "ক্লাসিক" এককটিন কোড করতে চান যা বহুবিধ পরিবেশে কাজ করে (জাভা 1.5 থেকে শুরু করে) আপনি এটি ব্যবহার করতে হবে।

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class){
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance ;
  }
}

এটি 1.5 এর আগে থ্রেড-নিরাপদ নয় কারণ অস্থির শব্দটির বাস্তবায়ন ভিন্ন ছিল।

প্রাথমিক লোড হচ্ছে সিঙ্গলটন (জাভা 1.5 এর আগে এমনকি কাজ করে)

ক্লাসটি লোড হওয়া এবং থ্রেড সুরক্ষা প্রদান করে এই বাস্তবায়নটি সিঙ্গল্টনকে তাত্ক্ষণিক করে।

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}

সংস্করণ 1:

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton() {}
    public static synchronized MySingleton getInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}

অলস লোড হচ্ছে, ব্লকিং সঙ্গে সুরক্ষিত থ্রেড, কম কর্মক্ষমতা কারণ synchronized

সংস্করণ 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

অলস লোডিং, অ ব্লকিং, উচ্চ কর্মক্ষমতা সঙ্গে নিরাপদ থ্রেড।


আমি এখনও জাভা 1.5 এর পরে মনে করি, enum সবচেয়ে উপলব্ধ সিঙ্গল্টন বাস্তবায়ন উপলব্ধ কারণ এটি নিশ্চিত করে যে এমনকি মাল্টি থ্রেড পরিবেশগুলিতেও - শুধুমাত্র একটি উদাহরণ তৈরি করা হয়।

public enum Singleton{ INSTANCE; }

এবং আপনি সম্পন্ন করা হয় !!!


আমি কখনও দেখেছি সেরা সিঙ্গলটন প্যাটার্ন সরবরাহকারী ইন্টারফেস ব্যবহার করে।

  • এটা জেনেরিক এবং পুনর্ব্যবহারযোগ্য
  • এটা অলস সূচনা সমর্থন করে
  • এটি শুরু হওয়া পর্যন্ত এটি কেবলমাত্র সিঙ্ক্রোনাইজ করা হয়, তবে ব্লকিং সরবরাহকারীটি একটি অ-ব্লকিং সরবরাহকারীর সাথে প্রতিস্থাপিত হয়।

নিচে দেখ:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}

এককোন বস্তু তৈরি করার বিভিন্ন উপায়:

  1. যিহোশূয় ব্লক অনুযায়ী - Enum সেরা হবে।

  2. আপনি ডবল চেক লকিং ব্যবহার করতে পারেন।

  3. এমনকি অভ্যন্তরীণ স্ট্যাটিক ক্লাস ব্যবহার করা যেতে পারে।


সহজ একক্টন ক্লাস

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

এই খেলার জন্য একটু দেরী হতে পারে, কিন্তু একটি সিঙ্গল্ট বাস্তবায়ন কাছাকাছি অনেক বুদ্ধি আছে। ধারক প্যাটার্ন অনেক পরিস্থিতিতে ব্যবহার করা যাবে না। এবং একটি অস্থায়ী ব্যবহার যখন IMO - আপনি একটি স্থানীয় পরিবর্তনশীল ব্যবহার করা উচিত। আসুন শুরুতে শুরু করি এবং সমস্যাটির পুনরাবৃত্তি করি। আপনি কি বোঝাতে পারবেন।

প্রথম প্রচেষ্টাটি এমন কিছু দেখতে পারে:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

এখানে আমাদের মাইসিংল্টন ক্লাস রয়েছে যা INSTANCE নামক একটি ব্যক্তিগত স্ট্যাটিক সদস্য এবং getInstance () নামক একটি সার্বজনীন স্ট্যাটিক পদ্ধতি রয়েছে। প্রথমবার getInstance () বলা হয়, ইনস্ট্যান্স সদস্য নিল। তারপর প্রবাহ সৃষ্টি অবস্থায় পড়ে এবং MySingleton বর্গ একটি নতুন উদাহরণ তৈরি করবে। GetInstance এর পরবর্তী কলগুলি () পাওয়া যাবে যে INSTANCE পরিবর্তনশীল ইতিমধ্যে সেট করা আছে এবং সেইজন্য অন্য মাইসিংল্টন ইনস্ট্যান্স তৈরি করবেন না। এটি নিশ্চিত করে যে মাইসিংলেটনের একমাত্র উদাহরণ রয়েছে যা GetInstance () এর সকল কলকারীদের মধ্যে ভাগ করা হয়।

কিন্তু এই বাস্তবায়ন একটি সমস্যা আছে। একাধিক থ্রেডেড অ্যাপ্লিকেশনগুলির একক দৃষ্টান্ত তৈরির জন্য একটি জাতি শর্ত থাকবে। যদি একাধিক থ্রেড এক্সিকিউশনটি একই সময়ে (বা প্রায়) এ getInstance () পদ্ধতিটিকে আঘাত করে তবে তারা প্রত্যেকে INSTANCE সদস্যকে নিল হিসাবে দেখতে পাবে। এর ফলে প্রতিটি থ্রেড একটি নতুন মাইসিংটন ইনস্ট্যান্স তৈরি করবে এবং পরবর্তীতে INSTANCE সদস্য সেট করবে।

private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

এখানে আমরা getInstance () পদ্ধতিটি সিঙ্ক্রোনাইজ করার জন্য পদ্ধতি স্বাক্ষরে সিঙ্ক্রোনাইজড কীওয়ার্ড ব্যবহার করেছি। এই অবশ্যই আমাদের জাতি অবস্থা ঠিক করা হবে। থ্রেড এখন একটি সময়ে ব্লক এবং পদ্ধতি এক প্রবেশ করবে। কিন্তু এটি একটি কর্মক্ষমতা সমস্যা সৃষ্টি করে। এই বাস্তবায়নটি কেবলমাত্র একক ইনস্ট্যান্সের সৃষ্টিকে সিঙ্ক্রোনাইজ করে না, এটি readInst () সহ সমস্ত কলগুলিকে সিঙ্ক্রোনাইজ করে। তারা সহজেই INSTANCE মান ফেরত হিসাবে সমলয় করা প্রয়োজন পড়তে না। যেহেতু পঠনগুলি আমাদের কলগুলির বড় অংশটি তৈরি করবে (মনে রাখবেন, কেবলমাত্র প্রথম কলটিতে তাত্ক্ষণিকতা ঘটে), আমরা সমগ্র পদ্ধতিটি সিঙ্ক্রোনাইজ করে একটি অপ্রয়োজনীয় কর্মক্ষমতা আঘাত করব।

private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

এখানে আমরা পদ্ধতি স্বাক্ষর থেকে সিঙ্ক্রোনাইজেশন স্থানান্তরিত করেছি, একটি সিঙ্ক্রোনাইজড ব্লক যা মাইসিংল্টন ইনস্ট্যান্স তৈরির জন্য আবদ্ধ। কিন্তু এটা কি আমাদের সমস্যার সমাধান করে? আচ্ছা, আমরা আর পড়তে ব্লক করছি না, কিন্তু আমরা একটি পদক্ষেপ পিছিয়েও নিয়েছি। একাধিক থ্রেড একই সময়ে বা প্রায় কাছাকাছি getInstance () পদ্ধতিটি আঘাত করবে এবং তারা সমস্ত INSTANCE সদস্যকে নাল হিসাবে দেখতে পাবে। তারপর সেটি সিঙ্ক্রোনাইজড ব্লকটি আঘাত করবে যেখানে লকটি পাবে এবং ইনস্ট্যান্স তৈরি করবে। যে থ্রেডটি ব্লক থেকে বের হয়ে গেলে, অন্যান্য থ্রেডগুলি লকের জন্য দমন করবে, এবং প্রতিটি এক থ্রেড ব্লকের মাধ্যমে পড়ে এবং আমাদের ক্লাসের একটি নতুন উদাহরণ তৈরি করবে। তাই আমরা ঠিক যেখানে আমরা শুরু।

private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

এখানে আমরা ব্লক ভিতরে থেকে অন্য চেক ইস্যু। ইনস্ট্যান্স সদস্য ইতিমধ্যে সেট করা হয়েছে, আমরা প্রবর্তন বাদ দিতে হবে। এটি ডাবল-চেক করা লকিং বলা হয়।

এই একাধিক তাত্পর্য আমাদের সমস্যা সমাধান করে। কিন্তু আবার, আমাদের সমাধান আরেকটি চ্যালেঞ্জ উপস্থাপন করেছে। অন্যান্য থ্রেডগুলি "দেখতে" না পারে যে INSTANCE সদস্য আপডেট করা হয়েছে। জাভা কিভাবে মেমরি অপারেশন অপ্টিমাইজ করে তা এই কারণে। থ্রেড মূল মেমরি থেকে CPU এর ক্যাশে ভেরিয়েবলের মূল মানগুলি অনুলিপি করে। মান পরিবর্তনগুলি তারপর লিখিত হয়, এবং যে ক্যাশ থেকে পড়া। এই কর্মক্ষমতা অপ্টিমাইজ করার জন্য ডিজাইন জাভা একটি বৈশিষ্ট্য। কিন্তু এটি আমাদের সিঙ্গলটন বাস্তবায়ন জন্য একটি সমস্যা সৃষ্টি করে। একটি দ্বিতীয় থ্রেড - একটি ভিন্ন CPU বা কোর দ্বারা প্রক্রিয়া করা হচ্ছে, একটি ভিন্ন ক্যাশে ব্যবহার করে - প্রথম দ্বারা করা পরিবর্তনগুলি দেখতে পাবে না। এটি দ্বিতীয় থ্রেডটিকে আমাদের সিঙ্গল্টনের নতুন উদাহরণ তৈরি করার জন্য INSTANCE সদস্যকে নাল হিসাবে দেখাবে।

private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

আমরা ইনস্ট্যান্স সদস্য ঘোষণার উপর উদ্বায়ী শব্দ ব্যবহার করে এই সমাধান। এটি কম্পাইলারকে সর্বদা পড়তে বলবে, এবং লিখতে হবে, প্রধান মেমরি, এবং CPU ক্যাশে নয়।

কিন্তু এই সহজ পরিবর্তন একটি খরচ আসে। কারণ আমরা সিপিইউ ক্যাশে বাইপাস করছি, আমরা প্রতিস্থাপিত ইনস্ট্যান্স সদস্যের পরিচালনা করি প্রতিটি সময় আমরা একটি কর্মক্ষমতা আঘাত করব - যা আমরা 4 বার করি। আমরা অস্তিত্ব দ্বিগুণ (1 এবং 2), মান সেট (3), এবং তারপর মান (4) ফেরত। আমরা এই পদ্ধতির প্রথম কল চলাকালীন শুধুমাত্র উদাহরণ তৈরি হিসাবে এই পথ ফ্রিজ কেস যে যুক্তি দিতে পারে। সম্ভবত সৃষ্টির উপর একটি কর্মক্ষমতা আঘাত সহ্যযোগ্য। কিন্তু এমনকি আমাদের প্রধান ব্যবহার কেস, পড়া, অস্থির সদস্য দুইবার কাজ করবে। একবার অস্তিত্ব পরীক্ষা, এবং আবার তার মান ফিরে।

private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

যেহেতু পারফরম্যান্স হিটটি অবিলম্বে উদ্বায়ী সদস্যের উপর অপারেটিংয়ের কারণে, আসুন স্থানীয় ভেরিয়েবলটিকে অস্থিরতার মানতে সেট করে এবং পরিবর্তে স্থানীয় ভেরিয়েবলে কাজ করি। এই অস্থিরতার উপর আমরা কত বার কাজ করি তা হ্রাস পাবে, ফলে আমাদের কিছু হারিয়ে যাওয়া কর্মক্ষমতা পুনরুদ্ধার করবে। উল্লেখ্য, আমরা যখন আমাদের সিঙ্ক্রোনাইজড ব্লকটি প্রবেশ করব তখন আবার আমাদের স্থানীয় পরিবর্তনশীল সেট করতে হবে। এটি লকটির জন্য অপেক্ষা করার সময় ঘটে যাওয়া কোনও পরিবর্তনগুলির সাথে এটি আপ টু ডেট নিশ্চিত করে।

আমি সম্প্রতি এই সম্পর্কে একটি নিবন্ধ লিখেছেন। সিঙ্গল্টন ডিকনস্ট্রাক্টিং । আপনি এই উদাহরণগুলিতে এবং "ধারক" প্যাটার্নের একটি উদাহরণ সম্পর্কে আরও তথ্য পেতে পারেন। দ্বিগুণ যাচাইযোগ্য অস্থির দৃষ্টিভঙ্গি প্রদর্শনের একটি বাস্তব-বিশ্ব উদাহরণ রয়েছে। আশাকরি এটা সাহায্য করবে.


একটি সহজ বাস্তবায়ন কিভাবে এই হল singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

এইভাবে আপনার অলসভাবে কিভাবে তৈরি করুন singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

সিলেটের বিরুদ্ধে প্রায়ই ব্যবহৃত আরেকটি যুক্তি তাদের পরীক্ষার সমস্যা। Singletons পরীক্ষার উদ্দেশ্যে সহজে mockable হয় না। যদি এটি একটি সমস্যা হতে পারে, তবে আমি নিম্নলিখিত সামান্য পরিবর্তন করতে চাই:

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

যোগ setInstanceপদ্ধতি পরীক্ষার সময় Singleton ক্লাসের একটি mockup বাস্তবায়ন সেটিং করতে পারবেন:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

এটি প্রাথমিক সূচনা পদ্ধতির সাথে কাজ করে:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

এটি স্বাভাবিক অ্যাপ্লিকেশনের ক্ষেত্রেও এই কার্যকারিতাটি প্রকাশ করার অপূর্ণতা রয়েছে। যে কোডটিতে কাজ করছেন অন্যান্য ডেভেলপারগুলি একটি নির্দিষ্ট ফাংশন পরিবর্তন করতে এবং এভাবে পুরো অ্যাপ্লিকেশন আচরণ পরিবর্তন করার জন্য ডেট ইনস্ট্যান্স পদ্ধতি ব্যবহার করতে প্রলুব্ধ হতে পারে, সুতরাং এই পদ্ধতিতে কমপক্ষে এটির জাভাডোকে একটি ভাল সতর্কতা থাকা উচিত।

তবুও, মকআপ-পরীক্ষার সম্ভাবনা (যখন প্রয়োজন), এই কোড এক্সপোজারের অর্থ প্রদানের জন্য গ্রহণযোগ্য মূল্য হতে পারে।


public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

GetInstance এর আগে আমরা সিঙ্ক্রোনাইজড কীওয়ার্ড যোগ করেছি, একই সময়ে GetTystance নামে দুটি থ্রেড কল করার ক্ষেত্রে আমরা রেস অবস্থা এড়িয়ে চলি।







design-patterns