111. ¿Cómo intercambiar datos entre hilos?
Para intercambiar datos entre subprocesos, puede utilizar muchos enfoques y medios diferentes: por ejemplo, utilizar variables atómicas, colecciones sincronizadas y un semáforo. Pero para solucionar este problema, daré un ejemplo con Exchanger . Exchanger es una clase de sincronización del paquete concurrente que facilita el intercambio de elementos entre un par de subprocesos mediante la creación de un punto de sincronización común. Su uso simplifica el intercambio de datos entre dos hilos. La forma en que funciona es bastante simple: espera a que dos subprocesos separados llamen a su método exchange() . Entre ellos se crea algo así como un punto de intercambio: el primer hilo pone su objeto y recibe a cambio el objeto del otro, y éste, a su vez, recibe el objeto del primero y pone el suyo. Es decir, el primer hilo usa el método exchange() y está inactivo hasta que otro hilo llama al método exchange() en el mismo objeto y se intercambian datos entre ellos. Como ejemplo, considere la siguiente implementación de la clase 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();
}
}
}
}
En el constructor del hilo, definimos un objeto Exchanger que acepta objetos de tipo String , y al inicio (en el método run ) usamos su exchange() para intercambiar un mensaje con otro hilo que usa este método en el mismo Exchanger . Ejecutémoslo en main :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
La consola mostrará:
112. ¿Cuál es la diferencia entre la clase Thread y la interfaz Runnable?
Lo primero que notaré es que Thread es una clase, Runnable es una interfaz, lo cual es una diferencia muy obvia =D También diré que Thread usa Runnable (composición). Es decir, tenemos dos formas:-
Hereda de Thread , anula el método de ejecución, luego crea este objeto e inicia el hilo a través del método start() .
-
Implemente Runnable en una determinada clase, implemente su método run() y luego cree un objeto Thread , asignando la implementación de este objeto de la interfaz Runnable a su constructor . Bueno, al final, lanza el objeto Thread usando el método start() .
-
Cuando implementas la interfaz Runnable , no cambias el comportamiento del hilo. Básicamente, simplemente le estás dando al hilo algo para ejecutar. Y esta es nuestra composición, que a su vez se considera un buen enfoque.
-
La implementación de Runnable le da más flexibilidad a su clase. Si heredas de Thread , entonces la acción que realices siempre estará en el hilo. Pero si implementas Runnable, no tiene por qué ser solo un hilo. Después de todo, puedes ejecutarlo en un hilo o pasarlo a algún servicio ejecutor. Bueno, o simplemente páselo a algún lugar como una tarea en una aplicación de un solo subproceso.
-
El uso de Runnable le permite separar lógicamente la ejecución de tareas de la lógica de control de subprocesos.
-
En Java, sólo es posible la herencia única, por lo que sólo se puede ampliar una clase. Al mismo tiempo, la cantidad de interfaces expandibles es ilimitada (bueno, no del todo ilimitada, pero 65535 , pero es poco probable que alguna vez alcance este límite).
113. Hay hilos T1, T2 y T3. ¿Cómo implementarlos secuencialmente?
Lo primero y más sencillo que me viene a la mente es utilizar el método join() . Suspende la ejecución del hilo actual (que llamó al método) hasta que el hilo en el que se llamó al método termine de ejecutarse. Creemos nuestra propia implementación de hilo: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 + " - закончил свою работу");
}
}
Comencemos tres de estos hilos uno por uno usando 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();
Salida de consola:
Tareas practicas
114. Suma diagonal de matriz (problema de Leetcode)
Condición: Calcula la suma de todos los elementos de la diagonal principal y todos los elementos de la diagonal adicional que no forman parte de la diagonal principal. 1. Con una matriz de la forma: mat = [[1,2,3], [4,5,6], [7,8,9]] La salida debe ser - 25 2. Con una matriz - mat = [[1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] La salida debe ser - 8 3. Con una matriz - mat = [[ 5]] La conclusión debe ser - 5 Haga una pausa en la lectura e implemente su decisión. Mi solución sería la siguiente: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;
}
Todo sucede con una pasada por la matriz, durante la cual tenemos dos índices para el informe: i - para informar las filas de la matriz y las columnas de la diagonal principal, j - para informar las columnas de la diagonal adicional. Si la celda de la diagonal principal y la adicional coinciden, entonces uno de los valores se ignora al calcular la suma. Comprobemos usando las matrices de la condición:
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));
Salida de consola:
115. Mover ceros (desafío Leetcode)
Condición: en una matriz de números enteros, mueva todos los ceros hasta el final, manteniendo el orden relativo de los elementos distintos de cero. 1. Con una matriz: [0,1,0,3,12] La salida debe ser: [1,3,12,0,0] 2. Con una matriz: [0] La salida debe ser: [0] Haz una pausa y escribe mi decisión... Mi decisión: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;// заполняем последние элементы массива нулями согласно счётчику нулей
}
}
Examen:
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));
Salida de consola:
116. Lista dada <Cadena> nombres. Elimine la primera letra de cada nombre y rote la lista ordenada
1. Lo primero que me viene a la mente son los métodos de la clase Colecciones , que contiene muchos métodos auxiliares para las colecciones: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. Además, si utilizamos Java versión 8 y superior, simplemente debemos mostrar la solución mediante streams:
public static List<String> processTheList(List<String> nameList) {
return nameList.stream()
.map(x -> x.substring(1))
.sorted().collect(Collectors.toList());
}
Independientemente de la solución elegida, la comprobación puede ser la siguiente:
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));
Salida de consola:
117. Voltear la matriz
Solución 1 De nuevo, lo primero que me viene a la mente es utilizar los métodos de la clase de utilidad auxiliar Collections . Pero como tenemos una matriz, primero debemos convertirla en una colección (lista):public static Integer[] reverse(Integer[] arr) {
List<Integer> list = Arrays.asList(arr);
Collections.reverse(list);
return list.toArray(arr);
}
Solución 2 Dado que la pregunta era sobre una matriz, creo que es necesario mostrar la solución sin utilizar la funcionalidad lista para usar, por así decirlo, según los clásicos:
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;
}
Examen:
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(Arrays.toString(reverse(arr)));
Salida de consola:
118. Comprobar si una cuerda es un palíndromo
Solución 1 Vale la pena recordar inmediatamente StringBuilder : es más flexible y rico en varios métodos en comparación con el String normal . Estamos especialmente interesados en el método inverso :public static boolean isPalindrome(String string) {
string = string.toLowerCase(); //приводит всю строку к нижнему регистру
StringBuilder builder = new StringBuilder();
builder.append(string);
builder.reverse(); // перевочиваем строку методом Builder-а
return (builder.toString()).equals(string);
}
Solución: El siguiente enfoque será sin utilizar las “lagunas” existentes. Comparamos los caracteres de la parte posterior de la cadena con los caracteres correspondientes del frente:
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;
}
Y comprobamos ambos enfoques:
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Salida de consola:
119. Escriba un algoritmo de clasificación simple (Burbuja, Selección o Lanzadera). ¿Cómo puede ser mejorado?
Como algoritmo simple de implementación, elegí el ordenamiento por selección: Ordenamiento por selección: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]; // так Cómo отрезок постоянно уменьшается
arr[i] = temp; // и выпадающие из него числа будут минимальными в текущем отрезке
} // и Cómo итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
La versión mejorada quedaría así:
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;
}
Bueno, ahora tenemos que asegurarnos de si la clasificación realmente ha mejorado. Comparemos el rendimiento:
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 + "%");
Ambos tipos comenzaron en el mismo ciclo, porque Si hubiera bucles separados, ordenar desde el código anterior mostraría un resultado peor que si se colocara en segundo lugar. Esto se debe al hecho de que el programa se "calienta" y luego funciona un poco más rápido. Pero me estoy saliendo un poco del tema. Después de cinco ejecuciones de esta verificación en la consola, vi un aumento en el rendimiento de: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.091980705743566% 37.120220461591444% En cuanto a mí, esto es bastante bueno. resultado.
120. Escriba un algoritmo (secuencia de acciones) para componer un literal de tipo int con un literal de tipo byte. Explica qué sucede con la memoria.
-
El valor del byte se convierte a int. No se le asignará 1 byte de memoria, pero como todos los valores int, 4, si este valor aún no está en la pila int. Si lo hay, simplemente se recibirá un enlace.
-
Se sumarán dos valores int y se obtendrá el tercero. Se le asignará una nueva sección de memoria: 4 bytes (o se recibirá una referencia de la pila int al valor existente).
En este caso, la memoria de dos int todavía estará ocupada y sus valores se almacenarán en la pila de int, respectivamente.
GO TO FULL VERSION