111. Bagaimana untuk menukar data antara benang?
Untuk menukar data antara utas, anda boleh menggunakan pelbagai pendekatan dan cara: contohnya, gunakan pembolehubah atom, koleksi disegerakkan dan semafor. Tetapi untuk menyelesaikan masalah ini, saya akan memberikan contoh dengan Exchanger . Penukar ialah kelas penyegerakan daripada pakej serentak yang memudahkan pertukaran elemen antara sepasang benang dengan mencipta titik penyegerakan biasa. Penggunaannya memudahkan pertukaran data antara dua utas. Cara ia berfungsi agak mudah: ia menunggu dua utas berasingan untuk memanggil kaedah exchange() nya . Sesuatu seperti titik pertukaran dicipta di antara mereka: benang pertama meletakkan objeknya dan menerima objek yang lain sebagai balasan, dan yang terakhir, seterusnya, menerima objek yang pertama dan meletakkan objeknya sendiri. Iaitu, thread pertama menggunakan kaedah exchange() dan melahu sehingga thread lain memanggil kaedah exchange() pada objek yang sama dan data ditukar antara mereka. Sebagai contoh, pertimbangkan pelaksanaan 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();
}
}
}
}
Dalam pembina thread, kami mentakrifkan objek Exchanger yang menerima objek jenis String , dan semasa permulaan (dalam kaedah run ) kami menggunakan exchange() untuk menukar mesej dengan thread lain yang menggunakan kaedah ini dalam Exchanger yang sama . Mari jalankannya dalam utama :
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 memaparkan:
112. Apakah perbezaan antara kelas Thread dan antara muka Runnable?
Perkara pertama yang saya akan perhatikan ialah Thread ialah kelas, Runnable ialah antara muka, yang merupakan perbezaan yang sangat jelas =D Saya juga akan mengatakan bahawa Thread menggunakan Runnable (komposisi). Iaitu, kita mempunyai dua cara:-
Warisi daripada Thread , ganti kaedah run, kemudian buat objek ini dan mulakan thread melalui kaedah start() .
-
Laksanakan Runnable dalam kelas tertentu, laksanakan kaedah run() nya , dan kemudian buat objek Thread , berikan pelaksanaan objek ini antara muka Runnable kepada pembinanya . Nah, pada akhirnya, lancarkan objek Thread menggunakan kaedah start() .
-
Apabila anda melaksanakan antara muka Runnable , anda tidak mengubah tingkah laku benang. Pada asasnya anda hanya memberikan benang sesuatu untuk dijalankan. Dan ini adalah komposisi kami, yang seterusnya dianggap sebagai pendekatan yang baik.
-
melaksanakan Runnable memberikan lebih fleksibiliti kepada kelas anda. Jika anda mewarisi daripada Thread , maka tindakan yang anda lakukan akan sentiasa berada pada thread. Tetapi jika anda melaksanakan Runnable, ia tidak semestinya hanya benang. Lagipun, anda boleh sama ada menjalankannya dalam benang atau menyerahkannya kepada beberapa perkhidmatan pelaksana. Baiklah, atau hanya serahkannya ke suatu tempat sebagai tugas dalam aplikasi berbenang tunggal.
-
Menggunakan Runnable membolehkan anda memisahkan pelaksanaan tugas secara logik daripada logik kawalan benang.
-
Di Jawa, hanya warisan tunggal yang mungkin, jadi hanya satu kelas boleh dilanjutkan. Pada masa yang sama, bilangan antara muka yang boleh dikembangkan adalah tidak terhad (baik, bukan tidak terhad, tetapi 65535 , tetapi anda tidak mungkin mencapai had ini).
113. Terdapat benang T1, T2 dan T3. Bagaimana untuk melaksanakannya secara berurutan?
Perkara pertama dan paling mudah yang terlintas di fikiran ialah menggunakan kaedah join() . Ia menangguhkan pelaksanaan utas semasa (yang dipanggil kaedah) sehingga utas yang mana kaedah itu dipanggil selesai melaksanakan. Mari kita buat pelaksanaan 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 mulakan tiga utas sedemikian satu demi 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();
Output konsol:
Tugasan praktikal
114. Jumlah Diagonal Matriks (masalah Leetcode)
Keadaan: Kira jumlah semua unsur pada pepenjuru utama dan semua unsur pada pepenjuru tambahan yang bukan sebahagian daripada pepenjuru utama. 1. Dengan matriks dalam bentuk: mat = [[1,2,3], [4,5,6], [7,8,9]] Output hendaklah - 25 2. Dengan matriks - mat = [[1,1 ,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] Output hendaklah - 8 3. Dengan a matriks - mat = [[ 5]] Kesimpulannya hendaklah - 5 Jeda bacaan dan laksanakan keputusan anda. Penyelesaian saya adalah seperti 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 berlaku dengan satu laluan melalui tatasusunan, di mana kita mempunyai dua indeks untuk laporan: i - untuk melaporkan baris tatasusunan dan lajur pepenjuru utama, j - untuk melaporkan lajur pepenjuru tambahan. Jika sel pepenjuru utama dan yang tambahan bertepatan, maka salah satu nilai diabaikan semasa mengira jumlahnya. Mari kita semak menggunakan matriks dari keadaan:
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));
Output konsol:
115. Move Zeroes (cabaran Leetcode)
Keadaan: Dalam tatasusunan integer, gerakkan semua 0 ke penghujung, mengekalkan susunan relatif unsur bukan sifar. 1. Dengan tatasusunan: [0,1,0,3,12] Output hendaklah: [1,3,12,0,0] 2. Dengan tatasusunan: [0] Output hendaklah: [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;// заполняем последние элементы массива нулями согласно счётчику нулей
}
}
Peperiksaan:
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));
Output konsol:
116. Nama Senarai <String> yang diberikan. Keluarkan huruf pertama daripada setiap nama dan putar senarai yang diisih
1. Perkara pertama yang terlintas di fikiran ialah kaedah kelas Koleksi , yang mengandungi banyak kaedah 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 kami menggunakan Java versi 8 dan lebih tinggi, kami hanya perlu menunjukkan penyelesaian melalui strim:
public static List<String> processTheList(List<String> nameList) {
return nameList.stream()
.map(x -> x.substring(1))
.sorted().collect(Collectors.toList());
}
Terlepas dari penyelesaian yang dipilih, semakan mungkin seperti 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));
Output konsol:
117. Balikkan tatasusunan
Penyelesaian 1 Sekali lagi, perkara pertama yang terlintas di fikiran ialah menggunakan kaedah Koleksi kelas utiliti tambahan . Tetapi kerana kita mempunyai tatasusunan, kita perlu menukarnya terlebih dahulu menjadi koleksi (senarai):public static Integer[] reverse(Integer[] arr) {
List<Integer> list = Arrays.asList(arr);
Collections.reverse(list);
return list.toArray(arr);
}
Penyelesaian 2 Memandangkan soalan itu adalah mengenai tatasusunan, saya fikir ia perlu untuk menunjukkan penyelesaian tanpa menggunakan fungsi siap sedia di luar kotak, dan boleh dikatakan, mengikut 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;
}
Peperiksaan:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Output konsol:
118. Periksa sama ada rentetan ialah palindrom
Penyelesaian 1 Perlu diingati dengan segera StringBuilder : ia lebih fleksibel dan kaya dengan pelbagai kaedah berbanding String biasa . Kami amat berminat dengan kaedah terbalik :public static boolean isPalindrome(String string) {
string = string.toLowerCase(); //приводит всю строку к нижнему регистру
StringBuilder builder = new StringBuilder();
builder.append(string);
builder.reverse(); // перевочиваем строку методом Builder-а
return (builder.toString()).equals(string);
}
Penyelesaian: Pendekatan seterusnya adalah tanpa menggunakan "celah" di luar kotak. Kami membandingkan aksara dari belakang rentetan dengan aksara yang sepadan dari hadapan:
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 menyemak kedua-dua pendekatan:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Output konsol:
119. Tulis algoritma pengisihan mudah (Bubble, Selection atau Shuttle). Bagaimana ia boleh diperbaiki?
Sebagai algoritma mudah untuk pelaksanaan, saya memilih jenis pilihan - Isih Pemilihan: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 dipertingkatkan akan kelihatan 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 sama ada pengisihan telah benar-benar bertambah baik. Mari bandingkan prestasi:
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-dua jenis bermula dalam kitaran yang sama, kerana jika terdapat gelung yang berasingan, pengisihan daripada dalam kod di atas akan menunjukkan hasil yang lebih teruk berbanding jika ia diletakkan di tempat kedua. Ini disebabkan oleh fakta bahawa program "memanaskan badan" dan kemudian berfungsi sedikit lebih cepat. Tetapi saya akan keluar sedikit daripada topik. Selepas lima larian semakan ini dalam konsol, saya melihat peningkatan dalam prestasi sebanyak: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.0919807057435616% Oleh kerana ini adalah 2.091980705743566661% hasil yang cukup baik.
120. Tulis algoritma (urutan tindakan) untuk mengarang literal jenis int dengan literal jenis bait. Terangkan apa yang berlaku kepada ingatan
-
nilai bait ditukar kepada int. Tidak 1 bait ingatan akan diperuntukkan untuknya, tetapi seperti semua nilai int - 4, jika nilai ini belum ada pada timbunan int. Jika ada, pautan kepadanya hanya akan diterima.
-
Dua nilai int akan ditambah dan yang ketiga akan diperolehi. Bahagian memori baharu akan diperuntukkan untuknya - 4 bait (atau rujukan akan diterima daripada timbunan int kepada nilai sedia ada).
Dalam kes ini, memori dua int masih akan diduduki, dan nilainya masing-masing akan disimpan pada timbunan int.
GO TO FULL VERSION