111. 스레드 간에 데이터를 교환하는 방법은 무엇입니까?
스레드 간에 데이터를 교환하려면 원자 변수, 동기화된 컬렉션, 세마포어 등 다양한 접근 방식과 수단을 사용할 수 있습니다. 하지만 이 문제를 해결하기 위해 Exchanger 를 예로 들어 보겠습니다 . Exchanger 는 공통 동기화 지점을 생성하여 스레드 쌍 간의 요소 교환을 용이하게 하는 동시 패키지 의 동기화 클래스입니다 . 이를 사용하면 두 스레드 간의 데이터 교환이 단순화됩니다. 작동 방식은 매우 간단합니다. 두 개의 별도 스레드가 exchange() 메서드를 호출할 때까지 기다립니다 . 그들 사이에 교환 지점과 같은 것이 생성됩니다. 첫 번째 스레드는 자신의 개체를 배치하고 그 대가로 다른 스레드의 개체를 받고, 후자는 차례로 첫 번째 스레드의 개체를 받고 자신의 개체를 넣습니다. 즉, 첫 번째 스레드는 exchange() 메서드를 사용하고 다른 스레드가 동일한 개체에 대해 exchange() 메서드를 호출 하고 두 스레드 간에 데이터가 교환될 때까지 유휴 상태입니다 . 예를 들어 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();
}
}
}
}
스레드 생성자에서 String 유형의 개체를 허용하는 Exchanger 개체를 정의 하고 시작 시( run 메서드에서) exchange() 를 사용하여 동일한 Exchanger 에서 이 메서드를 사용하는 다른 스레드와 메시지를 교환합니다 . main 에서 실행해보자 :
Exchanger<String> exchanger = new Exchanger<>();
CustomThread first = new CustomThread("Первый ", exchanger);
first.setMessage("Сообщение первого потока");
CustomThread second = new CustomThread("Второй", exchanger);
second.setMessage("Сообщение второго потока");
first.start();
second.start();
콘솔에 다음이 표시됩니다.
112. Thread 클래스와 Runnable 인터페이스의 차이점은 무엇입니까?
가장 먼저 주목해야 할 점은 Thread 는 클래스이고 Runnable 은 인터페이스라는 점입니다. 이는 매우 분명한 차이점입니다. =D 또한 Thread는 Runnable (구성)을 사용한다고 말할 것입니다 . 즉, 두 가지 방법이 있습니다.-
Thread 에서 상속 하고 run 메서드를 재정의한 다음 이 개체를 만들고 start() 메서드를 통해 스레드를 시작합니다 .
-
특정 클래스에서 Runnable을 구현하고 run() 메소드를 구현 한 다음 Thread 객체를 생성하여 Runnable 인터페이스 의 이 객체 구현을 해당 생성자에 할당합니다 . 마지막에는 start() 메소드를 사용하여 Thread 객체를 시작합니다 .
-
Runnable 인터페이스를 구현할 때 스레드의 동작은 변경되지 않습니다. 본질적으로 당신은 스레드에 실행할 무언가를 제공하는 것입니다. 그리고 이것이 우리의 구성이며, 이는 좋은 접근 방식으로 간주됩니다.
-
Runnable을 구현하면 클래스에 더 많은 유연성이 제공됩니다. Thread 에서 상속받는 경우 수행하는 작업은 항상 스레드에서 수행됩니다. 그러나 Runnable을 구현한다면 단순히 스레드일 필요는 없습니다. 결국 스레드에서 실행하거나 일부 실행 서비스에 전달할 수 있습니다. 글쎄, 아니면 단일 스레드 응용 프로그램의 작업으로 어딘가에 전달하십시오.
-
Runnable을 사용하면 작업 실행을 스레드 제어 논리와 논리적으로 분리할 수 있습니다.
-
Java에서는 단일 상속만 가능하므로 하나의 클래스만 확장할 수 있습니다. 동시에 확장 가능한 인터페이스의 수는 무제한입니다(무제한은 아니지만 65535 이지만 이 제한에 도달할 가능성은 거의 없습니다).
113. 스레드 T1, T2 및 T3이 있습니다. 순차적으로 구현하는 방법은 무엇입니까?
가장 먼저 떠오르는 가장 간단한 것은 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. 행렬 대각선 합 (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이 아닌 요소의 상대적 순서를 유지하면서 모든 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. 주어진 목록 <String> 이름. 각 이름의 첫 글자를 제거하고 정렬된 목록을 회전합니다.
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 다시 한 번 가장 먼저 생각나는 것은 보조 유틸리티 클래스 Collections 의 메서드를 사용하는 것입니다 . 하지만 배열이 있으므로 먼저 이를 컬렉션(목록)으로 변환해야 합니다.public static Integer[] reverse(Integer[] arr) {
List<Integer> list = Arrays.asList(arr);
Collections.reverse(list);
return list.toArray(arr);
}
솔루션 2 질문은 배열에 관한 것이었기 때문에 고전에 따르면 기성 기능을 사용하지 않고 솔루션을 보여줄 필요가 있다고 생각합니다.
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 에 비해 더 유연하고 다양한 메서드가 풍부합니다 . 우리는 특히 역방향 방법에 관심이 있습니다 .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. 간단한 정렬 알고리즘(버블, 선택 또는 셔틀)을 작성하세요. 어떻게 개선할 수 있나요?
구현을 위한 간단한 알고리즘으로 선택 정렬(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;
}
자, 이제 정렬이 실제로 향상되었는지 확인해야 합니다. 성능을 비교해 보겠습니다.
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 + "%");
두 종류 모두 같은 주기로 시작되었습니다. 별도의 루프가 있는 경우 위 코드에서 정렬하면 두 번째로 배치된 경우보다 더 나쁜 결과가 표시됩니다. 이는 프로그램이 "워밍업"된 다음 조금 더 빠르게 작동하기 때문입니다. 그러나 나는 주제에서 조금 벗어났습니다. 콘솔에서 이 검사를 5번 실행한 후 성능이 다음과 같이 증가한 것을 확인했습니다. 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.091980705743566% 37.120220461591444% 나로서는 이것이 꽤 좋습니다. 결과.
120. int 유형 리터럴을 byte 유형 리터럴로 구성하기 위한 알고리즘(작업 순서)을 작성하세요. 기억에 무슨 일이 일어나는지 설명해보세요
-
바이트 값은 int로 변환됩니다. 1바이트의 메모리가 할당되지 않지만 모든 int 값과 마찬가지로 이 값이 아직 int 스택에 없으면 4입니다. 있는 경우 해당 링크가 수신됩니다.
-
두 개의 int 값이 추가되고 세 번째 값이 얻어집니다. 이를 위해 새로운 메모리 섹션(4바이트)이 할당됩니다(또는 int 스택에서 기존 값에 대한 참조가 수신됩니다).
이 경우 두 int의 메모리는 여전히 점유되며 해당 값은 각각 int 스택에 저장됩니다.
GO TO FULL VERSION