111. Bagaimana cara bertukar data antar thread?
Untuk bertukar data antar thread, Anda dapat menggunakan banyak pendekatan dan cara yang berbeda: misalnya, menggunakan variabel atom, koleksi tersinkronisasi, dan semaphore. Namun untuk mengatasi masalah tersebut, saya akan memberikan contoh dengan Exchanger . Exchanger adalah kelas sinkronisasi dari paket konkuren yang memfasilitasi pertukaran elemen antara sepasang thread dengan membuat titik sinkronisasi umum. Penggunaannya menyederhanakan pertukaran data antara dua thread. Cara kerjanya cukup sederhana: ia menunggu dua thread terpisah memanggil metode exchange() miliknya . Sesuatu seperti titik pertukaran dibuat di antara mereka: utas pertama menempatkan objeknya dan menerima objek dari yang lain sebagai imbalannya, dan yang terakhir, pada gilirannya, menerima objek dari yang pertama dan menempatkan miliknya sendiri. Artinya, thread pertama menggunakan metode exchange() dan menganggur hingga thread lain memanggil metode exchange() pada objek yang sama dan data dipertukarkan di antara keduanya. Sebagai contoh, perhatikan implementasi kelas Thread berikut :public class CustomThread extends Thread {
private String threadName;
private String message;
private Exchanger<String> exchanger;
public CustomThread(String threadName, Exchanger<String> exchanger) {
this.threadName = threadName;
this.exchanger = exchanger;
}
public void setMessage(final String message) {
this.message = message;
}
@Override
public void run() {
while (true) {
try {
message = exchanger.exchange(message);
System.out.println(threadName + " поток получил сообщение: " + message);
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Di konstruktor thread, kita mendefinisikan objek Exchanger yang menerima objek bertipe String , dan saat startup (dalam metode run ) kita menggunakan exchange() untuk bertukar pesan dengan thread lain yang menggunakan metode ini di Exchanger yang sama . Mari kita jalankan di main :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
Konsol akan menampilkan:
112. Apa perbedaan antara kelas Thread dan antarmuka Runnable?
Hal pertama yang saya perhatikan adalah bahwa Thread adalah sebuah kelas, Runnable adalah sebuah antarmuka, yang merupakan perbedaan yang sangat jelas =D Saya juga akan mengatakan bahwa Thread menggunakan Runnable (komposisi). Artinya, kita punya dua cara:-
Mewarisi dari Thread , ganti metode run, lalu buat objek ini dan mulai thread melalui metode start() .
-
Implementasikan Runnable di kelas tertentu, implementasikan metode run() -nya , lalu buat objek Thread , dengan menugaskan implementasi objek antarmuka Runnable ini ke konstruktornya . Nah, pada akhirnya, luncurkan objek Thread menggunakan metode start() .
-
Saat Anda mengimplementasikan antarmuka Runnable , Anda tidak mengubah perilaku thread. Pada dasarnya Anda hanya memberikan sesuatu pada thread untuk dijalankan. Dan inilah komposisi kami, yang pada gilirannya dianggap sebagai pendekatan yang baik.
-
mengimplementasikan Runnable memberikan lebih banyak fleksibilitas pada kelas Anda. Jika Anda mewarisi dari Thread , maka tindakan yang Anda lakukan akan selalu ada di thread. Namun jika Anda mengimplementasikan Runnable, itu tidak harus hanya berupa thread. Lagi pula, Anda bisa menjalankannya di thread atau meneruskannya ke beberapa layanan pelaksana. Ya, atau serahkan saja ke suatu tempat sebagai tugas dalam aplikasi single-thread.
-
Menggunakan Runnable memungkinkan Anda memisahkan eksekusi tugas secara logis dari logika kontrol thread.
-
Di Java, hanya pewarisan tunggal yang dimungkinkan, sehingga hanya satu kelas yang dapat diperluas. Pada saat yang sama, jumlah antarmuka yang dapat diperluas tidak terbatas (yah, tidak sepenuhnya tidak terbatas, tetapi 65535 , tetapi kemungkinan besar Anda tidak akan pernah mencapai batas ini).
113. Ada benang T1, T2 dan T3. Bagaimana cara menerapkannya secara berurutan?
Hal pertama dan paling sederhana yang terlintas dalam pikiran adalah menggunakan metode join() . Ini menangguhkan eksekusi thread saat ini (yang memanggil metode) hingga thread yang memanggil metode tersebut selesai dieksekusi. Mari buat implementasi thread kita sendiri:public class CustomThread extends Thread {
private String threadName;
public CustomThread(final String threadName){
this.threadName = threadName;
}
@Override
public void run() {
System.out.println(threadName + " - начал свою работу");
try {
// происходит некая логика
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName + " - закончил свою работу");
}
}
Mari kita mulai tiga thread tersebut satu per satu menggunakan join() :
CustomThread t1 = new CustomThread("Первый поток");
t1.start();
t1.join();
CustomThread t2 = new CustomThread("Второй поток");
t2.start();
t2.join();
CustomThread t3 = new CustomThread("Третий поток");
t3.start();
t3.join();
Keluaran konsol:
Tugas praktis
114. Jumlah Diagonal Matriks (masalah Leetcode)
Syarat: Hitung jumlah semua elemen pada diagonal utama dan semua elemen pada diagonal tambahan yang bukan merupakan bagian dari diagonal utama. 1. Dengan matriks berbentuk: mat = [[1,2,3], [4,5,6], [7,8,9]] Outputnya seharusnya - 25 2. Dengan matriks - mat = [[1,1 ,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] Outputnya seharusnya - 8 3. Dengan a matriks - mat = [[ 5]] Kesimpulannya seharusnya - 5 Jeda membaca dan terapkan keputusan Anda. Solusi saya adalah sebagai berikut:public static int countDiagonalSum(int[][] matrix) {
int sum = 0;
for (int i = 0, j = matrix.length - 1; i < matrix.length; i++, j--) {
sum += matrix[i][i];
if (j != i) {
sum += matrix[i][j];
}
}
return sum;
}
Semuanya terjadi dengan satu lintasan melalui larik, di mana kita memiliki dua indeks untuk laporan: i - untuk melaporkan baris larik dan kolom diagonal utama, j - untuk melaporkan kolom diagonal tambahan. Jika sel diagonal utama dan sel diagonal tambahan bertepatan, maka salah satu nilai diabaikan saat menghitung jumlahnya. Mari kita periksa menggunakan matriks dari kondisi:
int[][] arr1 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}};
System.out.println(countDiagonalSum(arr1));
int[][] arr2 = {
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
System.out.println(countDiagonalSum(arr2));
int[][] arr3 = {{5}};
System.out.println(countDiagonalSum(arr3));
Keluaran konsol:
115. Pindahkan Nol (tantangan Leetcode)
Kondisi: Dalam array bilangan bulat, pindahkan semua angka 0 ke akhir, pertahankan urutan relatif elemen bukan nol. 1. Dengan array: [0,1,0,3,12] Outputnya harusnya: [1,3,12,0,0] 2. Dengan array: [0] Outputnya harusnya: [0] Jeda dan tulis keputusan saya... Keputusan saya:public static void moveZeroes(int[] nums) {
int counterWithoutNulls = 0;
int counterWithNulls = 0;
int length = nums.length;
while (counterWithNulls < length) {
if (nums[counterWithNulls] == 0) {// находим нулевые элементы и увеличиваем счётчик
counterWithNulls++;
} else { // сдвигаем элементы на количество найденных нулевых элементов слева
nums[counterWithoutNulls++] = nums[counterWithNulls++];
}
}
while (counterWithoutNulls < length) {
nums[counterWithoutNulls++] = 0;// заполняем последние элементы массива нулями согласно счётчику нулей
}
}
Penyelidikan:
int[] arr1 = {1, 2, 0, 0, 12, 9};
moveZeroes(arr1);
System.out.println(Arrays.toString(arr1));
int[] arr2 = {0};
moveZeroes(arr2);
System.out.println(Arrays.toString(arr2));
Keluaran konsol:
116. Diberikan Daftar nama <String>. Hapus huruf pertama dari setiap nama dan putar daftar yang diurutkan
1. Hal pertama yang terlintas dalam pikiran adalah metode kelas Collections , yang berisi banyak metode tambahan untuk koleksi:public static List<String> processTheList(List<String> nameList) {
for (int i = 0; i < nameList.size(); i++) {
nameList.set(i, nameList.get(i).substring(1));
}
Collections.sort(nameList);
return nameList;
}
2. Selain itu, jika kita menggunakan Java versi 8 dan lebih tinggi, kita cukup menunjukkan solusinya melalui stream:
public static List<String> processTheList(List<String> nameList) {
return nameList.stream()
.map(x -> x.substring(1))
.sorted().collect(Collectors.toList());
}
Terlepas dari solusi yang dipilih, pemeriksaannya mungkin sebagai berikut:
List<String> nameList = new ArrayList();
nameList.add("John");
nameList.add("Bob");
nameList.add("Anna");
nameList.add("Dmitriy");
nameList.add("Peter");
nameList.add("David");
nameList.add("Igor");
System.out.println(processTheList(nameList));
Keluaran konsol:
117. Balikkan susunannya
Solusi 1 Sekali lagi, hal pertama yang terlintas dalam pikiran adalah menggunakan metode kelas utilitas tambahan Collections . Tapi karena kita memiliki array, pertama-tama kita perlu mengubahnya menjadi koleksi (daftar):public static Integer[] reverse(Integer[] arr) {
List<Integer> list = Arrays.asList(arr);
Collections.reverse(list);
return list.toArray(arr);
}
Solusi 2 Karena pertanyaannya adalah tentang array, saya pikir perlu untuk menunjukkan solusinya tanpa menggunakan fungsionalitas yang sudah jadi, dan bisa dikatakan, menurut klasik:
public static Integer[] reverse(Integer[] arr) {
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
return arr;
}
Penyelidikan:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Keluaran konsol:
118. Periksa apakah suatu string merupakan palindrom
Solusi 1 Perlu segera diingat StringBuilder : ini lebih fleksibel dan kaya akan berbagai metode dibandingkan dengan String biasa . Kami terutama tertarik pada metode sebaliknya :public static boolean isPalindrome(String string) {
string = string.toLowerCase(); //приводит всю строку к нижнему регистру
StringBuilder builder = new StringBuilder();
builder.append(string);
builder.reverse(); // перевочиваем строку методом Builder-а
return (builder.toString()).equals(string);
}
Solusi: Pendekatan selanjutnya adalah tanpa menggunakan “celah” yang ada. Kami membandingkan karakter di belakang string dengan karakter terkait di depan:
public static boolean isPalindrome(String string) {
string = string.toLowerCase();
int length = string.length();
int fromBeginning = 0;
int fromEnd = length - 1;
while (fromEnd > fromBeginning) {
char forwardChar = string.charAt(fromBeginning++);
char backwardChar = string.charAt(fromEnd--);
if (forwardChar != backwardChar)
return false;
}
return true;
}
Dan kami memeriksa kedua pendekatan tersebut:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Keluaran konsol:
119. Tulis algoritma pengurutan sederhana (Bubble, Selection atau Shuttle). Bagaimana cara memperbaikinya?
Sebagai algoritma implementasi sederhana, saya memilih pengurutan pilihan - Pengurutan Pilihan:public static void selectionSorting(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j; // выбираем минимальный элемент в текущем числовом отрезке
}
}
int temp = arr[min]; // меняем местами минимальный элемент с элементом под индексом i
arr[min] = arr[i]; // так How отрезок постоянно уменьшается
arr[i] = temp; // и выпадающие из него числа будут минимальными в текущем отрезке
} // и How итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
Versi yang ditingkatkan akan terlihat seperti ini:
public static void improvedSelectionSorting(int[] arr) {
for (int i = 0, j = arr.length - 1; i < j; i++, j--) { // рассматриваемый отрезок с каждой итерацией
// будет уменьшаться с ДВУХ сторон по одному элементу
int min = arr[i];
int max = arr[i];
int minIndex = i;
int maxIndex = i;
for (int n = i; n <= j; n++) { // выбираем min и max на текущем отрезке
if (arr[n] > max) {
max = arr[n];
maxIndex = n;
} else if (arr[n] < min) {
min = arr[n];
minIndex = n;
}
}
// меняем найденный минимальный элемент с позиции с индексом min на позицию с индексом i
swap(arr, i, minIndex);
if (arr[minIndex] == max) {// срабатывает, если элемент max оказался смещен предыдущей перестановкой -
swap(arr, j, minIndex); // на старое место min, поэтому с позиции с индексом min смещаем его на позицию j
} else {
swap(arr, j, maxIndex); // простое обмен местами элементов с индексами max и j
}
}
}
static int[] swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
return arr;
}
Nah, sekarang kita perlu memastikan apakah penyortiran sudah benar-benar membaik. Mari kita bandingkan kinerjanya:
long firstDifference = 0;
long secondDifference = 0;
long primaryTime;
int countOfApplying = 10000;
for (int i = 0; i < countOfApplying; i++) {
int[] arr1 = {234, 33, 123, 4, 5342, 76, 3, 65,
3, 5, 35, 75, 255, 4, 46, 48, 4658, 44, 22,
678, 324, 66, 151, 268, 433, 76, 372, 45, 13,
9484, 499959, 567, 774, 473, 3, 32, 865, 67, 43,
63, 332, 24, 1};
primaryTime = System.nanoTime();
selectionSorting(arr1);
firstDifference += System.nanoTime() - primaryTime;
int[] arr2 = {234, 33, 123, 4, 5342, 76, 3, 65,
3, 5, 35, 75, 255, 4, 46, 48, 4658, 44, 22,
678, 324, 66, 151, 268, 433, 76, 372, 45, 13,
9484, 499959, 567, 774, 473, 3, 32, 865, 67, 43,
63, 332, 24, 1};
primaryTime = System.nanoTime();
improvedSelectionSorting(arr2);
secondDifference += System.nanoTime() - primaryTime;
}
System.out.println(((double) firstDifference / (double) secondDifference - 1) * 100 + "%");
Kedua jenis tersebut dimulai pada siklus yang sama, karena jika ada loop terpisah, pengurutan dari kode di atas akan menunjukkan hasil yang lebih buruk daripada jika ditempatkan kedua. Hal ini disebabkan oleh fakta bahwa program “memanas” dan kemudian bekerja sedikit lebih cepat. Tapi saya akan sedikit keluar dari topik. Setelah lima kali menjalankan pemeriksaan ini di konsol, saya melihat peningkatan kinerja sebesar: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.091980705743566% 37.120220461591444% Bagi saya, ini cukup bagus hasil.
120. Tulislah algoritma (urutan tindakan) untuk menyusun literal bertipe int dengan literal bertipe byte. Jelaskan apa yang terjadi pada ingatan
-
nilai byte diubah menjadi int. Bukan 1 byte memori yang akan dialokasikan untuk itu, tetapi seperti semua nilai int - 4, jika nilai ini belum ada di tumpukan int. Jika ada, tautan ke sana akan diterima begitu saja.
-
Dua nilai int akan ditambahkan dan nilai ketiga akan diperoleh. Bagian memori baru akan dialokasikan untuk itu - 4 byte (atau referensi akan diterima dari tumpukan int ke nilai yang ada).
Dalam hal ini, memori dua int akan tetap terisi, dan nilainya masing-masing akan disimpan di tumpukan int.
GO TO FULL VERSION