111. Comment échanger des données entre les threads ?
Pour échanger des données entre les threads, vous pouvez utiliser de nombreuses approches et moyens différents : par exemple, utiliser des variables atomiques, des collections synchronisées et un sémaphore. Mais pour résoudre ce problème, je vais donner un exemple avec Exchanger . Exchanger est une classe de synchronisation du package concurrent qui facilite l'échange d'éléments entre une paire de threads en créant un point de synchronisation commun. Son utilisation simplifie l'échange de données entre deux threads. Son fonctionnement est assez simple : il attend que deux threads distincts appellent sa méthode Exchange() . Quelque chose comme un point d'échange se crée entre eux : le premier fil pose son objet et reçoit en retour l'objet de l'autre, et ce dernier, à son tour, reçoit l'objet du premier et met le sien. Autrement dit, le premier thread utilise la méthode Exchange() et reste inactif jusqu'à ce qu'un autre thread appelle la méthode Exchange() sur le même objet et que des données soient échangées entre eux. À titre d'exemple, considérons l'implémentation suivante de la classe 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();
}
}
}
}
Dans le constructeur de thread, nous définissons un objet Exchanger qui accepte les objets de type String , et au démarrage (dans la méthode run ) nous utilisons son Exchange() pour échanger un message avec un autre thread qui utilise cette méthode dans le même Exchanger . Exécutons-le dans 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 console affichera :
112. Quelle est la différence entre la classe Thread et l'interface Runnable ?
La première chose que je noterai est que Thread est une classe, Runnable est une interface, ce qui est une différence très évidente =D Je dirai aussi que Thread utilise Runnable (composition). Autrement dit, nous avons deux manières :-
Héritez de Thread , remplacez la méthode run , puis créez cet objet et démarrez le thread via la méthode start() .
-
Implémentez Runnable dans une certaine classe, implémentez sa méthode run() , puis créez un objet Thread , en attribuant cette implémentation d'objet de l' interface Runnable à son constructeur . Eh bien, à la fin, lancez l' objet Thread en utilisant la méthode start() .
-
Lorsque vous implémentez l' interface Runnable , vous ne modifiez pas le comportement du thread. Essentiellement, vous donnez simplement au fil quelque chose à exécuter. Et c’est notre composition, qui à son tour est considérée comme une bonne approche.
-
l'implémentation de Runnable donne plus de flexibilité à votre classe. Si vous héritez de Thread , alors l'action que vous effectuez sera toujours sur le thread. Mais si vous implémentez Runnable, il n’est pas nécessaire que ce soit simplement un thread. Après tout, vous pouvez soit l'exécuter dans un thread, soit le transmettre à un service exécuteur. Eh bien, ou transmettez-le simplement quelque part en tant que tâche dans une application monothread.
-
L'utilisation de Runnable vous permet de séparer logiquement l'exécution des tâches de la logique de contrôle des threads.
-
En Java, un seul héritage est possible, donc une seule classe peut être étendue. Dans le même temps, le nombre d'interfaces extensibles est illimité (enfin, pas tout à fait illimité, mais 65 535 , mais il est peu probable que vous atteigniez jamais cette limite).
113. Il existe des fils T1, T2 et T3. Comment les mettre en œuvre de manière séquentielle ?
La toute première et la plus simple chose qui me vient à l’esprit est d’utiliser la méthode join() . Il suspend l'exécution du thread actuel (qui a appelé la méthode) jusqu'à ce que le thread sur lequel la méthode a été appelée termine son exécution. Créons notre propre implémentation de thread :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 + " - закончил свою работу");
}
}
Commençons trois de ces threads un par un en utilisant 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();
Sortie de la console :
Tâches pratiques
114. Somme diagonale matricielle (problème Leetcode)
Condition : Calculer la somme de tous les éléments de la diagonale principale et de tous les éléments de la diagonale supplémentaire qui ne font pas partie de la diagonale principale. 1. Avec une matrice de la forme : mat = [[1,2,3], [4,5,6], [7,8,9]] Le résultat doit être - 25 2. Avec une matrice - mat = [[1,1 ,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1]] La sortie doit être - 8 3. Avec une matrice - mat = [[ 5]] La conclusion devrait être - 5 Arrêtez la lecture et mettez en œuvre votre décision. Ma solution serait la suivante :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;
}
Tout se passe avec un seul passage à travers le tableau, au cours duquel nous avons deux index pour le rapport : i - pour rapporter les lignes du tableau et les colonnes de la diagonale principale, j - pour rapporter les colonnes de la diagonale supplémentaire. Si la cellule de la diagonale principale et celle supplémentaire coïncident, alors l'une des valeurs est ignorée lors du calcul de la somme. Vérifions en utilisant les matrices de la condition :
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));
Sortie de la console :
115. Déplacer les zéros (défi Leetcode)
Condition : dans un tableau d'entiers, déplacez tous les 0 vers la fin, en conservant l'ordre relatif des éléments non nuls. 1. Avec un tableau : [0,1,0,3,12] La sortie doit être : [1,3,12,0,0] 2. Avec un tableau : [0] La sortie doit être : [0] Faites une pause et écrivez ma décision... Ma décision :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));
Sortie de la console :
116. Noms de liste <String> donnés. Supprimez la première lettre de chaque nom et faites pivoter la liste triée
1. La première chose qui vient à l'esprit, ce sont les méthodes de la classe Collections , qui contiennent de nombreuses méthodes auxiliaires pour les 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. De plus, si nous utilisons Java version 8 et supérieure, nous devons simplement montrer la solution via des flux :
public static List<String> processTheList(List<String> nameList) {
return nameList.stream()
.map(x -> x.substring(1))
.sorted().collect(Collectors.toList());
}
Quelle que soit la solution choisie, le contrôle peut être le suivant :
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));
Sortie de la console :
117. Retournez le tableau
Solution 1 Encore une fois, la première chose qui vient à l'esprit est d'utiliser les méthodes de la classe utilitaire auxiliaire Collections . Mais puisque nous avons un tableau, nous devons d’abord le convertir en une collection (liste) :public static Integer[] reverse(Integer[] arr) {
List<Integer> list = Arrays.asList(arr);
Collections.reverse(list);
return list.toArray(arr);
}
Solution 2 Puisque la question concernait un tableau, je pense qu'il est nécessaire de montrer la solution sans utiliser de fonctionnalités toutes faites, et pour ainsi dire, selon les classiques :
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)));
Sortie de la console :
118. Vérifiez si une chaîne est un palindrome
Solution 1 Il convient de rappeler immédiatement StringBuilder : il est plus flexible et riche en méthodes diverses que le String classique . Nous sommes particulièrement intéressés par la méthode inverse :public static boolean isPalindrome(String string) {
string = string.toLowerCase(); //приводит всю строку к нижнему регистру
StringBuilder builder = new StringBuilder();
builder.append(string);
builder.reverse(); // перевочиваем строку методом Builder-а
return (builder.toString()).equals(string);
}
Solution : La prochaine approche consistera à ne pas utiliser les « échappatoires » prêtes à l'emploi. Nous comparons les caractères du début de la chaîne avec les caractères correspondants du début :
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;
}
Et nous vérifions les deux approches :
boolean isPalindrome = isPalindrome("Tenet");
System.out.println(isPalindrome);
Sortie de la console :
119. Écrivez un algorithme de tri simple (Bulle, Sélection ou Navette). Comment peut-il être amélioré ?
Comme algorithme simple d'implémentation, j'ai choisi le tri par sélection - 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 итог - числа оставшиеся вне текущей итерации отсортированы от самого наименьшего к большему
}
La version améliorée ressemblerait à ceci :
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;
}
Eh bien, il faut maintenant vérifier si le tri s'est vraiment amélioré. Comparons les performances :
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 + "%");
Les deux types ont commencé dans le même cycle, car s'il y avait des boucles séparées, le tri dans le code ci-dessus afficherait un résultat pire que s'il était placé en deuxième position. Cela est dû au fait que le programme « se réchauffe » puis fonctionne un peu plus vite. Mais je sors un peu du sujet. Après cinq exécutions de cette vérification dans la console, j'ai constaté une augmentation des performances de : 36,41006735635892 % 51,46131097160771 % 41,88918834013988 % 48,091980705743566 % 37,120220461591444 % Quant à moi, c'est un plutôt bon résultat.
120. Écrivez un algorithme (séquence d'actions) pour composer un littéral de type int avec un littéral de type byte. Expliquer ce qui arrive à la mémoire
-
la valeur de l'octet est convertie en int. Pas 1 octet de mémoire ne lui sera alloué, mais comme toutes les valeurs int - 4, si cette valeur n'est pas encore sur la pile int. Si tel est le cas, un lien vers celui-ci sera simplement reçu.
-
Deux valeurs int seront ajoutées et la troisième sera obtenue. Une nouvelle section de mémoire lui sera allouée - 4 octets (ou une référence sera reçue de la pile int à la valeur existante).
Dans ce cas, la mémoire de deux ints sera toujours occupée et leurs valeurs seront respectivement stockées sur la pile int.
GO TO FULL VERSION