111. Жіптер арасында мәліметтер қалай алмасуға болады?
Ағындар арасында деректер алмасу үшін көптеген әртүрлі тәсілдер мен құралдарды пайдалануға болады: мысалы, атомдық айнымалыларды, синхрондалған жинақтарды және семафорды пайдаланыңыз. Бірақ бұл мәселені шешу үшін мен алмастырғышпен мысал келтіремін . Алмастырушы - ортақ синхрондау нүктесін жасау арқылы ағындар жұбының арасындағы элементтердің алмасуын жеңілдететін қатарлас пакеттен синхрондау класы . Оны пайдалану екі ағын арасындағы деректер алмасуды жеңілдетеді. Оның жұмыс істеу жолы өте қарапайым: ол Exchange() әдісін шақыру үшін екі бөлек ағынды күтеді . Олардың арасында алмасу нүктесі сияқты бірдеңе құрылады: бірінші жіп өз an objectісін қойып, орнына екіншісінің an objectісін алады, ал екіншісі, өз кезегінде, біріншінің an objectісін алып, өздікін қояды. Яғни, бірінші ағын 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 нысанын анықтаймыз және іске қосу кезінде ( іске қосу әдісінде) сол Exchanger ішінде осы әдісті пайдаланатын басқа ағынмен хабарлама алмасу үшін оның exchange() функциясын қолданамыз . Оны негізгі түрде іске қосайық :
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 ішінен мұраға алыңыз , іске қосу әдісін қайта анықтаңыз, содан кейін осы нысанды жасаңыз және ағынды start() әдісі арқылы бастаңыз .
-
Белгілі бір сыныпта Runnable бағдарламасын іске қосыңыз , оның run() әдісін орындаңыз , содан кейін Runnable интерфейсінің осы нысан іске асырылуын оның конструкторына тағайындай отырып, Thread нысанын жасаңыз . Соңында start() әдісі арқылы Thread нысанын іске қосыңыз .
-
Runnable интерфейсін іске асырған кезде ағынның әрекетін өзгертпейсіз. Негізінде сіз жіпке іске қосу үшін бір нәрсе бересіз. Және бұл өз кезегінде жақсы тәсіл деп саналатын біздің композиция.
-
Runnable бағдарламасын енгізу сыныпқа көбірек икемділік береді. Егер сіз Thread ішінен мұра алсаңыз , орындалатын әрекет әрқашан ағында болады. Бірақ Runnable қолданбасын іске асырсаңыз, ол жай ғана ағын болуы керек емес. Өйткені, сіз оны ағында іске қоса аласыз немесе оны қандай да бір орындаушы қызметіне бере аласыз. Немесе оны бір ағынды қолданбадағы тапсырма ретінде бір жерге жіберіңіз.
-
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. Матрицалық диагональды қосынды (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. a матрица - 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. Нөлдерді жылжыту (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 көмекші утorтасының әдістерін қолдану керек . Бірақ бізде массив болғандықтан, алдымен оны жинаққа (тізімге) түрлендіру керек: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. Қарапайым сұрыптау алгоритмін жазыңыз (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 + "%");
Екі түр де бір циклде басталды, өйткені егер бөлек циклдар болса, жоғарыдағы code бойынша сұрыптау екінші орынға қойылғаннан гөрі нашар нәтиже көрсетеді. Бұл бағдарламаның «жылытуына», содан кейін сәл жылдамырақ жұмыс істеуіне байланысты. Бірақ мен тақырыптан сәл ауытқып кеттім. Консольде осы тексеруді бес рет орындағаннан кейін мен өнімділіктің жоғарылауын көрдім: 36,41006735635892% 51,46131097160771% 41,88918834013988% 48,0919807057437014% 48,09198014 , мен үшін бұл. өте жақсы нәтиже.
120. Int типті литералды byte типті литералмен құру алгоритмін (әрекеттер тізбегін) жазыңыз. Жадқа не болатынын түсіндіріңіз
-
byte мәні int түрлендіріледі. Ол үшін 1 byte жад бөлінбейді, бірақ барлық int мәндері сияқты - 4, егер бұл мән әлі int стекінде болмаса. Егер бар болса, оған сілтеме жай ғана алынады.
-
Екі int мәні қосылады, ал үшіншісі алынады. Ол үшін жадтың жаңа бөлімі бөлінеді - 4 byte (немесе int стекінен бар мәнге сілтеме алынады).
Бұл жағдайда екі инт жады әлі де бос болады және олардың мәндері сәйкесінше int стегінде сақталады.
GO TO FULL VERSION