Apa cara yang efisien untuk menerapkan pola tunggal di Jawa? [Tutup]

812

Apa cara yang efisien untuk menerapkan pola tunggal di Jawa?

Riyaz Mohammed Ibrahim
sumber
1
"Apa cara efisien untuk menerapkan pola tunggal di Jawa?" tolong jelaskan efisien.
Marian Paździoch
medium.com/@kevalpatel2106/… . Ini adalah artikel lengkap tentang cara mencapai keselamatan ulir, refleksi, dan serialisasi dalam pola tunggal. Ini adalah sumber yang bagus untuk memahami manfaat dan keterbatasan kelas singleton.
Keval Patel
Seperti yang ditunjukkan Joshua Bloch di Java Efektif, enum singleton adalah cara terbaik untuk melakukannya. Di sini saya telah mengkategorikan berbagai implementasi sebagai malas / bersemangat dll.
isaolmez

Jawaban:

782

Gunakan enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch menjelaskan pendekatan ini dalam pidatonya di Java Reloaded di Google I / O 2008: tautan ke video . Lihat juga slide 30-32 dari presentasinya ( effective_java_reloaded.pdf ):

Cara yang Benar untuk Menerapkan Singleton yang Berhubungan Serial

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

Edit: Sebuah porsi online "Efektif Jawa" kata:

"Pendekatan ini secara fungsional setara dengan pendekatan lapangan publik, kecuali bahwa itu lebih ringkas, menyediakan mesin serialisasi secara gratis, dan memberikan jaminan kuat terhadap berbagai instantiasi, bahkan dalam menghadapi serialisasi canggih atau serangan refleksi. Sementara pendekatan ini memiliki belum diadopsi secara luas, tipe enum elemen tunggal adalah cara terbaik untuk mengimplementasikan singleton . "

Stephen Denne
sumber
203
Saya pikir orang harus mulai melihat enums hanya sebagai kelas dengan fitur. jika Anda bisa membuat daftar instance kelas Anda pada waktu kompilasi, gunakan enum.
Amir Arad
7
Saya pribadi tidak sering menemukan kebutuhan untuk menggunakan pola tunggal secara langsung. Saya kadang-kadang menggunakan injeksi ketergantungan pegas dengan konteks aplikasi yang berisi apa yang disebut sebagai lajang. Kelas utilitas saya cenderung hanya berisi metode statis, dan saya tidak memerlukannya.
Stephen Denne
3
Hai, ada yang bisa memberi tahu saya bagaimana singleton jenis ini dapat diejek dan diuji dalam kasus uji. Saya mencoba menukar instance singleton palsu untuk jenis ini tetapi tidak bisa.
Ashish Sharma
29
Saya kira itu masuk akal, tetapi saya masih tidak menyukainya. Bagaimana Anda membuat singleton yang memperluas kelas lain? Jika Anda menggunakan enum, Anda tidak bisa.
chharvey
11
@ Bvdb: Jika Anda ingin banyak fleksibilitas, Anda sudah mengacaukan dengan menerapkan singleton di tempat pertama. Kemampuan untuk membuat contoh independen ketika Anda membutuhkannya agak tak ternilai harganya.
cHao
233

Bergantung pada penggunaannya, ada beberapa jawaban "benar".

Karena java5 cara terbaik untuk melakukannya adalah dengan menggunakan enum:

public enum Foo {
   INSTANCE;
}

Sebelum java5, kasus paling sederhana adalah:

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");
    }
}

Mari kita bahas kodenya. Pertama, Anda ingin kelas menjadi final. Dalam hal ini, saya telah menggunakan finalkata kunci untuk memberi tahu pengguna bahwa itu final. Maka Anda perlu menjadikan konstruktor sebagai pribadi untuk mencegah pengguna membuat Foo mereka sendiri. Melempar pengecualian dari konstruktor mencegah pengguna untuk menggunakan refleksi untuk membuat Foo kedua. Kemudian Anda membuat private static final Foobidang untuk menampung satu-satunya contoh, dan public static Foo getInstance()metode untuk mengembalikannya. Spesifikasi Java memastikan bahwa konstruktor hanya dipanggil ketika kelas pertama kali digunakan.

Ketika Anda memiliki objek yang sangat besar atau kode konstruksi yang berat DAN juga memiliki metode atau bidang statis yang dapat diakses lainnya yang mungkin digunakan sebelum instance diperlukan, maka dan hanya dengan demikian Anda perlu menggunakan inisialisasi malas.

Anda dapat menggunakan a private static classuntuk memuat instance. Kode tersebut kemudian akan terlihat seperti:

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;
    }
}

Karena baris private static final Foo INSTANCE = new Foo();hanya dieksekusi ketika kelas FooLoader benar-benar digunakan, ini menangani instantiation yang malas, dan apakah dijamin aman untuk thread.

Ketika Anda juga ingin dapat membuat serial objek Anda, Anda perlu memastikan deserialisasi tidak akan membuat salinan.

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;
    }
}

Metode ini readResolve()akan memastikan satu-satunya instance akan dikembalikan, bahkan ketika objek itu serial dalam menjalankan program Anda sebelumnya.

