111. จะแลกเปลี่ยนข้อมูลระหว่างเธรดได้อย่างไร?

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();
}
}
}
}
ในตัวสร้างเธรด เรากำหนด อ็อบเจ็กต์ Exchangerที่ยอมรับอ็อบเจ็กต์ประเภทStringและเมื่อเริ่มต้น (ใน เมธอด 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 แทนที่วิธีการ run จากนั้นสร้างอ็อบเจ็กต์นี้และเริ่มเธรดผ่านเมธอดstart()
-
ใช้งานRunnableในคลาสที่กำหนด ใช้ วิธี การ run()จากนั้นสร้าง วัตถุ Threadโดยกำหนดการใช้งานวัตถุนี้ของ อินเทอร์เฟซ Runnable ให้กับ ตัว สร้าง ในตอนท้ายให้เปิด วัตถุ Threadโดยใช้ เมธอด start ( )
-
เมื่อคุณใช้ อินเทอร์เฟซ Runnableคุณจะไม่เปลี่ยนลักษณะการทำงานของเธรด โดยพื้นฐานแล้วคุณแค่ให้บางสิ่งแก่เธรดเพื่อให้ทำงาน และนี่คือองค์ประกอบของเรา ซึ่งถือเป็นแนวทางที่ดี
-
การใช้Runnableช่วยให้ชั้นเรียนของคุณมีความยืดหยุ่นมากขึ้น หากคุณสืบทอดมาจากThreadการกระทำที่คุณทำจะอยู่บนเธรดเสมอ แต่ถ้าคุณใช้Runnableมันไม่จำเป็นต้องเป็นแค่เธรด ท้ายที่สุดคุณสามารถเรียกใช้ในเธรดหรือส่งต่อไปยังบริการที่ดำเนินการบางอย่างได้ หรือเพียงแค่ส่งมันไปที่ไหนสักแห่งเป็นงานในแอปพลิเคชันแบบเธรดเดียว
-
การใช้Runnableช่วยให้คุณสามารถแยกการดำเนินการงานออกจากตรรกะการควบคุมเธรดได้อย่างมีเหตุผล
-
ใน Java สามารถสืบทอดได้เพียงรายการเดียวเท่านั้น ดังนั้นจึงสามารถขยายได้เพียงคลาสเดียวเท่านั้น ในขณะเดียวกัน จำนวนอินเทอร์เฟซที่ขยายได้นั้นไม่จำกัด (แต่ก็ไม่ไม่จำกัด แต่เป็น65535แต่คุณไม่น่าจะถึงขีดจำกัดนี้เลย)
113. มีเธรด T1, T2 และ T3 จะดำเนินการอย่างไรตามลำดับ?![การวิเคราะห์คำถามและคำตอบจากการสัมภาษณ์นักพัฒนา Java ตอนที่ 13 - 4]()
สิ่งแรกและง่ายที่สุดที่นึกถึงคือการใช้เมธอด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)
เงื่อนไข: คำนวณผลรวมขององค์ประกอบทั้งหมดบนเส้นทแยงมุมหลักและองค์ประกอบทั้งหมดบนเส้นทแยงมุมเพิ่มเติมที่ไม่ได้เป็นส่วนหนึ่งของเส้นทแยงมุมหลัก
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. ย้ายศูนย์ (ท้าทาย 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. รายการที่กำหนด <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. ตรวจสอบว่าสตริงเป็นพาลินโดรมหรือไม่

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 หรือ Shuttle) จะปรับปรุงได้อย่างไร?
เนื่องจากเป็นอัลกอริธึมอย่างง่ายสำหรับการนำไปใช้ ฉันจึงเลือกการเรียงลำดับการเลือก - การเรียงลำดับการเลือก: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 + "%");
ทั้งสองประเภทเริ่มต้นในวงจรเดียวกันเพราะว่า หากมีลูปแยกกัน การเรียงลำดับจากโค้ดด้านบนจะแสดงผลลัพธ์ที่แย่กว่าการเรียงลำดับที่สอง เนื่องจากโปรแกรม "อุ่นเครื่อง" แล้วทำงานเร็วขึ้นเล็กน้อย แต่ฉันจะออกนอกหัวข้อเล็กน้อย หลังจากตรวจสอบคอนโซลนี้ห้าครั้ง ฉันเห็นประสิทธิภาพเพิ่มขึ้นโดย: 36.41006735635892% 51.46131097160771% 41.88918834013988% 48.091980705743566% 37.120220461591444% สำหรับฉัน นี่เป็นสิ่งที่ดีทีเดียว ผลลัพธ์ 
120. เขียนอัลกอริทึม (ลำดับของการกระทำ) สำหรับการเขียนลิเทอรัลประเภท int ด้วยลิเทอรัลประเภทไบต์ อธิบายว่าเกิดอะไรขึ้นกับความทรงจำ
-
ค่าไบต์จะถูกแปลงเป็น int จะไม่มีการจัดสรรหน่วยความจำ 1 ไบต์ แต่เช่นเดียวกับค่า int ทั้งหมด - 4 หากค่านี้ยังไม่ได้อยู่ใน int stack หากมีก็จะได้รับลิงก์ไปยังลิงก์นั้น
-
จะเพิ่มค่า int สองค่าและค่าที่สามจะได้รับ ส่วนหน่วยความจำใหม่จะได้รับการจัดสรร - 4 ไบต์ (หรือจะได้รับการอ้างอิงจาก int stack ไปยังค่าที่มีอยู่)
ในกรณีนี้หน่วยความจำของสอง ints จะยังคงถูกครอบครองและค่าของพวกมันจะถูกเก็บไว้ใน int stack ตามลำดับ

GO TO FULL VERSION