111. Paano makipagpalitan ng data sa pagitan ng mga thread?
Upang makipagpalitan ng data sa pagitan ng mga thread, maaari kang gumamit ng maraming iba't ibang mga diskarte at paraan: halimbawa, gumamit ng mga atomic variable, naka-synchronize na mga koleksyon, at isang semaphore. Ngunit upang malutas ang problemang ito, magbibigay ako ng isang halimbawa sa Exchanger . Ang Exchanger ay isang klase ng pag-synchronize mula sa kasabay na pakete na nagpapadali sa pagpapalitan ng mga elemento sa pagitan ng isang pares ng mga thread sa pamamagitan ng paglikha ng isang karaniwang punto ng pag-synchronize. Pinapasimple ng paggamit nito ang pagpapalitan ng data sa pagitan ng dalawang thread. Ang paraan ng paggana nito ay medyo simple: naghihintay ito ng dalawang magkahiwalay na thread na tawagan ang exchange() method nito . Isang bagay na tulad ng isang exchange point ay nilikha sa pagitan nila: ang unang thread ay naglalagay ng bagay nito at tumatanggap ng bagay ng isa bilang kapalit, at ang huli, naman, ay tumatanggap ng bagay ng una at inilalagay ang kanyang sarili. Iyon ay, ang unang thread ay gumagamit ng exchange() na paraan at idle hanggang ang isa pang thread ay tumawag sa exchange() na paraan sa parehong bagay at ang data ay ipinagpapalit sa pagitan nila. Bilang halimbawa, isaalang-alang ang sumusunod na pagpapatupad ng klase ng Thread :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();
}
}
}
}
Sa thread constructor, tinutukoy namin ang isang Exchanger object na tumatanggap ng mga object ng uri ng String , at sa startup (sa run method ) ginagamit namin ang exchange() nito para makipagpalitan ng mensahe sa isa pang thread na gumagamit ng paraang ito sa parehong Exchanger . Patakbuhin natin ito sa pangunahing :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
Ipapakita ng console ang:
112. В чем заключается отличие класса Thread от интерфейса Runnable?
Первое, что отмечу, Thread — это класс, Runnable — интерфейс, что весьма очевидное отличие =D Также скажу, что Thread использует Runnable (композиция). То есть, у нас есть два пути:-
Наследоваться от Thread, переопределить метод run, после чего создать данный an object и запустить поток через метод start().
-
Реализовать Runnable в определенном классе, реализовать его метод run(), после чего создать an object Thread, задав ему в конструктор этот an object-реализацию интерфейса Runnable. Ну и в конце запустить an object Thread с помощью метода start().
-
при реализации интерфейса Runnable вы не изменяете поведение потока. По сути вы просто даете потоку что-то запустить. А это у нас композиция, что в свою очередь считается хорошим подходом.
-
реализация Runnable даёт больше гибкости вашему классу. Если вы наследуетесь от Thread, то действие, которое вы выполняете, всегда будет в потоке. Но если вы реализуете Runnable, это не обязательно будет просто поток. Ведь вы можете How запустить его в потоке, так и передать его Howой-либо службе-исполнителю. Ну or просто передать его куда-то How задачу в однопоточном приложении.
-
использование Runnable позволяет логически отделить выполнение задачи от логики управления потоками.
-
в Java возможно только одиночное наследование, поэтому можно расширить только один класс. В то же время количество расширяемых интерфейсов неограниченно (ну не совсем неограниченное, а 65535, но вряд ли вы когда-то упретесь в этот лимит).
113. Есть потоки Т1, Т2 и Т3. Как реализовать их последовательное выполнение?
Самое первое и самое простое, что приходит на ум — это использование метода join(). Он приостанавливает выполнение текущего (вызвавшего данный метод) потока до тех пор, пока поток, на котором был вызван метод, не закончит свое выполнение. Создадим свою реализацию потока: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 + " - закончил свою работу");
}
}
Запустим три таких потока поочередно, используя 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();
Вывод в консоли:
Практические задания
114. Matrix Diagonal Sum (задача с Leetcode)
Условие: Подсчитайте сумму всех элементов на основной диагонали и всех элементов на дополнительной диагонали, которые не являются частью основной диагонали. 1. При матрице вида : mat = [[1,2,3], [4,5,6], [7,8,9]] Вывод должен быть — 25 2. При матрице - mat = [[1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] Вывод должен быть — 8 3. При матрице - mat = [[5]] Вывод должен быть — 5 Сделайте паузу в прочтении и реализуйте своё решение. Мое же решение будет следующим: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;
}
Всё происходит с помощью одного прохода по массиву, во время которого у нас есть два индекса для отчёта: i — для отчёта строк массива и колонок основной диагонали, j — для отчёта колонок дополнительной диагонали. Если же ячейка основной диагонали и дополнительной совпадают, то одно из значений игнорируется при подсчете суммы. Проверим, используя матрицы из условия:
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));
Вывод в консоли:
115. Move Zeroes (задача с Leetcode)
Условие: В целочисленном массиве переместите все 0 в конец, сохраняя относительный порядок ненулевых элементов. 1. При массиве: [0,1,0,3,12] Вывод должен быть: [1,3,12,0,0] 2. При массиве: [0] Вывод должен быть: [0] Сделайте паузу и напишите свое решение ... Моё решение: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;// заполняем последние элементы массива нулями согласно счётчику нулей
}
}
Проверка:
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));
Вывод в консоль:
116. Given List <String> names. Удалите первую букву из каждого имени и поверните отсортированный список
1. Первое, что приходит в голову, это методы класса Collections, хранящий в себе множество вспомогательных методов для коллекций: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. Также если мы используем Java версии 8 и выше мы просто обязаны показать решение через стримы:
public static List<String> processTheList(List<String> nameList) {
return nameList.stream()
.map(x -> x.substring(1))
.sorted().collect(Collectors.toList());
}
Независимо от выбранного решения, проверка может быть следующая:
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));
Вывод в консоли:
117. Переверните массив
Решение 1 Опять же, первое, что приходит в голову — использовать методы вспомогательного, утorтного класса Collections. Но так How у нас массив, сперва нужно преобразовать его в коллекцию (список):public static Integer[] reverse(Integer[] arr) {
List<Integer> list = Arrays.asList(arr);
Collections.reverse(list);
return list.toArray(arr);
}
Решение 2 Так How вопрос был про массив, думаю, необходимо показать решение и без использования готового функционала из коробки, а так сказать, по классике:
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;
}
Проверка:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Вывод в консоли:
118. Проверить, является ли строка палиндромом
Решение 1 Стоит сразу вспомнить о StringBuilder: он более гибкий и насыщенный различными методами по сравнению с обычным String. Нас особенно интересует метод reverse:public static boolean isPalindrome(String string) {
string = string.toLowerCase(); //приводит всю строку к нижнему регистру
StringBuilder builder = new StringBuilder();
builder.append(string);
builder.reverse(); // перевочиваем строку методом Builder-а
return (builder.toString()).equals(string);
}
Решение: Следующий подход будет без использования “лазеек” из коробки. Сравниваем символы из задней части строки с соответствующими символами из передней:
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;
}
И проверяем оба подхода:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Вывод в консоли:
119. Написать простой алгоритм сортировки (Bubble, Selection or Shuttle). Как его можно улучшить?
В качестве просто алгоритма для реализации я выбрал сортировку выбором — Selection Sort: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 итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
Улучшенный вариант будет выглядеть следующим образом:
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;
}
Ну а теперь нам нужно убедиться, правда ли sorting улучшилась. Давайте сравним производительность:
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 + "%");
Обе сортировки запустorсь в одном и том же цикле, т.к. если бы были отдельные циклы, sorting из в codeе выше показывала бы худший результат, нежели если её поставить второй. Это связано с тем, что программа How бы “разогревается” и дальше работает немного быстрее. Но я немного отошёл от темы. После пяти запусков данной проверки в консоли я увидел увеличение производительности на: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.091980705743566% 37.120220461591444% Как по мне, это довольно-таки хороший результат.
120. Напишите алгоритм (последовательность действий) составления литерала типа int с литералом типа byte. Объясните, что происходит с памятью
-
byte meaning приводится к int. Для него будет выделен не 1 byte памяти, а How и для всех int значений — 4, если этого значения ещё нет в int стеке. Если же есть — просто будет получена link на него.
-
Два int значения будут сложены и получится третье. Под него выделится новый участок памяти — 4 byteа (либо будет получена link из int стека на существующее meaning).
При этом память двух int всё ещё будет занята, и их значения будут храниться в int стеке соответственно.
GO TO FULL VERSION