Roel Spilker
sumber
32
Pemeriksaan refleksi tidak berguna. Jika kode lain menggunakan refleksi pada privat, itu Game Over. Tidak ada alasan untuk mencoba berfungsi dengan benar di bawah penyalahgunaan tersebut. Dan jika Anda mencoba, itu akan menjadi "perlindungan" tidak lengkap, hanya banyak kode yang terbuang.
Wouter Coekaerts
5
> "Pertama, Anda ingin kelas menjadi final". Bisakah seseorang menjelaskan hal ini?
PlagueHammer
2
Perlindungan deserialisation benar-benar rusak (saya pikir ini disebutkan dalam Java Efektif 2nd Ed).
Tom Hawtin - tackline
9
-1 ini sama sekali bukan kasus yang paling sederhana, ini dibuat dan tidak perlu rumit. Lihatlah jawaban Jonathan untuk solusi yang sebenarnya paling sederhana yang cukup dalam 99,9% dari semua kasus.
Michael Borgwardt
2
Ini berguna ketika singleton Anda perlu mewarisi dari superclass. Anda tidak dapat menggunakan pola enum singleton dalam kasus ini, karena enum tidak dapat memiliki superclass (mereka dapat mengimplementasikan antarmuka). Misalnya, Google Guava menggunakan bidang final statis saat pola enum singleton bukan opsi: code.google.com/p/guava-libraries/source/browse/trunk/guava/src/…
Etienne Neveu
139

Penafian: Saya baru saja merangkum semua jawaban yang luar biasa dan menuliskannya dalam kata-kata saya.


Saat menerapkan Singleton, kami memiliki 2 opsi
1. Pemuatan malas
2. Pemuatan awal

Pemuatan malas menambah bit overhead (banyak yang harus jujur) jadi gunakanlah hanya ketika Anda memiliki objek yang sangat besar atau kode konstruksi yang berat DAN juga memiliki metode atau bidang statis yang dapat diakses lainnya yang mungkin digunakan sebelum sebuah instance dibutuhkan, kemudian dan hanya kemudian Anda perlu menggunakan inisialisasi malas. Selain itu memilih pemuatan awal adalah pilihan yang baik.

Cara paling sederhana untuk mengimplementasikan Singleton adalah

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

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

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

Semuanya baik kecuali singleton yang dimuat awal. Mari kita coba tunggal dimuat malas

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

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

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Sejauh ini bagus tapi pahlawan kita tidak akan bertahan saat bertarung sendirian dengan banyak utas jahat yang menginginkan banyak contoh pahlawan kita. Jadi mari kita lindungi dari kejahatan multi threading

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

tapi itu tidak cukup untuk melindungi pahlawan, Sungguh !!! Ini adalah yang terbaik yang bisa kita lakukan untuk membantu pahlawan kita

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Ini disebut "idiom Penguncian Ganda". Sangat mudah untuk melupakan pernyataan volatile dan sulit untuk memahami mengapa itu perlu.
Untuk detail: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Sekarang kita yakin tentang utas kejahatan tetapi bagaimana dengan serialisasi yang kejam? Kita harus memastikan walaupun de-serialiaztion tidak ada objek baru yang dibuat

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

Metode ini readResolve()akan memastikan satu-satunya instance akan dikembalikan, bahkan ketika objek itu serial dalam menjalankan program kami sebelumnya.

Akhirnya kami telah menambahkan perlindungan yang cukup terhadap utas dan serialisasi tetapi kode kami terlihat besar dan jelek. Mari kita beri pahlawan kita make over

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

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

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

Ya, ini adalah pahlawan kami yang sama :)
Karena baris private static final Foo INSTANCE = new Foo();hanya dieksekusi ketika kelas FooLoaderbenar-benar digunakan, ini menangani instantiation yang malas,

dan apakah dijamin aman dari utas.

Dan kami telah sampai sejauh ini, di sini adalah cara terbaik untuk mencapai semua yang kami lakukan adalah cara terbaik

 public enum Foo {
       INSTANCE;
   }

Yang secara internal akan diperlakukan seperti

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Itu dia! Tidak ada lagi rasa takut serialisasi, utas dan kode jelek. ENUMS juga singleton diinisialisasi dengan malas .

Pendekatan ini secara fungsional setara dengan pendekatan lapangan publik, kecuali bahwa itu lebih ringkas, menyediakan mesin serialisasi secara gratis, dan memberikan jaminan kuat terhadap berbagai instantiasi, bahkan dalam menghadapi serialisasi canggih atau serangan refleksi. Sementara pendekatan ini belum diadopsi secara luas, tipe enum elemen tunggal adalah cara terbaik untuk menerapkan singleton.

-Joshua Bloch dalam "Java Efektif"

Sekarang Anda mungkin telah menyadari mengapa ENUM dianggap sebagai cara terbaik untuk menerapkan Singleton dan terima kasih atas kesabaran Anda :)
Diperbarui di blog saya .

xyz
sumber
3
Hanya klarifikasi: lajang diimplementasikan menggunakan enum diinisialisasi malas. Detail di sini: stackoverflow.com/questions/16771373/…
2
jawaban yang bagus satu hal lagi, ganti metode klon untuk melempar pengecualian.
riship89
2
@xyz penjelasan yang bagus, saya benar-benar menikmati dan belajar dengan sangat mudah dan saya harap tidak pernah melupakan ini
Premraj
1
Salah satu jawaban terbaik yang pernah saya terima di stackoverflow. Terima kasih!
Shay Tsadok
1
Ada adalah masalah serialisasi dengan menggunakan enum sebagai tunggal: Setiap nilai-nilai field anggota yang tidak serial dan karena itu tidak dikembalikan. Lihat Spesifikasi Serialisasi Objek Java, versi 6.0 . Masalah lain: Tidak ada versioning - semua jenis enum telah tetap serialVersionUIDdari 0L. Masalah ketiga: Tidak ada kustomisasi: Setiap writeObject khusus kelas, readObject, readObjectNoData, writeReplace, dan metode readResolve yang didefinisikan oleh tipe enum diabaikan selama serialisasi dan deserialisasi.
Basil Bourque
124

