Mengapa Tidak Memulai Thread Di Konstruktor?

1. Ikhtisar

Dalam tutorial singkat ini, kita akan melihat mengapa kita tidak boleh memulai utas di dalam konstruktor.

Pertama, kami akan memperkenalkan secara singkat konsep publikasi di Java dan JVM. Kemudian, kita akan melihat bagaimana konsep ini memengaruhi cara kita memulai utas.

2. Publikasi dan Escape

Setiap kali kami membuat objek tersedia untuk kode lain di luar cakupan saat ini, kami pada dasarnya menerbitkan objek itu . Misalnya, penerbitan terjadi ketika kita mengembalikan sebuah objek, menyimpannya ke dalam referensi publik , atau bahkan meneruskannya ke metode lain.

Saat kami mempublikasikan objek yang seharusnya tidak kami miliki, kami mengatakan bahwa objek tersebut telah lolos .

Ada banyak cara yang memungkinkan kita untuk melepaskan referensi objek, seperti menerbitkan objek sebelum konstruksi penuhnya. Faktanya, ini adalah salah satu bentuk pelarian yang umum: ketika referensi ini lolos selama konstruksi objek.

Saat referensi ini lolos selama konstruksi, thread lain mungkin melihat objek tersebut dalam keadaan yang tidak tepat dan tidak sepenuhnya dibuat. Hal ini, pada gilirannya, dapat menyebabkan komplikasi keamanan benang yang aneh.

3. Melarikan diri dengan Utas

Salah satu cara paling umum untuk membiarkan referensi ini lolos adalah dengan memulai thread dalam konstruktor. Untuk lebih memahami ini, mari pertimbangkan contoh:

public class LoggerRunnable implements Runnable { public LoggerRunnable() { Thread thread = new Thread(this); // this escapes thread.start(); } @Override public void run() { System.out.println("Started..."); } }

Di sini, kami secara eksplisit meneruskan referensi ini ke konstruktor Thread . Oleh karena itu, utas yang baru dimulai mungkin dapat melihat objek penutup sebelum konstruksi penuhnya selesai. Dalam konteks yang berbarengan, hal ini dapat menyebabkan bug halus.

Juga dimungkinkan untuk meneruskan referensi ini secara implisit :

public class ImplicitEscape { public ImplicitEscape() { Thread t = new Thread() { @Override public void run() { System.out.println("Started..."); } }; t.start(); } }

Seperti yang ditunjukkan di atas, kami membuat kelas dalam anonim yang berasal dari Thread . Karena kelas dalam mempertahankan referensi ke kelas yang melingkupinya, referensi ini sekali lagi keluar dari konstruktor.

Tidak ada yang salah dengan membuat Thread di dalam konstruktor. Namun, sangat tidak disarankan untuk segera memulainya , karena sebagian besar waktu, kami berakhir dengan lolosnya referensi ini , baik secara eksplisit maupun implisit.

3.1. Alternatif

Alih-alih memulai utas di dalam konstruktor, kita dapat mendeklarasikan metode khusus untuk skenario ini:

public class SafePublication implements Runnable { private final Thread thread; public SafePublication() { thread = new Thread(this); } @Override public void run() { System.out.println("Started..."); } public void start() { thread.start(); } };:

Seperti yang ditunjukkan di atas, kami masih menerbitkan referensi ini ke Thread. Namun, kali ini, kami memulai utas setelah konstruktor mengembalikan:

SafePublication publication = new SafePublication(); publication.start();

Oleh karena itu, referensi objek tidak melarikan diri ke utas lain sebelum konstruksi penuhnya.

4. Kesimpulan

Dalam tutorial singkat ini, setelah pengantar singkat tentang publikasi aman, kita melihat mengapa kita tidak boleh memulai utas di dalam konstruktor.

Informasi lebih rinci tentang publikasi dan pelarian di Java dapat ditemukan di buku Java Concurrency in Practice.

Seperti biasa, semua contoh tersedia di GitHub.