berbagi satu contoh peta hash antara semua contoh kelas layanan pegas

Saya bermaksud membuat penghitung waktu nyata. Jadi satu pengguna dapat menambah nilai penghitung untuk kunci tertentu. Sementara yang lain mendapatkan nilai hitungan yang diperbarui melalui permintaan ajax (baik dalam satu lingkaran, atau menggunakan metode polling yang panjang). Saya akan menggunakan pengontrol pegas, yang akan menyuntikkan kelas layanan, bisakah saya melakukan sesuatu seperti di bawah ini, atau adakah cara yang lebih baik:

@Service
public MyService{

//instance variable in spring injected service class, not sure if this correct
static final Map<String, Integer> myMap;


public void add(String key){
  Integer count = myMap.get(key);
  count++;
  myMap.put(key, count);
}

//accessed via ajax loop (and controller), if value changes update display
public Integer getCount(String key){
  return myMap.get(key)
}

@PostConstruct
public load(){
  myMap = new HashMap<String, Integer>(10){{//initialize}};
}

Edit ada beberapa jawaban tetapi tidak jelas mana yang terbaik : Sinkronkan metode penambahan? Buat peta di kelas lain (repositori beranotasi) dan masukkan itu? Sesuatu yang lain?


person NimChimpsky    schedule 20.06.2012    source sumber


Jawaban (3)


Anda bisa, tetapi perlu menyadari masalah-masalah tersebut:

  • peta awalnya kosong, tetapi Anda tidak pernah memeriksa penghitung nol;
  • metode add() tidak mengubah penghitung di peta. Anda harus mengembalikan penghitung ke dalam peta setelah menambahnya, karena Integer tidak dapat diubah. Atau Anda perlu menyimpan penghitung yang bisa berubah di dalam peta
  • beberapa thread mengakses peta tanpa sinkronisasi apa pun, yang akan menyebabkan bug, perilaku tidak menentu, atau pengecualian
  • strategi ini jelas akan gagal jika aplikasi Anda dikelompokkan di antara beberapa server
person JB Nizet    schedule 20.06.2012
comment
Saya telah mengedit masalah kecil, tetapi cara terbaik untuk menyelesaikan beberapa thread adalah mengakses peta tanpa sinkronisasi apa pun, yang akan menyebabkan bug, perilaku tidak menentu, atau pengecualian - person NimChimpsky; 20.06.2012
comment
Dalam aplikasi nyata, ada lebih dari sekadar thread utama yang dapat mengakses sumber daya bersama Anda (khususnya di Aplikasi Web), sehingga satu orang dapat meletakkan objek A dengan kunci utama, sementara thread lainnya akan memasukkan objek B dengan kunci utama di waktu yang sama. waktu. Dalam hal peta, Anda dapat menggunakan java. util.concurrent.ConcurrentHashMap untuk mengelola masalah ini. - person Luiggi Mendoza; 20.06.2012
comment
Penggunaan ConcurrentMap yang menyimpan instance AtomicInteger dengan benar adalah sebuah solusi. Namun Anda benar-benar perlu memahami semua potensi masalah threading sebelum menerapkan apa pun. Baca Konkurensi Java dalam Praktek. - person JB Nizet; 20.06.2012
comment
@JBNizet Saya tidak bisa begitu saja membuat kelas untuk membungkus peta, membubuhi keterangan sebagai repositori dan menyuntikkannya? - person NimChimpsky; 20.06.2012
comment
Ya, Anda bisa, seperti yang saya katakan di jawaban saya. Namun karena kacang akan diakses oleh banyak thread, Anda harus membuatnya aman dan benar. - person JB Nizet; 20.06.2012
comment
@JBNizet Oke, bagaimana dengan ini :stackoverflow.com/q/11126684/106261 terima kasih atas bantuannya btw - person NimChimpsky; 20.06.2012

Gunakan ConcurrentHashMap

public void add(String key){
    Integer count = myMap.get(key);
    count= count++;
    myMap.put(key, count);
}
person Subin Sebastian    schedule 20.06.2012
comment
Ini masih kode yang salah. Tidak ada masalah sinkronisasi apa pun, tetapi Anda mungkin memiliki tiga utas yang memanggil metode penambahan secara bersamaan, dan nilainya bertambah 1 saja. - person JB Nizet; 20.06.2012
comment
ya kamu benar. harus membuat metode ini disinkronkan menurut saya? - person Subin Sebastian; 20.06.2012
comment
Itulah cara untuk membuat kodenya benar, asalkan metode lain yang mengakses peta juga disinkronkan. Namun jika ini dilakukan, penggunaan ConcurrentMap tidak memiliki keuntungan apa pun dibandingkan menggunakan HashMap sederhana. - person JB Nizet; 20.06.2012
comment
sinkronisasi metode layanan adalah sesuatu yang harus kita hindari - person Subin Sebastian; 20.06.2012

Kelas Integer tidak dapat diubah. Artinya, Anda tidak dapat melakukan modifikasi terhadapnya. Jadi, untuk menambah hitungannya, Anda harus memasukkannya kembali ke dalam peta setelah Anda menambahnya:

public void add(String key){
  Integer count = myMap.get(key);
  count++;
  myMap.put(key, count);
}

Masalah yang timbul dari hal ini adalah keamanan thread. Jika kelas layanan ini akan diakses oleh beberapa thread secara bersamaan, maka Anda harus memastikan datanya diakses dengan cara yang aman. Karena myMap sedang dimodifikasi, dan karena kelas HashMap tidak aman untuk thread, Anda harus membuatnya aman untuk thread. Salah satu cara Anda dapat melakukannya adalah dengan menggunakan metode Collections.synchronizedMap(). Ini secara otomatis akan membuat instance Map aman untuk thread.

@PostConstruct
public load(){
  myMap = new HashMap<String, Integer>(10){{//initialize}};
  myMap = Collections.synchronizedMap(myMap);
}
person Michael    schedule 20.06.2012
comment
Ini akan membuat thread Map aman, tetapi metode add() non-atom masih belum aman untuk thread. - person nicholas.hauschild; 20.06.2012
comment
@ nicholas.hauschild Oh ya, benar juga. Anda harus membuat metode add() disinkronkan atau membungkus tubuhnya dalam blok synchronized(myMap). - person Michael; 20.06.2012