Solusi yang diposting oleh Stu Thompson berlaku di Java5.0 dan yang lebih baru. Tapi saya lebih suka tidak menggunakannya karena saya pikir itu rawan kesalahan.

Sangat mudah untuk melupakan pernyataan volatile dan sulit untuk memahami mengapa itu perlu. Tanpa volatile kode ini tidak akan aman lagi karena antipattern penguncian diperiksa ulang. Lihat lebih lanjut tentang ini dalam paragraf 16.2.4 Java Concurrency in Practice . Singkatnya: Pola ini (sebelum Java5.0 atau tanpa pernyataan volatile) dapat mengembalikan referensi ke objek Bar yang (masih) dalam keadaan salah.

Pola ini diciptakan untuk optimalisasi kinerja. Tapi ini benar-benar bukan masalah nyata lagi. Kode inisialisasi malas berikut ini cepat dan - yang lebih penting - lebih mudah dibaca.

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

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
Benno Richters
sumber
1
Cukup adil! Saya hanya nyaman dengan volatile dan itu digunakan. Oh, dan tiga sorakan untuk JCiP.
Stu Thompson
1
Oh, ini rupanya pendekatan yang dianjurkan oleh William Pugh, dari ketenaran FindBugz.
Stu Thompson
4
@Stu Edisi pertama Java Efektif (hak cipta 2001) merinci pola ini di bawah item 48.
Pascal Thivent
9
@ Tidak: Bagaimana dengan menjadikan konstruktor sebagai pribadi?
xyz
2
@ AlikElzin-kilaka Tidak cukup. Mesin virtual dibuat dalam fase pemuatan kelas untuk BarHolder , yang ditunda hingga pertama kali dibutuhkan. Konstruktor bar bisa serumit yang Anda inginkan, tetapi tidak akan dipanggil sampai yang pertama getBar(). (Dan jika getBardisebut "terlalu dini" maka Anda akan menghadapi masalah yang sama tidak peduli bagaimana singleon diterapkan.) Anda dapat melihat pemuatan kelas yang malas dari kode di atas di sini: pastebin.com/iq2eayiR
Ti Strga
95

Thread aman di Java 5+:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT : Perhatikan volatilepengubah di sini. :) Ini penting karena tanpa itu, utas lainnya tidak dijamin oleh JMM (Java Memory Model) untuk melihat perubahan nilainya. Sinkronisasi tidak menangani hal itu - itu hanya membuat serialisasi akses ke blok kode itu.

EDIT 2 : Jawaban @Bno merinci pendekatan yang direkomendasikan oleh Bill Pugh (FindBugs) dan dapat diperdebatkan dengan lebih baik. Baca dan ikutilah jawabannya.

Stu Thompson
sumber
1
Di mana saya dapat mempelajari lebih lanjut tentang pengubah volatil?
sebelas81
Lihat komentar stackoverflow.com/questions/70689/…
Pascal Thivent
2
Saya pikir penting untuk menyebutkan tentang serangan refleksi. Benar bahwa sebagian besar pengembang tidak perlu khawatir, tetapi tampaknya contoh-contoh seperti ini (lebih dari lajang berbasis Enum) harus mencakup salah satu kode yang melindungi terhadap serangan multi-Instansiasi atau hanya meletakkan penafian yang menunjukkan kemungkinan seperti itu.
luis.espinal
2
Kata kunci yang mudah menguap tidak diperlukan di sini - karena sinkronisasi memberikan pengecualian bersama sekaligus visibilitas memori.
Hemant
2
Mengapa repot-repot dengan semua ini di Java 5+? Pemahaman saya adalah bahwa pendekatan enum menyediakan keamanan utas dan inisialisasi malas. Ini juga jauh lebih sederhana ... Selain itu, jika Anda ingin menghindari enum, saya masih akan mencegah pendekatan kelas statis bersarang ...
Alexandros
91

Lupakan inisialisasi malas , itu terlalu bermasalah. Ini adalah solusi paling sederhana:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
Jonathan
sumber
21
variabel instan tunggal dapat dibuat final juga. misalnya, private static final A singleton = new A ();
jatanp
19
Itu secara efektif adalah inisialisasi malas, karena singleton statis tidak akan dipakai hingga kelas dimuat dan kelas tidak akan dimuat sampai diperlukan (yang akan benar tentang waktu ketika Anda pertama kali referensi metode getInstance ()).
Dan Dyer
7
Jika kelas A mendapatkan cara dimuat sebelum Anda ingin statis akan dipakai, Anda dapat membungkus statis dalam kelas batin statis untuk memisahkan kelas inisialisasi kelas.
Tom Hawtin - tackline
3
Saya setuju jawaban ini adalah yang paling sederhana, dan Anirudhan, tidak perlu menyatakan instance final. Tidak ada utas lain yang akan mendapatkan akses ke kelas sementara anggota statis diinisialisasi. ini dijamin oleh kompiler, dengan kata lain, semua inisialisasi statis dilakukan secara tersinkronisasi - hanya satu utas.
inor
4
Pendekatan ini memiliki satu batasan: Konstruktor tidak dapat memberikan pengecualian.
Wangf
47

