Keamanan Thread dan Efek Samping
Hindari keadaan mutable yang dibagikan.
Keamanan Thread dan Efek Samping adalah pelajaran Java Academy gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Java Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Java Academy mencakup 4 pelajaran total.
Paralel Berarti Akses Bersamaan
Dalam aliran parallel, beberapa utas menjalankan lambda Anda pada waktu yang sama. Setiap status bersama yang dapat diubah yang disentuhnya menjadi kondisi balapan.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int safe = IntStream.rangeClosed(1, 1000).parallel().sum();
System.out.println(safe);
}
}Bahaya Mengubah Variabel Bersama
Penghitung biasa yang disentuh oleh banyak utas dapat kehilangan pembaruan. Jangan pernah mengakumulasi hasil ke dalam status bersama yang dapat diubah di dalam forEach.
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger();
IntStream.rangeClosed(1, 1000).parallel()
.forEach(n -> counter.incrementAndGet());
System.out.println(counter.get());
}
}Mengapa Koleksi Biasa Rusak
Menambahkan elemen ke ArrayList yang tidak aman untuk utas dari forEach parallel dapat merusaknya atau menghilangkan elemen. Solusinya adalah tidak berbagi dan mengubahnya sama sekali.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
List<Integer> result = IntStream.rangeClosed(1, 10).parallel()
.boxed()
.collect(Collectors.toList());
System.out.println(result.size());
}
}Alat yang Tepat: collect
Gunakan collect, bukan forEach yang menghasilkan efek samping. Kerangka kolektor mengakumulasi wadah parsial dengan aman untuk setiap utas, lalu menggabungkannya.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
List<Integer> squares = IntStream.rangeClosed(1, 6).parallel()
.map(n -> n * n)
.boxed()
.collect(Collectors.toList());
System.out.println(squares);
}
}Alat yang Tepat: reduce
reduce pada dasarnya aman untuk utas jika operasinya asosiatif dan tidak memiliki status, karena operasi tersebut menggabungkan hasil parsial tanpa perubahan bersama.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int sum = IntStream.rangeClosed(1, 1000).parallel()
.reduce(0, Integer::sum);
System.out.println(sum);
}
}Lambda Tanpa Status
Lambda dalam aliran harus tanpa status: hasilnya hanya bergantung pada masukan, bukan pada variabel eksternal yang dapat diubah atau elemen sebelumnya.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long count = IntStream.rangeClosed(1, 1000).parallel()
.filter(n -> n % 2 == 0)
.count();
System.out.println(count);
}
}Tipe Atomik sebagai Jalan Keluar
Jika Anda benar-benar harus berbagi penghitung, gunakan kelas atomik seperti AtomicLong. Kelas tersebut menyerialkan pembaruan dengan benar, meskipun menambah persaingan akses.
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
AtomicLong sum = new AtomicLong();
IntStream.rangeClosed(1, 1000).parallel()
.forEach(sum::addAndGet);
System.out.println(sum.get());
}
}Koleksi Bersamaan
Jika Anda memerlukan map bersama di antara utas, ConcurrentHashMap aman digunakan. Namun, jika memungkinkan, tetap utamakan Collectors.groupingByConcurrent.
import java.util.Map;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> words = List.of("a", "bb", "cc", "ddd");
Map<Integer, List<String>> byLen = words.parallelStream()
.collect(Collectors.groupingByConcurrent(String::length));
System.out.println(byLen);
}
}Hindari Logika yang Bergantung pada Urutan
Jangan bergantung pada urutan kemunculan di dalam lambda parallel. Operasi yang mengasumsikan pemrosesan berurutan akan menghasilkan hasil yang salah secara parallel.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int sum = IntStream.rangeClosed(1, 100).parallel()
.map(n -> n * 2)
.sum();
System.out.println(sum);
}
}Fungsi Murni Dapat Diparalelkan dengan Aman
Aturan utamanya: tulislah operasi yang murni, tanpa status, dan tidak saling mengganggu. Dengan demikian, aliran dapat berupa sequential atau parallel dan tetap menghasilkan hasil yang identik serta benar.
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> in = List.of("red", "green", "blue");
String out = in.parallelStream()
.map(String::toUpperCase)
.sorted()
.collect(Collectors.joining(","));
System.out.println(out);
}
}Tidak Saling Mengganggu
Jangan mengubah koleksi sumber saat aliran sedang melintasinya. Hal ini menyebabkan ConcurrentModificationException atau perilaku tak terdefinisi, baik secara sequential maupun parallel.
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> src = List.of(1, 2, 3, 4);
List<Integer> doubled = src.parallelStream()
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println(doubled);
}
}Pemeriksaan Singkat
Apa cara yang disarankan untuk mengakumulasi hasil dari aliran parallel?
Rangkuman
Anda telah menjaga aliran parallel tetap benar:
- Status bersama yang dapat diubah dalam lambda parallel menyebabkan kondisi balapan.
- Utamakan
collectdanreducedaripadaforEachyang menghasilkan efek samping. - Jaga agar lambda tidak memiliki status, murni, dan tidak saling mengganggu.
- Jika harus berbagi, gunakan tipe atomik atau koleksi bersamaan, tetapi jadikan itu pilihan terakhir.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Keamanan Thread dan Efek Samping” gratis?
Ya — teks lengkap “Keamanan Thread dan Efek Samping” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Java Academy, upgrade ke CoddyKit PRO. Kursus Java Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Keamanan Thread dan Efek Samping”?
Hindari keadaan mutable yang dibagikan. Kamu berlatih Java Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Java Academy?
Tidak diperlukan pengalaman sebelumnya. Java Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.
Berapa lama pelajaran “Keamanan Thread dan Efek Samping” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Java Academy ini?
Ya. Setiap pelajaran Java Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Membuat Stream Paralel
- Kapan Paralelisme Membantu
- Keamanan Thread dan Efek Samping
- Kesalahan Umum