Data bersama adalah salah satu fitur paling penting dari program bersamaan. Ini adalah aspek yang sangat penting apakah itu objek yang mewarisi kelas Thread atau objek yang mengimplementasikan antarmuka Runnable.
Jika Anda membuat objek kelas yang mengimplementasikan antarmuka Runnable dan menggunakan objek tersebut untuk memulai serangkaian thread, semua thread tersebut memiliki properti yang sama. Dengan kata lain, jika satu thread mengubah properti, semua thread yang tersisa akan terpengaruh oleh perubahan tersebut.
Terkadang, kami lebih suka menggunakannya sendiri dalam sebuah thread daripada membaginya dengan thread lain yang dimulai dengan objek yang sama. Antarmuka konkurensi Java menyediakan mekanisme yang sangat jelas untuk memenuhi persyaratan ini, yang disebut variabel thread-lokal. Performa mekanisme ini juga sangat mengesankan.
mengetahuinya
Ikuti langkah-langkah yang ditunjukkan di bawah ini untuk menyelesaikan contoh program.
1. Pertama, implementasikan program dengan permasalahan di atas. Buat kelas bernama UnsafeTask dan implementasikan antarmuka Runnable. Deklarasikan properti pribadi bertipe java.util.Date di kelas. Kodenya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
kelas publik UnsafeTask mengimplementasikan Runnable {
Tanggal mulai Tanggal pribadi;
2. Menerapkan metode run() pada UnsafeTask, yang membuat instance atribut startDate dan mengeluarkan nilainya ke konsol. Tidur selama jangka waktu acak lalu cetak kembali nilai atribut startDate ke konsol. Kodenya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
@Mengesampingkan
menjalankan kekosongan publik() {
tanggal mulai = Tanggal baru();
System.out.printf("Utas Awal: %s : %s/n",
Thread.currentThread().getId(), startDate);
mencoba {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Utas Selesai: %s : %s/n",
Thread.currentThread().getId(), startDate);
}
3. Mengimplementasikan kelas utama dari program yang bermasalah. Buat kelas dengan metode main(), UnsafeMain. Dalam metode main(), buat objek UnsafeTask dan gunakan objek ini untuk membuat 10 objek Thread untuk memulai 10 thread. Di tengah setiap utas, tidurlah selama 2 detik. Kodenya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
kelas publik UnsafeMain {
public static void main(String[] args) {
Tugas UnsafeTask = new UnsafeTask();
untuk (int saya = 0; saya < 10; saya++) {
Utas utas = Utas baru (tugas);
thread.mulai();
mencoba {
TimeUnit.SECONDS.sleep(2);
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4. Dari logika di atas, setiap thread memiliki waktu startup yang berbeda-beda. Namun, menurut log keluaran di bawah, ada banyak nilai waktu yang identik. sebagai berikut:
Copy kode kodenya sebagai berikut:
Thread Awal: 9 : Minggu 29 Sep 23:31:08 CST 2013
Thread Awal: 10: Minggu 29 Sep 23:31:10 CST 2013
Thread Awal: 11 : Minggu 29 Sep 23:31:12 CST 2013
Thread Awal: 12: Minggu 29 Sep 23:31:14 CST 2013
Thread Selesai: 9 : Minggu 29 Sep 23:31:14 CST 2013
Thread Awal: 13: Minggu 29 Sep 23:31:16 CST 2013
Thread Selesai: 10: Minggu 29 Sep 23:31:16 CST 2013
Thread Awal: 14: Minggu 29 Sep 23:31:18 CST 2013
Thread Selesai: 11: Minggu 29 Sep 23:31:18 CST 2013
Thread Awal: 15 : Minggu 29 Sep 23:31:20 CST 2013
Thread Selesai: 12: Minggu 29 Sep 23:31:20 CST 2013
Thread Awal: 16: Minggu 29 Sep 23:31:22 CST 2013
Thread Awal: 17: Minggu 29 Sep 23:31:24 CST 2013
Thread Selesai: 17 : Minggu 29 Sep 23:31:24 CST 2013
Thread Selesai: 15 : Minggu 29 Sep 23:31:24 CST 2013
Thread Selesai: 13 : Minggu 29 Sep 23:31:24 CST 2013
Thread Awal: 18: Minggu 29 Sep 23:31:26 CST 2013
Thread Selesai: 14 : Minggu 29 Sep 23:31:26 CST 2013
Thread Selesai: 18: Minggu 29 Sep 23:31:26 CST 2013
Thread Selesai: 16 : Minggu 29 Sep 23:31:26 CST 2013
5. Seperti yang ditunjukkan di atas, kita akan menggunakan mekanisme variabel thread-lokal untuk menyelesaikan masalah ini.
6. Buat kelas bernama SafeTask dan implementasikan antarmuka Runnable. Kodenya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
SafeTask kelas publik mengimplementasikan Runnable {
7. Deklarasikan objek bertipe ThreadLocal<Date>. Saat objek dibuat, metode InitialValue() akan diganti dan nilai tanggal sebenarnya dikembalikan dalam metode ini. Kodenya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
pribadi statis ThreadLocal<Tanggal> startDate = baru
ThreadLokal<Tanggal>() {
@Mengesampingkan
Tanggal dilindungi initialValue() {
kembalikan Tanggal baru();
}
};
8. Implementasikan metode run() pada kelas SafeTask. Metode ini sama dengan metode run() pada UnsafeTask, hanya saja metode atribut startDate sedikit disesuaikan. Kodenya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
@Mengesampingkan
menjalankan kekosongan publik() {
System.out.printf("Utas Awal: %s : %s/n",
Thread.currentThread().getId(), startDate.get());
mencoba {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Utas Selesai: %s : %s/n",
Thread.currentThread().getId(), startDate.get());
}
9. Kelas utama dari contoh aman ini pada dasarnya sama dengan kelas utama dari program non-aman, hanya saja UnsafeTask perlu dimodifikasi menjadi SafeTask. Kode spesifiknya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
SafeMain kelas publik {
public static void main(String[] args) {
Tugas SafeTask = SafeTask baru();
untuk (int saya = 0; saya < 10; saya++) {
Utas utas = Utas baru (tugas);
thread.mulai();
mencoba {
TimeUnit.SECONDS.sleep(2);
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
}
}
}
10. Jalankan program dan analisa perbedaan kedua input tersebut.
Untuk membakukan penamaan kelas, maka penamaan kelas utama pada artikel ini sedikit berbeda dengan teks aslinya. Selain itu, program asli dan deskripsi teks tidak konsisten. Itu pasti kesalahan klerikal.
tahu kenapa
Di bawah ini adalah hasil eksekusi contoh keamanan. Dari hasilnya terlihat dengan mudah bahwa setiap thread memiliki nilai atribut startDate milik thread masing-masing. Masukan programnya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
Thread Awal: 9 : Minggu 29 Sep 23:52:17 CST 2013
Thread Awal: 10: Minggu 29 Sep 23:52:19 CST 2013
Thread Awal: 11: Minggu 29 Sep 23:52:21 CST 2013
Thread Selesai: 10: Minggu 29 Sep 23:52:19 CST 2013
Thread Awal: 12: Minggu 29 Sep 23:52:23 CST 2013
Thread Selesai: 11: Minggu 29 Sep 23:52:21 CST 2013
Thread Awal: 13: Minggu 29 Sep 23:52:25 CST 2013
Thread Selesai: 9 : Minggu 29 Sep 23:52:17 CST 2013
Thread Awal: 14: Minggu 29 Sep 23:52:27 CST 2013
Thread Awal: 15: Minggu 29 Sep 23:52:29 CST 2013
Thread Selesai: 13 : Minggu 29 Sep 23:52:25 CST 2013
Thread Awal: 16: Minggu 29 Sep 23:52:31 CST 2013
Thread Selesai: 14 : Minggu 29 Sep 23:52:27 CST 2013
Thread Awal: 17: Minggu 29 Sep 23:52:33 CST 2013
Thread Selesai: 12: Minggu 29 Sep 23:52:23 CST 2013
Thread Selesai: 16 : Minggu 29 Sep 23:52:31 CST 2013
Thread Selesai: 15 : Minggu 29 Sep 23:52:29 CST 2013
Thread Awal: 18: Minggu 29 Sep 23:52:35 CST 2013
Thread Selesai: 17 : Minggu 29 Sep 23:52:33 CST 2013
Thread Selesai: 18: Minggu 29 Sep 23:52:35 CST 2013
Variabel lokal thread menyimpan salinan properti untuk setiap thread. Anda dapat menggunakan metode get() ThreadLocal untuk mendapatkan nilai variabel, dan menggunakan metode set() untuk menetapkan nilai variabel. Jika variabel lokal thread diakses untuk pertama kalinya dan variabel tersebut belum diberi nilai, metode InitialValue() dipanggil untuk menginisialisasi nilai untuk setiap thread.
tidak pernah berakhir
Kelas ThreadLocal juga menyediakan metode hapus() untuk menghapus nilai variabel lokal yang disimpan dalam thread yang memanggil metode ini.
Selain itu, API konkurensi Java juga menyediakan kelas InheritableThreadLocal, yang memungkinkan thread anak menerima nilai awal dari semua variabel lokal thread yang dapat diwariskan untuk mendapatkan nilai yang dimiliki oleh thread induk. Jika thread A memiliki variabel lokal thread, saat thread A membuat thread B, thread B akan memiliki variabel lokal thread yang sama dengan thread A. Anda juga dapat mengganti childValue() untuk menginisialisasi variabel lokal thread dari thread anak. Metode ini akan menerima nilai variabel lokal thread yang diteruskan sebagai parameter dari thread induk.
Gunakan doktrin
Artikel ini diterjemahkan dari "Buku Masak Konkurensi Java 7" (D Gua Ge mencurinya sebagai "Koleksi Contoh Konkurensi Java7") dan hanya digunakan sebagai bahan pembelajaran. Ini tidak boleh digunakan untuk tujuan komersial apa pun tanpa izin.
Keberhasilan kecil
Di bawah ini adalah versi lengkap dari semua kode yang disertakan dalam contoh di bagian ini.
Kode lengkap kelas UnsafeTask:
Copy kode kodenya sebagai berikut:
paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.Date;
import java.util.bersamaan.TimeUnit;
/**
* Contoh dimana keamanan benang tidak dapat dijamin
* Tanggal: 23-09-2013
* Waktu: 23:58
*/
kelas publik UnsafeTask mengimplementasikan Runnable {
Tanggal mulai Tanggal pribadi;
@Mengesampingkan
menjalankan kekosongan publik() {
tanggal mulai = Tanggal baru();
System.out.printf("Utas Awal: %s : %s/n",
Thread.currentThread().getId(), startDate);
mencoba {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Utas Selesai: %s : %s/n",
Thread.currentThread().getId(), startDate);
}
}
Kode lengkap kelas UnsafeMain:
Copy kode kodenya sebagai berikut:
paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.bersamaan.TimeUnit;
/**
* Contoh thread tidak aman
* Tanggal: 24-09-2013
* Waktu: 00:04
*/
kelas publik UnsafeMain {
public static void main(String[] args) {
Tugas UnsafeTask = new UnsafeTask();
untuk (int saya = 0; saya < 10; saya++) {
Utas utas = Utas baru (tugas);
thread.mulai();
mencoba {
TimeUnit.SECONDS.sleep(2);
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Kode lengkap kelas SafeTask:
Copy kode kodenya sebagai berikut:
paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.Date;
import java.util.bersamaan.TimeUnit;
/**
* Gunakan variabel lokal thread untuk memastikan keamanan thread
* Tanggal: 29-09-2013
* Waktu: 23:34
*/
SafeTask kelas publik mengimplementasikan Runnable {
pribadi statis ThreadLocal<Tanggal> startDate = baru
ThreadLokal<Tanggal>() {
@Mengesampingkan
Tanggal dilindungi initialValue() {
kembalikan Tanggal baru();
}
};
@Mengesampingkan
menjalankan kekosongan publik() {
System.out.printf("Utas Awal: %s : %s/n",
Thread.currentThread().getId(), startDate.get());
mencoba {
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Utas Selesai: %s : %s/n",
Thread.currentThread().getId(), startDate.get());
}
}
Kode lengkap kelas SafeMain:
Copy kode kodenya sebagai berikut:
paket com.diguage.books.concurrencycookbook.chapter1.recipe9;
import java.util.bersamaan.TimeUnit;
/**
* Contoh benang aman
* Tanggal: 24-09-2013
* Waktu: 00:04
*/
SafeMain kelas publik {
public static void main(String[] args) {
Tugas SafeTask = SafeTask baru();
untuk (int saya = 0; saya < 10; saya++) {
Utas utas = Utas baru (tugas);
thread.mulai();
mencoba {
TimeUnit.SECONDS.sleep(2);
} tangkapan (InterruptedException e) {
e.printStackTrace();
}
}
}
}