Pastikan Anda benar-benar membutuhkannya. Lakukan google untuk "anti-pola tunggal" untuk melihat beberapa argumen yang menentangnya. Saya kira tidak ada yang salah dengan itu tetapi itu hanya mekanisme untuk mengekspos beberapa sumber daya global / data jadi pastikan bahwa ini adalah cara terbaik. Secara khusus saya telah menemukan injeksi ketergantungan lebih berguna terutama jika Anda juga menggunakan tes unit karena DI memungkinkan Anda untuk menggunakan sumber daya yang diejek untuk tujuan pengujian.

Neil Burroughs
sumber
Anda dapat menyuntikkan nilai-nilai tiruan dengan metode tradisional juga, tetapi saya rasa itu bukan cara standar / srping sehingga kerja ekstra dengan hanya mendapatkan menjadi kode warisan ...
tgkprog
21

Saya bingung dengan beberapa jawaban yang menyarankan DI sebagai alternatif untuk menggunakan lajang; ini adalah konsep yang tidak terkait. Anda dapat menggunakan DI untuk menyuntikkan instance tunggal atau non-tunggal (mis. Per-utas). Setidaknya ini benar jika Anda menggunakan Spring 2.x, saya tidak bisa berbicara untuk kerangka DI lainnya.

Jadi jawaban saya untuk OP adalah (dalam semua kecuali kode sampel yang paling sepele) untuk:

  1. Gunakan kerangka kerja DI seperti Spring
  2. Jadikan itu bagian dari konfigurasi DI Anda apakah dependensi Anda adalah lajang, lingkup permintaan, ruang lingkup sesi, atau apa pun.

Pendekatan ini memberi Anda arsitektur terpisah yang bagus (dan karena itu fleksibel dan dapat diuji) di mana apakah menggunakan singleton adalah detail implementasi yang mudah dibalik (asalkan singlet yang Anda gunakan adalah threadsafe, tentu saja).

Andrew Swan
sumber
4
Mungkin karena orang tidak setuju dengan Anda. Saya belum menurunkan suara Anda, tetapi saya tidak setuju: saya pikir DI dapat digunakan untuk memecahkan masalah yang sama dengan para lajang. Ini didasarkan pada pemahaman "singleton" yang berarti "objek dengan satu instance yang diakses langsung dengan nama global", bukan hanya "objek dengan instance tunggal", yang mungkin sedikit rumit.
Tom Anderson
2
Untuk memperluas sedikit itu, pertimbangkan TicketNumbereryang perlu memiliki contoh global tunggal, dan di mana Anda ingin menulis kelas TicketIssueryang berisi baris kode int ticketNumber = ticketNumberer.nextTicketNumber();. Dalam pemikiran tradisional tunggal, baris kode sebelumnya harus seperti TicketNumberer ticketNumberer = TicketNumberer.INSTANCE;. Dalam berpikir DI, kelas akan memiliki konstruktor seperti public TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }.
Tom Anderson
2
Dan itu menjadi masalah orang lain untuk memanggil konstruktor itu. Kerangka kerja DI akan melakukannya dengan semacam peta global; arsitektur DI buatan tangan akan melakukannya karena metode aplikasi main(atau salah satu anteknya) akan menciptakan ketergantungan dan kemudian memanggil konstruktor. Pada dasarnya, penggunaan variabel global (atau metode global) hanyalah bentuk sederhana dari pola locator layanan yang ditakuti , dan dapat diganti dengan injeksi dependensi, sama seperti penggunaan lain dari pola itu.
Tom Anderson
@ TomAnderson Saya benar-benar bingung mengapa orang-orang 'takut' pola pelacak layanan. Saya pikir dalam kebanyakan kasus itu berlebihan atau tidak diperlukan di terbaik, namun, ada kasus yang tampaknya berguna. Dengan jumlah param yang lebih sedikit, DI pasti lebih disukai, tetapi bayangkan 20+. Mengatakan kode tidak terstruktur bukanlah argumen yang valid, karena kadang-kadang pengelompokan parameter tidak masuk akal. Juga, dari perspektif pengujian unit, saya tidak peduli tentang pengujian layanan, hanya logika bisnisnya, dan jika dikodekan dengan benar, maka ini akan mudah. Saya hanya melihat kebutuhan ini dalam proyek berskala sangat besar.
Brandon Ling
20

Sangat pertimbangkan mengapa Anda membutuhkan singleton sebelum menulisnya. Ada perdebatan semu-religius tentang penggunaannya yang bisa dengan mudah Anda temukan jika Anda menggunakan google singleton di Jawa.

Secara pribadi saya mencoba menghindari lajang sesering mungkin karena berbagai alasan, lagi-lagi sebagian besar dapat ditemukan dengan googling lajang. Saya merasa bahwa sering lajang dilecehkan karena mereka mudah dipahami oleh semua orang, mereka digunakan sebagai mekanisme untuk memasukkan data "global" ke dalam desain OO dan mereka digunakan karena mudah untuk menghindari manajemen siklus hidup objek (atau benar-benar berpikir tentang bagaimana Anda dapat melakukan A dari dalam B). Lihatlah hal-hal seperti Inversion of Control (IoC) atau Dependency Injection (DI) untuk jalan tengah yang menyenangkan.

Jika Anda benar-benar membutuhkannya maka wikipedia memiliki contoh implementasi singleton yang tepat.


sumber
Sepakat. Ini lebih merupakan kelas dasar yang memulai seluruh aplikasi Anda dan jika itu digandakan, Anda akan berakhir dengan kekacauan total (yaitu akses tunggal ke sumber daya atau menegakkan keamanan). Melewati data global di seluruh aplikasi Anda adalah tanda merah yang besar. Gunakan ketika Anda mengakui bahwa Anda benar-benar membutuhkannya.
Salvador Valencia
16

Berikut adalah 3 pendekatan berbeda

1) Enum

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

2) Pengecekan ganda Penguncian / pemuatan malas

/**
* 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) Metode pabrik statis

/**
* 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;
    }
}
Abhijit Gaikwad
sumber
13

Saya menggunakan Spring Framework untuk mengelola lajang saya. Itu tidak menegakkan "keanehan" kelas (yang sebenarnya tidak bisa Anda lakukan jika ada beberapa kelas loader yang terlibat) tetapi memberikan cara yang sangat mudah untuk membangun dan mengkonfigurasi pabrik yang berbeda untuk membuat berbagai jenis objek.

Mat
sumber
11

Wikipedia memiliki beberapa contoh lajang, juga di Jawa. Implementasi Java 5 terlihat cukup lengkap, dan aman untuk digunakan (diterapkan penguncian ganda).

macbirdie
sumber
11

Versi 1:

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

Pemuatan malas, aman dengan pemblokiran, kinerja rendah karena synchronized .

Versi 2:

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

Pemuatan malas, aman dengan non-blocking, kinerja tinggi.

coderz
sumber
10

Jika Anda tidak perlu memuat malas maka cukup coba

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();
    }
}

Jika Anda ingin pemuatan yang malas dan Anda ingin Singleton Anda aman dari benang, coba pola periksa dua kali

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();
        }
}

Karena pola pemeriksaan ganda tidak dijamin berfungsi (karena beberapa masalah dengan kompiler, saya tidak tahu apa-apa lagi tentang itu.), Anda juga dapat mencoba menyinkronkan seluruh metode getInstance atau membuat registri untuk semua Singletons Anda.

Aleksi Yrttiaho
sumber
2
Versi pertama adalah yang terbaik. Dengan asumsi kelas tidak melakukan apa-apa selain memberikan singleton, maka biasanya akan dipakai pada titik yang sama dengan yang ada di versi kedua karena pemuatan kelas yang malas.
Dan Dyer
1
Pengecekan tidak ada gunanya untuk statis. Dan mengapa Anda membuat metode klon yang dilindungi terbuka untuk umum?
Tom Hawtin - tackline
1
-1 versi penguncian ganda diperiksa rusak.
assylias
5
Anda juga perlu membuat variabel singleton Andavolatile
MyTitle
Versi pertama adalah malas dan aman.
Miha_x64
9

Saya akan mengatakan Enum singleton

Singleton menggunakan enum di Jawa umumnya cara untuk mendeklarasikan enum singleton. Enum singleton dapat berisi variabel instan dan metode instance. Demi kesederhanaan, juga perhatikan bahwa jika Anda menggunakan metode instance apa pun dari yang Anda butuhkan untuk memastikan keamanan metode tersebut jika hal itu memengaruhi keadaan objek.

Penggunaan enum sangat mudah diimplementasikan dan tidak memiliki kelemahan tentang objek serial, yang harus dielakkan dengan cara lain.

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

Anda dapat mengaksesnya dengan Singleton.INSTANCE, lebih mudah daripada memanggil getInstance()metode di Singleton.

1.12 Serialisasi dari Konstanta Enum

Konstanta Enum diserialisasi secara berbeda dari objek serialable atau externalizable biasa. Bentuk serial konstanta enum hanya terdiri atas namanya; nilai bidang konstanta tidak ada dalam formulir. Untuk membuat serial suatu konstanta enum, ObjectOutputStreamtulis nilai yang dikembalikan oleh metode nama konstanta enum. Untuk menghilangkan desalisasi konstanta enum, ObjectInputStreambaca nama konstanta dari stream; konstanta deserialized kemudian diperoleh dengan memanggil java.lang.Enum.valueOfmetode, melewati tipe enum konstanta bersama dengan nama konstanta yang diterima sebagai argumen. Seperti objek serializable atau eksternal lainnya, konstanta enum dapat berfungsi sebagai target referensi belakang muncul kemudian dalam aliran serialisasi.

Proses dimana konstanta enum yang serial tidak dapat disesuaikan: setiap kelas khusus writeObject, readObject, readObjectNoData, writeReplace, dan readResolvemetode yang didefinisikan oleh jenis enum diabaikan selama serialisasi dan deserialization. Demikian pula, setiap serialPersistentFieldsatau serialVersionUIDbidang deklarasi juga diabaikan - semua jenis enum telah tetap serialVersionUIDdari 0L. Mendokumentasikan bidang dan data berseri serial untuk jenis enum tidak perlu, karena tidak ada variasi dalam jenis data yang dikirim.

Dikutip dari Oracle docs

Masalah lain dengan Singletons konvensional adalah bahwa begitu Anda mengimplementasikan Serializableantarmuka, Singleton tidak lagi tetap Singleton karena readObject()metode selalu mengembalikan contoh baru seperti konstruktor di Jawa. Ini dapat dihindari dengan menggunakan readResolve()dan membuang instance yang baru dibuat dengan mengganti dengan singleton seperti di bawah ini

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

Ini dapat menjadi lebih kompleks jika Kelas Singleton Anda mempertahankan keadaan, karena Anda perlu membuatnya sementara, tetapi dengan di Enum Singleton, Serialisasi dijamin oleh JVM.


Baca Bagus

  1. Pola Singleton
  2. Enum, Lajang dan Deserialisasi
  3. Penguncian ganda dan pola Singleton
NullPoiиteя
sumber
8
There are 4 ways to create a singleton in java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
Dheeraj Sachan
sumber
(1) tidak bersemangat, itu malas karena mekanisme pemuatan kelas JVM.
Miha_x64
@ Miha_x64 kapan saya mengatakan eager loading, saya katakan bersemangat inisialisasi. Jika Anda berpikir keduanya sama maka apa yang ingin memuat. Mungkin Anda harus menulis buku dan memperbaiki kesalahan yang dilakukan oleh penulis sebelumnya seperti Joshua Bloch.
Dheeraj Sachan
Java yang efektif adalah buku yang bagus, tetapi pasti membutuhkan pengeditan.
Miha_x64
@ Miha_x64 apa yang ingin dimuat, dapatkah Anda jelaskan dengan contoh
Dheeraj Sachan
Melakukan sesuatu 'dengan penuh semangat' berarti 'sesegera mungkin'. Misalnya, Hibernate mendukung relasi pemuatan dengan penuh semangat, jika diperlukan secara eksplisit.
Miha_x64
8

Mungkin agak terlambat untuk permainan ini, tetapi ada banyak nuansa di sekitar menerapkan singleton. Pola dudukan tidak dapat digunakan dalam banyak situasi. Dan IMO saat menggunakan volatile - Anda juga harus menggunakan variabel lokal. Mari kita mulai dari awal dan beralih ke masalah. Anda akan melihat apa yang saya maksud.


Upaya pertama mungkin terlihat seperti ini:

public class MySingleton {

     private static MySingleton INSTANCE;

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

        return INSTANCE;
    }
    ...
}

Di sini kita memiliki kelas MySingleton yang memiliki anggota statis pribadi bernama INSTANCE, dan metode statis publik yang disebut getInstance (). Pertama kali getInstance () dipanggil, anggota INSTANCE adalah nol. Aliran kemudian akan jatuh ke dalam kondisi penciptaan dan membuat instance baru dari kelas MySingleton. Panggilan selanjutnya ke getInstance () akan menemukan bahwa variabel INSTANCE telah ditetapkan, dan karenanya tidak membuat instance MySingleton lain. Ini memastikan hanya ada satu instance MySingleton yang dibagikan di antara semua penelepon getInstance ().

Tetapi implementasi ini memiliki masalah. Aplikasi multi-utas akan memiliki kondisi balapan pada pembuatan instance tunggal. Jika beberapa utas eksekusi mengenai metode getInstance () pada (atau sekitar) pada waktu yang sama, mereka masing-masing akan melihat anggota INSTANCE sebagai nol. Ini akan menghasilkan setiap utas membuat instance MySingleton baru dan selanjutnya menetapkan anggota INSTANCE.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Di sini kami telah menggunakan kata kunci yang disinkronkan dalam tanda tangan metode untuk menyinkronkan metode getInstance (). Ini tentu akan memperbaiki kondisi balapan kami. Utas sekarang akan memblokir dan memasukkan metode satu per satu. Tetapi itu juga menciptakan masalah kinerja. Implementasi ini tidak hanya menyinkronkan pembuatan instance tunggal, tetapi juga menyinkronkan semua panggilan ke getInstance (), termasuk membaca. Membaca tidak perlu disinkronkan karena hanya mengembalikan nilai INSTANCE. Karena membaca akan menjadi bagian terbesar dari panggilan kami (ingat, instantiasi hanya terjadi pada panggilan pertama), kami akan mengalami performa yang tidak perlu dengan menyinkronkan seluruh metode.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Di sini kami telah memindahkan sinkronisasi dari tanda tangan metode, ke blok yang disinkronkan yang membungkus pembuatan instance MySingleton. Tetapi apakah ini menyelesaikan masalah kita? Ya, kami tidak lagi memblokir bacaan, tetapi kami juga telah mengambil langkah mundur. Beberapa utas akan menekan metode getInstance () pada atau sekitar waktu yang sama dan mereka semua akan melihat anggota INSTANCE sebagai nol. Mereka kemudian akan menekan blok yang disinkronkan di mana seseorang akan mendapatkan kunci dan membuat instance. Ketika utas itu keluar dari blok, utas lainnya akan bersaing untuk kunci, dan satu per satu setiap utas akan jatuh melalui blok dan membuat instance baru dari kelas kita. Jadi kami segera kembali ke tempat kami mulai.


private static MySingleton INSTANCE;

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

    return INSTANCE;
}

Di sini kami mengeluarkan cek lain dari DALAM blok. Jika anggota INSTANCE telah ditetapkan, kami akan melewatkan inisialisasi. Ini disebut penguncian ganda.

Ini memecahkan masalah kami tentang banyak instantiasi. Tetapi sekali lagi, solusi kami telah menghadirkan tantangan lain. Utas lain mungkin tidak “melihat” bahwa anggota INSTANCE telah diperbarui. Ini karena bagaimana Java mengoptimalkan operasi memori. Utas menyalin nilai asli variabel dari memori utama ke dalam cache CPU. Perubahan nilai kemudian ditulis ke, dan dibaca dari, cache itu. Ini adalah fitur Java yang dirancang untuk mengoptimalkan kinerja. Tetapi ini menciptakan masalah bagi implementasi tunggal kami. Utas kedua - sedang diproses oleh CPU atau inti yang berbeda, menggunakan cache yang berbeda - tidak akan melihat perubahan yang dilakukan oleh yang pertama. Ini akan menyebabkan utas kedua melihat anggota INSTANCE sebagai nol yang memaksa instance baru singleton kami dibuat.


private static volatile MySingleton INSTANCE;

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

    return INSTANCE;
}

Kami menyelesaikan ini dengan menggunakan kata kunci yang tidak stabil pada pernyataan anggota INSTANCE. Ini akan memberi tahu kompiler untuk selalu membaca dari, dan menulis ke, memori utama, dan bukan cache CPU.

Tetapi perubahan sederhana ini harus dibayar. Karena kita mem-bypass cache CPU, kita akan mendapatkan hit kinerja setiap kali kita beroperasi pada anggota INSTANCE yang mudah menguap - yang kita lakukan 4 kali. Kami memeriksa ulang keberadaan (1 dan 2), mengatur nilai (3), dan kemudian mengembalikan nilai (4). Orang bisa berpendapat bahwa jalan ini adalah kasus pinggiran karena kami hanya membuat contoh selama panggilan pertama dari metode ini. Mungkin kinerja yang berhasil pada penciptaan bisa ditoleransi. Tetapi bahkan use case utama kita, berbunyi, akan beroperasi pada anggota volatile dua kali. Sekali untuk memeriksa keberadaan, dan lagi untuk mengembalikan nilainya.


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;
}

Karena hit kinerja adalah karena beroperasi secara langsung pada anggota yang mudah menguap, mari kita tetapkan variabel lokal ke nilai yang tidak stabil dan beroperasi pada variabel lokal sebagai gantinya. Ini akan mengurangi berapa kali kami beroperasi pada volatile, sehingga mengambil kembali sebagian dari kinerja kami yang hilang. Perhatikan bahwa kita harus mengatur variabel lokal kita lagi ketika kita memasuki blok yang disinkronkan. Ini memastikan sudah mutakhir dengan perubahan apa pun yang terjadi saat kami sedang menunggu kunci.

Saya menulis artikel tentang ini baru-baru ini. Mendekonstruksi Singleton . Anda dapat menemukan lebih banyak info tentang contoh-contoh ini dan contoh pola "pemegang" di sana. Ada juga contoh dunia nyata yang menampilkan pendekatan volatil yang dicek ganda. Semoga ini membantu.

Michael Andrews
sumber
Bisakah Anda jelaskan mengapa BearerToken instancedalam artikel Anda tidak static? Dan apa itu result.hasExpired()?
Woland
Dan bagaimana class MySingleton- mungkin seharusnya final?
Woland
1
@Woland Turunannya BearerTokentidak statis karena merupakan bagian dari BearerTokenFactory - yang dikonfigurasi dengan server otorisasi tertentu. Mungkin ada banyak BearerTokenFactoryobjek - masing-masing memiliki "cache" sendiri yang dibagikan BearerTokensampai habis. The hasExpired()metode pada BeraerTokendisebut dalam pabrik get()metode untuk memastikan tidak membagikan sebuah kadaluarsa tanda. Jika kedaluwarsa, token baru akan diminta dari server otorisasi. Paragraf yang mengikuti blok kode menjelaskan ini secara lebih rinci.
Michael Andrews
4

Ini adalah cara mengimplementasikan yang sederhana 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;
    }
}

Ini adalah cara membuat malas dengan benar 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();
    }
}
Nicolas Filotto
sumber
Keduanya malas, dengan asumsi satu-satunya yang Anda butuhkan dari singleton adalah contohnya.
Miha_x64
@ Miha_x64 case pertama akan instantiate singleton ketika JVM menginisialisasi kelas, case kedua hanya akan instantiate singleton saat memanggil getInstance(). Tetapi memang jika Anda tidak memiliki metode statis lain di kelas SingletonAnda dan Anda hanya menelepon getInstance()tidak ada perbedaan nyata.
Nicolas Filotto
3

Anda perlu memeriksa ulang idiom jika Anda perlu memuat variabel instan dari sebuah kelas. Jika Anda perlu memuat variabel statis atau tunggal dengan malas, Anda perlu inisilisasi pada idiom pemegang permintaan .

Selain itu, jika singleton perlu seriliazble, semua bidang lainnya harus bersifat sementara dan metode readResolve () perlu diimplementasikan untuk mempertahankan invarian objek singleton. Jika tidak, setiap kali objek di-deserialisasi, instance baru dari objek akan dibuat. Apa yang readResolve () lakukan adalah mengganti objek baru yang dibaca oleh readObject (), yang memaksa objek baru itu menjadi sampah yang dikumpulkan karena tidak ada variabel yang merujuk padanya.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
Onur
sumber
3

Berbagai cara untuk membuat objek tunggal:

  1. Sesuai Joshua Bloch - Enum akan menjadi yang terbaik.

  2. Anda dapat menggunakan penguncian cek ganda juga.

  3. Bahkan kelas statis dalam dapat digunakan.

Shailendra Singh
sumber
3

Enum singleton

Cara paling sederhana untuk mengimplementasikan Singleton yang aman-threaded adalah menggunakan Enum

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

Kode ini berfungsi sejak diperkenalkannya Enum di Java 1.5

Penguncian diperiksa ganda

Jika Anda ingin kode singleton "klasik" yang berfungsi di lingkungan multithreaded (mulai dari Java 1.5) Anda harus menggunakan yang ini.

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 ;
  }
}

Ini tidak aman sebelum 1,5 karena penerapan kata kunci yang mudah menguap berbeda.

Pemuatan awal Singleton (bekerja bahkan sebelum Java 1.5)

Implementasi ini instantiates singleton ketika kelas dimuat dan memberikan keamanan thread.

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");
  }

}
Dan Moldovan
sumber
2

Untuk JSE 5.0 dan di atasnya, gunakan pendekatan Enum, jika tidak gunakan pendekatan static singleton holder ((pendekatan lazy loading yang dijelaskan oleh Bill Pugh). Solusi terakhir juga aman untuk benang tanpa memerlukan konstruksi bahasa khusus (yaitu volatile atau disinkronkan).

raoadnan
sumber
2

Argumen lain yang sering digunakan terhadap Lajang adalah masalah testabilitas mereka. Lajang tidak mudah dipermainkan untuk tujuan pengujian. Jika ini ternyata menjadi masalah, saya ingin melakukan sedikit modifikasi berikut:

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");
    }
}

setInstanceMetode yang ditambahkan memungkinkan pengaturan implementasi mockup dari kelas singleton selama pengujian:

public class SingletonMock extends SingletonImpl {

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

}

Ini juga bekerja dengan pendekatan inisialisasi awal:

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");
    }

}

Ini memiliki kelemahan dalam mengekspos fungsi ini ke aplikasi normal juga. Pengembang lain yang mengerjakan kode itu bisa tergoda untuk menggunakan metode 'setInstance' untuk mengubah fungsi tertentu dan dengan demikian mengubah seluruh perilaku aplikasi, oleh karena itu metode ini harus mengandung setidaknya peringatan yang baik di javadoc-nya.

Namun, untuk kemungkinan pengujian-mockup (bila perlu), paparan kode ini mungkin merupakan harga yang dapat diterima untuk membayar.

pengguna3792852
sumber
1

kelas singleton paling sederhana

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
rohan kamat
sumber
1
ini sama dengan jawaban Jonathan di bawah ini
inor
1
Duplikat jawaban saudara ini oleh Jonathan yang diposting lima tahun sebelumnya. Lihat jawaban itu untuk komentar yang menarik.
Basil Bourque
0

Saya masih berpikir setelah java 1.5, enum adalah implementasi singleton terbaik yang tersedia karena juga memastikan bahwa bahkan di lingkungan multi-threaded - hanya satu instance yang dibuat.

public enum Singleton{ INSTANCE; }

dan kamu selesai !!!

shikjohari
sumber
1
Ini sudah disebutkan dalam jawaban lain tahun lalu.
Pang
0

Lihat posting ini.

Contoh Pola Desain GoF di perpustakaan inti Jawa

Dari bagian "Singleton" jawaban terbaik,

Singleton (dapat dikenali dengan metode kreatif yang mengembalikan contoh yang sama (biasanya dengan sendirinya) setiap kali)

  • java.lang.Runtime # getRuntime ()
  • java.awt.Desktop # getDesktop ()
  • java.lang.System # getSecurityManager ()

Anda juga dapat mempelajari contoh Singleton dari kelas asli Jawa sendiri.

kenju
sumber
0

Pola singleton terbaik yang pernah saya lihat menggunakan antarmuka Pemasok.

  • Ini generik dan dapat digunakan kembali
  • Ini mendukung inisialisasi malas
  • Ini hanya disinkronkan sampai diinisialisasi, maka pemasok pemblokiran diganti dengan pemasok yang tidak memblokir.

Lihat di bawah:

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();
    }
}

sumber
-3

Terkadang " static Foo foo = new Foo();" sederhana tidak cukup. Pikirkan beberapa penyisipan data dasar yang ingin Anda lakukan.

Di sisi lain Anda harus menyinkronkan metode apa pun yang instantiates variabel singleton seperti itu. Sinkronisasi tidak seburuk itu, tetapi dapat menyebabkan masalah kinerja atau penguncian (dalam situasi yang sangat jarang menggunakan contoh ini. Solusinya adalah

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

Sekarang apa yang terjadi? Kelas dimuat melalui pemuat kelas. Langsung setelah kelas ditafsirkan dari byte Array, VM mengeksekusi blok {} - statis . itulah rahasianya: Blok-statis hanya dipanggil sekali, waktu kelas yang diberikan (nama) dari paket yang diberikan dimuat oleh loader satu kelas ini.

Georgi
sumber
3
Tidak benar. variabel statis diinisialisasi bersama dengan blok statis ketika kelas dimuat. Tidak perlu membagi deklarasi.
Craig P. Motlin
-5
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;

    }

}

Karena kami telah menambahkan kata kunci yang Disinkronkan sebelum getInstance, kami telah menghindari kondisi balapan dalam kasus ketika dua utas memanggil getInstance secara bersamaan.

somenath mukhopadhyay
sumber