1.实现单例模式 (Singleton Pattern) 懒汉式: 使用双重检查锁定 (DCL) 实现线程安全的懒汉式单例模式。这种模式在第一次调用时才创建实例,并且通过两次检查和同步块确保了线程安全和性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Singleton { private static volatile Singleton instance; private Singleton () {} public static Singleton getInstance () { if (instance == null ) { synchronized (Singleton.class) { if (instance == null ) { instance = new Singleton (); } } } return instance; } public void showMessage () { System.out.println("这是一个线程安全的懒汉式单例模式实例。" ); } }
饿汉式: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package test1;class SingletonEager { private static final SingletonEager INSTANCE = new SingletonEager (); private SingletonEager () {} public static SingletonEager getInstance () { return INSTANCE; } public void showMessage () { System.out.println("这是一个线程安全的饿汉式单例模式实例。" ); } }
2.继承与多态 (Inheritance and Polymorphism) 设计一个 Animal
抽象类,并创建 Dog
和 Cat
类来展示继承和多态。=
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 abstract class Animal { public abstract void eat () ; } class Dog extends Animal { @Override public void eat () { System.out.println("狗正在吃骨头。" ); } } class Cat extends Animal { @Override public void eat () { System.out.println("猫正在吃鱼。" ); } } class PolymorphismDemo { public static void showPolymorphism () { Animal myDog = new Dog (); Animal myCat = new Cat (); myDog.eat(); myCat.eat(); } }
equals()
和 hashCode()
编写 Student
类,并重写 equals()
和 hashCode()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import java.util.Objects;class Student { private int id; private String name; public Student (int id, String name) { this .id = id; this .name = name; } @Override public boolean equals (Object o) { if (this == o) return true ; if (o == null || getClass() != o.getClass()) return false ; Student student = (Student) o; return id == student.id && Objects.equals(name, student.name); } @Override public int hashCode () { return Objects.hash(id, name); } }
接口与实现 (Interfaces and Implementation) 设计 Drawable
接口,并由 Circle
和 Rectangle
实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface Drawable { void draw () ; } class Circle implements Drawable { @Override public void draw () { System.out.println("正在画一个圆形。" ); } } class Rectangle implements Drawable { @Override public void draw () { System.out.println("正在画一个矩形。" ); } }
异常处理 (Exception Handling) 使用 try-catch-finally
结构处理 FileNotFoundException
并确保资源关闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;class ExceptionDemo { public static void processFile (String filePath) { FileInputStream fileInputStream = null ; try { fileInputStream = new FileInputStream (filePath); System.out.println("文件已成功打开。" ); } catch (FileNotFoundException e) { System.err.println("错误:指定的文件不存在!路径:" + filePath); e.printStackTrace(); } finally { if (fileInputStream != null ) { try { fileInputStream.close(); System.out.println("文件流已在 finally 块中关闭。" ); } catch (IOException e) { System.err.println("关闭文件流时发生异常:" + e.getMessage()); } } } } }
try-with-resources
使用 try-with-resources
重写上一个题目,展示其简化优势。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;class TryWithResourcesDemo { public static void processFileWithResources (String filePath) { try (FileInputStream fileInputStream = new FileInputStream (filePath)) { System.out.println("文件已成功打开。" ); } catch (FileNotFoundException e) { System.err.println("错误:指定的文件不存在!路径:" + filePath); e.printStackTrace(); } catch (IOException e) { System.err.println("关闭文件流时发生异常:" + e.getMessage()); } System.out.println("文件流已在 try-with-resources 语句中自动关闭。" ); } }
冒泡排序 (Bubble Sort) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class SortingAlgorithms { public static void bubbleSort (int [] arr) { if (arr == null || arr.length <= 1 ) { return ; } int n = arr.length; for (int i = 0 ; i < n - 1 ; i++) { boolean swapped = false ; for (int j = 0 ; j < n - 1 - i; j++) { if (arr[j] > arr[j + 1 ]) { int temp = arr[j]; arr[j] = arr[j + 1 ]; arr[j + 1 ] = temp; swapped = true ; } } if (!swapped) { break ; } } } }
快速排序 (Quick Sort) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class SortingAlgorithms { public static void quickSort (int [] arr) { if (arr == null || arr.length <= 1 ) { return ; } quickSort(arr, 0 , arr.length - 1 ); } private static void quickSort (int [] arr, int low, int high) { if (low < high) { int pivotIndex = partition(arr, low, high); quickSort(arr, low, pivotIndex - 1 ); quickSort(arr, pivotIndex + 1 , high); } } private static int partition (int [] arr, int low, int high) { int pivot = arr[high]; int i = (low - 1 ); for (int j = low; j < high; j++) { if (arr[j] <= pivot) { i++; int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } int temp = arr[i + 1 ]; arr[i + 1 ] = arr[high]; arr[high] = temp; return i + 1 ; } }
二分查找 (Binary Search) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class SearchAlgorithms { public static int binarySearch (int [] arr, int target) { if (arr == null || arr.length == 0 ) { return -1 ; } int left = 0 ; int right = arr.length - 1 ; while (left <= right) { int mid = left + (right - left) / 2 ; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1 ; } else { right = mid - 1 ; } } return -1 ; } }
链表反转 (Reverse Linked List) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class LinkedListProblems { public static class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public static ListNode reverseList (ListNode head) { ListNode prev = null ; ListNode current = head; while (current != null ) { ListNode nextTemp = current.next; current.next = prev; prev = current; current = nextTemp; } return prev; } }
链表中间节点 (Middle of Linked List) 使用快慢指针 ,快指针每次走两步,慢指针每次走一步。当快指针到达链表末尾时,慢指针正好在中间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class LinkedListProblems { public static class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public static ListNode findMiddleNode (ListNode head) { if (head == null ) { return null ; } ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null ) { slow = slow.next; fast = fast.next.next; } return slow; } }
移除重复元素 (Remove Duplicates) 不使用 Set
,通过双层循环或排序后遍历实现。这里使用排序后遍历的方法,因为它更高效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.ArrayList;import java.util.Collections;public class ListProblems { public static void removeDuplicates (ArrayList<String> list) { if (list == null || list.size() <= 1 ) { return ; } Collections.sort(list); for (int i = list.size() - 2 ; i >= 0 ; i--) { if (list.get(i).equals(list.get(i + 1 ))) { list.remove(i + 1 ); } } } }
字符串反转 (Reverse String) 不使用 StringBuilder
或 StringBuffer
的 reverse()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class StringProblems { public static String reverseString (String str) { if (str == null || str.isEmpty()) { return str; } char [] charArray = str.toCharArray(); int left = 0 ; int right = charArray.length - 1 ; while (left < right) { char temp = charArray[left]; charArray[left] = charArray[right]; charArray[right] = temp; left++; right--; } return new String (charArray); } }
回文字符串 (Palindrome String) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class StringProblems { public static boolean isPalindrome (String str) { if (str == null ) { return false ; } String cleanedStr = str.toLowerCase().replaceAll("[^a-z0-9]" , "" ); int left = 0 ; int right = cleanedStr.length() - 1 ; while (left < right) { if (cleanedStr.charAt(left) != cleanedStr.charAt(right)) { return false ; } left++; right--; } return true ; } }
计算阶乘 (Factorial Calculation) 递归方式 1 2 3 4 5 6 7 8 9 10 11 public class MathProblems { public static long factorialRecursive (int n) { if (n < 0 ) { throw new IllegalArgumentException ("Factorial is not defined for negative numbers." ); } if (n == 0 || n == 1 ) { return 1 ; } return n * factorialRecursive(n - 1 ); } }
非递归方式 1 2 3 4 5 6 7 8 9 10 11 12 public class MathProblems { public static long factorialIterative(int n) { if (n < 0) { throw new IllegalArgumentException("Factorial is not defined for negative numbers."); } long result = 1; for (int i = 2; i <= n; i++) { result *= i; } return result; } }
斐波那契数列 (Fibonacci Sequence) 递归方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MathProblems { public static long fibonacciRecursive (int n) { if (n < 0 ) { throw new IllegalArgumentException ("Invalid input for Fibonacci sequence." ); } if (n == 0 ) { return 0 ; } if (n == 1 ) { return 1 ; } return fibonacciRecursive(n - 1 ) + fibonacciRecursive(n - 2 ); } }
非递归方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MathProblems { public static long fibonacciIterative (int n) { if (n < 0 ) { throw new IllegalArgumentException ("Invalid input for Fibonacci sequence." ); } if (n == 0 ) { return 0 ; } if (n == 1 ) { return 1 ; } long a = 0 ; long b = 1 ; for (int i = 2 ; i <= n; i++) { long sum = a + b; a = b; b = sum; } return b; } }
字符串中字符计数 (Character Count) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import java.util.HashMap;import java.util.Map;public class StringProblems { public static Map<Character, Integer> countCharacters (String str) { Map<Character, Integer> charCountMap = new HashMap <>(); if (str == null || str.isEmpty()) { return charCountMap; } for (char c : str.toCharArray()) { charCountMap.put(c, charCountMap.getOrDefault(c, 0 ) + 1 ); } return charCountMap; } }
两数之和 (Two Sum) 使用 HashMap
将值和索引存储起来,实现一次遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import java.util.HashMap;import java.util.Map;public class ArrayProblems { public static int [] twoSum(int [] nums, int target) { Map<Integer, Integer> numMap = new HashMap <>(); for (int i = 0 ; i < nums.length; i++) { int complement = target - nums[i]; if (numMap.containsKey(complement)) { return new int []{numMap.get(complement), i}; } numMap.put(nums[i], i); } return new int []{}; } }
数组中最大/小值 (Max/Min in Array) 一次遍历找到最大值和最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class ArrayProblems { public static int [] findMaxMin(int [] arr) { if (arr == null || arr.length == 0 ) { throw new IllegalArgumentException ("Array cannot be null or empty." ); } int min = arr[0 ]; int max = arr[0 ]; for (int i = 1 ; i < arr.length; i++) { if (arr[i] < min) { min = arr[i]; } if (arr[i] > max) { max = arr[i]; } } return new int []{max, min}; } }
质数判断 (Prime Number) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MathProblems { public static boolean isPrime (int n) { if (n <= 1 ) { return false ; } for (int i = 2 ; i * i <= n; i++) { if (n % i == 0 ) { return false ; } } return true ; } }
集合交集 (List Intersection) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.ArrayList;import java.util.List;public class ListProblems { public static <T> List<T> intersection (List<T> list1, List<T> list2) { List<T> result = new ArrayList <>(); if (list1 == null || list2 == null ) { return result; } for (T element : list1) { if (list2.contains(element) && !result.contains(element)) { result.add(element); } } return result; } }
数组合并 (Merge Sorted Arrays) 将两个已排序的数组合并为一个新的已排序数组,使用双指针 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class ArrayProblems { public static int [] mergeSortedArrays(int [] arr1, int [] arr2) { int [] merged = new int [arr1.length + arr2.length]; int i = 0 , j = 0 , k = 0 ; while (i < arr1.length && j < arr2.length) { if (arr1[i] < arr2[j]) { merged[k++] = arr1[i++]; } else { merged[k++] = arr2[j++]; } } while (i < arr1.length) { merged[k++] = arr1[i++]; } while (j < arr2.length) { merged[k++] = arr2[j++]; } return merged; } }
判断回文数 (Palindrome Number) 不使用 String
转换,通过数学方法反转一半数字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MathProblems { public static boolean isPalindromeNumber (int x) { if (x < 0 || (x % 10 == 0 && x != 0 )) { return false ; } int reversedNumber = 0 ; while (x > reversedNumber) { int lastDigit = x % 10 ; reversedNumber = reversedNumber * 10 + lastDigit; x /= 10 ; } return x == reversedNumber || x == reversedNumber / 10 ; } }
罗马数字转整数 (Roman to Integer) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.HashMap;import java.util.Map;public class StringProblems { public static int romanToInt (String s) { Map<Character, Integer> romanMap = new HashMap <>(); romanMap.put('I' , 1 ); romanMap.put('V' , 5 ); romanMap.put('X' , 10 ); romanMap.put('L' , 50 ); romanMap.put('C' , 100 ); romanMap.put('D' , 500 ); romanMap.put('M' , 1000 ); int result = 0 ; for (int i = 0 ; i < s.length(); i++) { int currentVal = romanMap.get(s.charAt(i)); if (i < s.length() - 1 && currentVal < romanMap.get(s.charAt(i + 1 ))) { result -= currentVal; } else { result += currentVal; } } return result; } }
爬楼梯 (Climbing Stairs) 这是经典的动态规划 问题。
动态规划方式 (非递归) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MathProblems { public static int climbStairs (int n) { if (n <= 2 ) { return n; } int [] dp = new int [n + 1 ]; dp[1 ] = 1 ; dp[2 ] = 2 ; for (int i = 3 ; i <= n; i++) { dp[i] = dp[i - 1 ] + dp[i - 2 ]; } return dp[n]; } }
好的,以下是基于 语言的线程基础与同步的实现,并附带详细注释。
线程创建 1. 继承 Thread
类 通过继承 java.lang.Thread
类并重写其 run()
方法来创建线程。
1 2 3 4 5 6 7 8 class MyThread extends Thread { @Override public void run () { System.out.println("使用继承 Thread 类的方式创建的线程正在运行。" ); } }
2. 实现 Runnable
接口 通过实现 java.lang.Runnable
接口并将其作为参数传递给 Thread
类的构造函数来创建线程。这种方式更灵活,推荐使用。
1 2 3 4 5 6 7 8 class MyRunnable implements Runnable { @Override public void run () { System.out.println("使用实现 Runnable 接口的方式创建的线程正在运行。" ); } }
sleep()
与 yield()
sleep()
和 yield()
都是线程调度的方法,但它们的作用和效果不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class SleepYieldDemo { public static void main (String[] args) { new Thread (() -> { System.out.println("线程 A: 开始执行..." ); try { System.out.println("线程 A: 准备睡眠 2 秒..." ); Thread.sleep(2000 ); System.out.println("线程 A: 睡眠结束,继续执行。" ); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread (() -> { System.out.println("线程 B: 开始执行..." ); for (int i = 0 ; i < 5 ; i++) { System.out.println("线程 B: 正在执行 " + i); Thread.yield (); } System.out.println("线程 B: 执行完毕。" ); }).start(); } }
sleep() : 使当前线程暂停执行指定的时间 ,进入 TIMED_WAITING 状态。它会释放 CPU 资源 ,但不释放锁 。
yield() : 提示线程调度器,当前线程愿意让出当前 CPU 时间片 。线程会从 RUNNING 状态转换为 RUNNABLE 状态,与其他线程竞争 CPU,但不保证 其他线程能立即获得执行。它主要用于优化线程调度,通常在多线程程序中不应依赖 其行为来保证正确性。
join()
方法join()
方法允许一个线程等待另一个线程执行完毕。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class JoinDemo { public static void main (String[] args) throws InterruptedException { Thread workerThread = new Thread (() -> { try { System.out.println("子线程: 正在执行..." ); Thread.sleep(3000 ); System.out.println("子线程: 执行完毕。" ); } catch (InterruptedException e) { e.printStackTrace(); } }); workerThread.start(); System.out.println("主线程: 等待子线程执行完毕..." ); workerThread.join(); System.out.println("主线程: 子线程已执行完毕,主线程继续执行。" ); } }
非线程安全计数器 在多线程环境下,多个线程同时对共享资源进行读写操作,可能导致数据不一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class UnsafeCounter { private int count = 0 ; public void increment () { count++; } public int getCount () { return count; } } class ThreadSafetyDemo { public static void main (String[] args) throws InterruptedException { UnsafeCounter counter = new UnsafeCounter (); Thread[] threads = new Thread [100 ]; for (int i = 0 ; i < threads.length; i++) { threads[i] = new Thread (() -> { for (int j = 0 ; j < 10000 ; j++) { counter.increment(); } }); threads[i].start(); } for (Thread thread : threads) { thread.join(); } System.out.println("非线程安全计数器的最终结果: " + counter.getCount()); } }
synchronized
关键字使用 synchronized
关键字可以保证同一时刻只有一个线程访问共享资源,从而解决线程安全问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class SynchronizedCounter { private int count = 0 ; public synchronized void increment () { count++; } public int getCount () { return count; } } class SynchronizedDemo { public static void main (String[] args) throws InterruptedException { SynchronizedCounter counter = new SynchronizedCounter (); Thread[] threads = new Thread [100 ]; for (int i = 0 ; i < threads.length; i++) { threads[i] = new Thread (() -> { for (int j = 0 ; j < 10000 ; j++) { counter.increment(); } }); threads[i].start(); } for (Thread thread : threads) { thread.join(); } System.out.println("synchronized 计数器的最终结果: " + counter.getCount()); } }
synchronized
块与方法
synchronized 方法 : 锁定的是当前对象实例 (this
)。
synchronized 块 : 提供了更细粒度的控制,可以指定锁定的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class SynchronizedBlockDemo { private final Object lock1 = new Object (); private final Object lock2 = new Object (); public synchronized void synchronizedMethod () { System.out.println(Thread.currentThread().getName() + " 进入 synchronized 方法。" ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 离开 synchronized 方法。" ); } public void synchronizedBlock1 () { System.out.println(Thread.currentThread().getName() + " 尝试获取 lock1..." ); synchronized (lock1) { System.out.println(Thread.currentThread().getName() + " 已获取 lock1。" ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 释放 lock1。" ); } } public void synchronizedBlock2 () { System.out.println(Thread.currentThread().getName() + " 尝试获取 lock2..." ); synchronized (lock2) { System.out.println(Thread.currentThread().getName() + " 已获取 lock2。" ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 释放 lock2。" ); } } }
volatile
关键字volatile
关键字保证了变量在多线程间的可见性 ,但不保证原子性 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class VolatileDemo { private static volatile boolean ready = false ; public static void main (String[] args) throws InterruptedException { new Thread (() -> { System.out.println("线程 A: 正在等待标志位变为 true。" ); while (!ready) { } System.out.println("线程 A: 标志位已变为 true,循环结束。" ); }).start(); Thread.sleep(1000 ); System.out.println("主线程: 正在将标志位设置为 true。" ); ready = true ; } }
AtomicInteger
AtomicInteger
是一个原子类,它使用 CAS (Compare-and-Swap) 机制来保证操作的原子性,从而实现线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.concurrent.atomic.AtomicInteger;class AtomicCounter { private AtomicInteger count = new AtomicInteger (0 ); public void increment () { count.incrementAndGet(); } public int getCount () { return count.get(); } } class AtomicDemo { public static void main (String[] args) throws InterruptedException { AtomicCounter counter = new AtomicCounter (); Thread[] threads = new Thread [100 ]; for (int i = 0 ; i < threads.length; i++) { threads[i] = new Thread (() -> { for (int j = 0 ; j < 10000 ; j++) { counter.increment(); } }); threads[i].start(); } for (Thread thread : threads) { thread.join(); } System.out.println("AtomicInteger 计数器的最终结果: " + counter.getCount()); } }
线程通信 使用 wait()
和 notifyAll()
实现两个线程交替打印奇数和偶数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class PrintNumbers { private int count = 0 ; private final Object lock = new Object (); public void printOdd () { synchronized (lock) { while (count < 10 ) { while (count % 2 == 0 ) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ": " + count++); lock.notifyAll(); } } } public void printEven () { synchronized (lock) { while (count < 10 ) { while (count % 2 != 0 ) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ": " + count++); lock.notifyAll(); } } } public static void main (String[] args) { PrintNumbers pn = new PrintNumbers (); Thread oddThread = new Thread (pn::printOdd, "奇数线程" ); Thread evenThread = new Thread (pn::printEven, "偶数线程" ); oddThread.start(); evenThread.start(); } }
好的,以下是 中高级并发编程的实现,并附带详细注释。
生产者-消费者模式 (使用 wait()
和 notifyAll()
) 这是一个经典的线程协作问题。生产者生产数据,放入共享队列;消费者从队列中取出数据进行消费。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import java.util.LinkedList;import java.util.Queue;class ProducerConsumerClassic { private final Queue<Integer> queue = new LinkedList <>(); private final int MAX_SIZE = 5 ; private final Object LOCK = new Object (); public void produce () throws InterruptedException { int i = 0 ; while (true ) { synchronized (LOCK) { while (queue.size() == MAX_SIZE) { System.out.println("队列已满,生产者等待..." ); LOCK.wait(); } queue.offer(i); System.out.println("生产者生产了: " + i); i++; LOCK.notifyAll(); } Thread.sleep(100 ); } } public void consume () throws InterruptedException { while (true ) { synchronized (LOCK) { while (queue.isEmpty()) { System.out.println("队列为空,消费者等待..." ); LOCK.wait(); } int data = queue.poll(); System.out.println("消费者消费了: " + data); LOCK.notifyAll(); } Thread.sleep(500 ); } } }
生产者-消费者模式 (使用 BlockingQueue
) java.util.concurrent.BlockingQueue
接口提供了线程安全的队列操作,简化了生产者-消费者模型的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;class ProducerConsumerBlockingQueue { private final BlockingQueue<Integer> queue = new LinkedBlockingQueue <>(5 ); public void produce () throws InterruptedException { int i = 0 ; while (true ) { queue.put(i); System.out.println("生产者生产了: " + i); i++; Thread.sleep(100 ); } } public void consume () throws InterruptedException { while (true ) { int data = queue.take(); System.out.println("消费者消费了: " + data); Thread.sleep(500 ); } } }
ReentrantLock
使用 ReentrantLock
实现线程安全计数器,并解释其与 synchronized
的区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.concurrent.locks.ReentrantLock;class ReentrantLockCounter { private int count = 0 ; private final ReentrantLock lock = new ReentrantLock (); public void increment () { lock.lock(); try { count++; } finally { lock.unlock(); } } public int getCount () { lock.lock(); try { return count; } finally { lock.unlock(); } } }
ReentrantLock
与 Condition
使用 ReentrantLock
和 Condition
重新实现生产者-消费者模型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import java.util.LinkedList;import java.util.Queue;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;class ProducerConsumerCondition { private final Queue<Integer> queue = new LinkedList <>(); private final int MAX_SIZE = 5 ; private final ReentrantLock lock = new ReentrantLock (); private final Condition producerCondition = lock.newCondition(); private final Condition consumerCondition = lock.newCondition(); public void produce () throws InterruptedException { int i = 0 ; while (true ) { lock.lock(); try { while (queue.size() == MAX_SIZE) { System.out.println("队列已满,生产者等待..." ); producerCondition.await(); } queue.offer(i); System.out.println("生产者生产了: " + i); i++; consumerCondition.signalAll(); } finally { lock.unlock(); } Thread.sleep(100 ); } } public void consume () throws InterruptedException { while (true ) { lock.lock(); try { while (queue.isEmpty()) { System.out.println("队列为空,消费者等待..." ); consumerCondition.await(); } int data = queue.poll(); System.out.println("消费者消费了: " + data); producerCondition.signalAll(); } finally { lock.unlock(); } Thread.sleep(500 ); } } }
ExecutorService
创建一个固定大小的线程池,并向其提交多个任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class ExecutorServiceDemo { public static void main (String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3 ); for (int i = 0 ; i < 10 ; i++) { final int taskId = i; executorService.execute(() -> { System.out.println("任务 " + taskId + " 正在由线程 " + Thread.currentThread().getName() + " 执行。" ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } executorService.shutdown(); } }
Callable
与 Future
使用 Callable
提交任务,并使用 Future
获取返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.util.concurrent.*;class CallableFutureDemo { public static void main (String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); Callable<String> task = () -> { System.out.println("任务开始执行..." ); Thread.sleep(2000 ); return "任务执行完毕,返回结果!" ; }; Future<String> future = executorService.submit(task); System.out.println("主线程: 任务已提交,继续执行其他操作..." ); String result = future.get(); System.out.println("主线程: 获得任务结果 -> " + result); executorService.shutdown(); } }
CountDownLatch
CountDownLatch
允许一个或多个线程等待直到在其他线程中执行的一组操作完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class CountDownLatchDemo { public static void main (String[] args) throws InterruptedException { int workerCount = 5 ; CountDownLatch latch = new CountDownLatch (workerCount); ExecutorService executor = Executors.newFixedThreadPool(workerCount); for (int i = 0 ; i < workerCount; i++) { final int workerId = i; executor.execute(() -> { System.out.println("工作线程 " + workerId + " 开始执行任务..." ); try { Thread.sleep((long ) (Math.random() * 2000 )); System.out.println("工作线程 " + workerId + " 任务完成。" ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { latch.countDown(); } }); } System.out.println("主线程: 等待所有工作线程完成..." ); latch.await(); System.out.println("主线程: 所有工作线程已完成,继续执行下一步。" ); executor.shutdown(); } }
CyclicBarrier
CyclicBarrier
允许一组线程相互等待,直到所有线程都到达一个共同的屏障点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class CyclicBarrierDemo { public static void main (String[] args) { int partySize = 3 ; CyclicBarrier barrier = new CyclicBarrier (partySize, () -> { System.out.println("\n所有线程已到达屏障点!继续执行下一阶段。\n" ); }); ExecutorService executor = Executors.newFixedThreadPool(partySize); for (int i = 0 ; i < partySize; i++) { final int threadId = i; executor.execute(() -> { try { System.out.println("线程 " + threadId + " 正在执行第一阶段任务..." ); Thread.sleep((long ) (Math.random() * 1000 )); System.out.println("线程 " + threadId + " 第一阶段任务完成,到达屏障。" ); barrier.await(); System.out.println("线程 " + threadId + " 正在执行第二阶段任务..." ); } catch (InterruptedException | BrokenBarrierException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown(); } }
Semaphore
Semaphore
(信号量)用来控制对资源的并发访问数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.util.concurrent.Semaphore;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class SemaphoreDemo { private final Semaphore semaphore = new Semaphore (3 ); public void accessResource (int threadId) { try { semaphore.acquire(); System.out.println("线程 " + threadId + " 正在访问资源..." ); Thread.sleep(2000 ); System.out.println("线程 " + threadId + " 访问资源完毕。" ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); } } public static void main (String[] args) { SemaphoreDemo demo = new SemaphoreDemo (); ExecutorService executor = Executors.newFixedThreadPool(10 ); for (int i = 0 ; i < 10 ; i++) { final int threadId = i; executor.execute(() -> demo.accessResource(threadId)); } executor.shutdown(); } }
线程死锁 1. 死锁演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class DeadlockDemo { private static final Object lock1 = new Object (); private static final Object lock2 = new Object (); public void threadA () { synchronized (lock1) { System.out.println("线程A: 已获得 lock1,尝试获取 lock2..." ); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("线程A: 已获得 lock2。" ); } } } public void threadB () { synchronized (lock2) { System.out.println("线程B: 已获得 lock2,尝试获取 lock1..." ); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("线程B: 已获得 lock1。" ); } } } public static void main (String[] args) { DeadlockDemo demo = new DeadlockDemo (); new Thread (demo::threadA, "Thread-A" ).start(); new Thread (demo::threadB, "Thread-B" ).start(); } }
死锁预防 通过打破死锁的四个必要条件之一来预防死锁。这里通过资源有序分配 来打破循环等待 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class DeadlockPrevention { private static final Object lock1 = new Object (); private static final Object lock2 = new Object (); public void threadA () { synchronized (lock1) { System.out.println("线程A: 已获得 lock1,尝试获取 lock2..." ); synchronized (lock2) { System.out.println("线程A: 已获得 lock2。" ); } } } public void threadB () { synchronized (lock1) { System.out.println("线程B: 已获得 lock1,尝试获取 lock2..." ); synchronized (lock2) { System.out.println("线程B: 已获得 lock2。" ); } } } public static void main (String[] args) { DeadlockPrevention demo = new DeadlockPrevention (); new Thread (demo::threadA, "Thread-A" ).start(); new Thread (demo::threadB, "Thread-B" ).start(); } }
ThreadLocal
ThreadLocal
为每个线程提供了一个独立的变量副本,实现了线程间的数据隔离。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class ThreadLocalDemo { private static final ThreadLocal<String> threadLocal = new ThreadLocal <>(); public void setAndPrint (String value) { threadLocal.set(value); try { Thread.sleep(1000 ); System.out.println(Thread.currentThread().getName() + " 的变量值: " + threadLocal.get()); } catch (InterruptedException e) { e.printStackTrace(); } finally { threadLocal.remove(); } } public static void main (String[] args) { ThreadLocalDemo demo = new ThreadLocalDemo (); new Thread (() -> demo.setAndPrint("线程 A 的数据" ), "Thread-A" ).start(); new Thread (() -> demo.setAndPrint("线程 B 的数据" ), "Thread-B" ).start(); } }
线程安全单例 (静态内部类) 静态内部类方式是实现线程安全的懒汉式单例的最佳实践之一。它利用了 JVM 类加载机制的线程安全特性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class SingletonThreadSafe { private SingletonThreadSafe () {} private static class SingletonHolder { private static final SingletonThreadSafe INSTANCE = new SingletonThreadSafe (); } public static SingletonThreadSafe getInstance () { return SingletonHolder.INSTANCE; } }
ReadWriteLock
ReadWriteLock
适用于读多写少的场景,它允许多个线程同时进行读操作,但写操作必须是互斥的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;class ReadWriteLockDemo { private final ReadWriteLock rwLock = new ReentrantReadWriteLock (); private final Object sharedData = "初始数据" ; public String readData () { rwLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 正在读取数据..." ); Thread.sleep(1000 ); return (String) sharedData; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null ; } finally { rwLock.readLock().unlock(); } } public void writeData (String newData) { rwLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 正在写入数据..." ); Thread.sleep(2000 ); System.out.println(Thread.currentThread().getName() + " 写入完成。" ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { rwLock.writeLock().unlock(); } } }
线程池关闭 ExecutorService
的两种关闭方式:shutdown()
和 shutdownNow()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class ShutdownDemo { public static void main (String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2 ); for (int i = 0 ; i < 5 ; i++) { final int taskId = i; executor.execute(() -> { try { System.out.println("任务 " + taskId + " 正在执行..." ); Thread.sleep(2000 ); } catch (InterruptedException e) { System.out.println("任务 " + taskId + " 被中断。" ); } }); } executor.shutdownNow(); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程池已关闭。" ); } }
中断机制 一个线程通过响应 interrupt()
调用来正确停止自身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class InterruptedThreadDemo { public static void main (String[] args) { Thread worker = new Thread (() -> { while (!Thread.currentThread().isInterrupted()) { try { System.out.println("线程正在执行..." ); Thread.sleep(1000 ); } catch (InterruptedException e) { System.out.println("线程被中断!" ); Thread.currentThread().interrupt(); return ; } } System.out.println("线程已退出。" ); }); worker.start(); try { Thread.sleep(3500 ); } catch (InterruptedException e) { e.printStackTrace(); } worker.interrupt(); } }
子数组求和 (Subarray Sum) 使用滑动窗口 或前缀和 + 哈希表 两种方式解决。这里演示前缀和 + 哈希表,它能处理负数情况且时间复杂度更优。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import java.util.HashMap;import java.util.Map;class ArrayProblems { public static int [] findSubarraySum(int [] nums, int target) { if (nums == null || nums.length == 0 ) { return null ; } Map<Integer, Integer> prefixSumMap = new HashMap <>(); prefixSumMap.put(0 , -1 ); int currentSum = 0 ; for (int i = 0 ; i < nums.length; i++) { currentSum += nums[i]; if (prefixSumMap.containsKey(currentSum - target)) { int start = prefixSumMap.get(currentSum - target) + 1 ; int end = i; return new int []{start, end}; } prefixSumMap.put(currentSum, i); } return null ; } }
字符串去重 (Remove Duplicates from String) 使用 LinkedHashSet
保持相对顺序或手动遍历。这里手动遍历实现,避免使用额外数据结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class StringProblems { public static String removeDuplicates (String str) { if (str == null || str.length() <= 1 ) { return str; } StringBuilder sb = new StringBuilder (); boolean [] charSet = new boolean [256 ]; for (int i = 0 ; i < str.length(); i++) { char c = str.charAt(i); if (!charSet[c]) { sb.append(c); charSet[c] = true ; } } return sb.toString(); } }
最长不重复子串 (Longest Substring Without Repeating Characters) 使用滑动窗口 加哈希表 或数组 来高效解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.HashMap;import java.util.Map;class StringProblems { public static int lengthOfLongestSubstring (String s) { if (s == null || s.length() == 0 ) { return 0 ; } Map<Character, Integer> charIndexMap = new HashMap <>(); int maxLength = 0 ; int left = 0 ; for (int right = 0 ; right < s.length(); right++) { char currentChar = s.charAt(right); if (charIndexMap.containsKey(currentChar)) { left = Math.max(left, charIndexMap.get(currentChar) + 1 ); } charIndexMap.put(currentChar, right); maxLength = Math.max(maxLength, right - left + 1 ); } return maxLength; } }
字符串转整数 (String to Integer - atoi) 实现 atoi
函数,需要考虑各种边界情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class StringProblems { public static int myAtoi (String s) { if (s == null || s.isEmpty()) { return 0 ; } s = s.trim(); if (s.isEmpty()) { return 0 ; } int index = 0 ; int sign = 1 ; if (s.charAt(index) == '-' ) { sign = -1 ; index++; } else if (s.charAt(index) == '+' ) { index++; } long result = 0 ; while (index < s.length()) { char c = s.charAt(index); if (!Character.isDigit(c)) { break ; } int digit = c - '0' ; result = result * 10 + digit; if (sign == 1 && result > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } if (sign == -1 && -result < Integer.MIN_VALUE) { return Integer.MIN_VALUE; } index++; } return (int ) (result * sign); } }
判断子串 (Substring Check) 使用 String.indexOf()
是最直接的方式。如果不能使用内置方法,可以通过双指针或 KMP 算法实现。这里提供双指针的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class StringProblems { public static boolean isSubstring (String mainStr, String subStr) { if (mainStr == null || subStr == null || mainStr.length() < subStr.length()) { return false ; } if (subStr.isEmpty()) { return true ; } for (int i = 0 ; i <= mainStr.length() - subStr.length(); i++) { boolean match = true ; for (int j = 0 ; j < subStr.length(); j++) { if (mainStr.charAt(i + j) != subStr.charAt(j)) { match = false ; break ; } } if (match) { return true ; } } return false ; } }
数组旋转 (Array Rotation) 向右旋转 k
步,可以通过三次反转或使用额外数组实现。这里使用空间复杂度为 O(1)
的三次反转方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class ArrayProblems { public static void rotate (int [] nums, int k) { if (nums == null || nums.length <= 1 ) { return ; } k %= nums.length; reverse(nums, 0 , nums.length - 1 ); reverse(nums, 0 , k - 1 ); reverse(nums, k, nums.length - 1 ); } private static void reverse (int [] nums, int start, int end) { while (start < end) { int temp = nums[start]; nums[start] = nums[end]; nums[end] = temp; start++; end--; } } }
交错字符串 (Interleaving String) 判断 s3
是否由 s1
和 s2
交错而成,可以使用动态规划 解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class StringProblems { public static boolean isInterleave (String s1, String s2, String s3) { if (s1.length() + s2.length() != s3.length()) { return false ; } boolean [][] dp = new boolean [s1.length() + 1 ][s2.length() + 1 ]; dp[0 ][0 ] = true ; for (int j = 1 ; j <= s2.length(); j++) { dp[0 ][j] = dp[0 ][j - 1 ] && s2.charAt(j - 1 ) == s3.charAt(j - 1 ); } for (int i = 1 ; i <= s1.length(); i++) { dp[i][0 ] = dp[i - 1 ][0 ] && s1.charAt(i - 1 ) == s3.charAt(i - 1 ); } for (int i = 1 ; i <= s1.length(); i++) { for (int j = 1 ; j <= s2.length(); j++) { dp[i][j] = (dp[i - 1 ][j] && s1.charAt(i - 1 ) == s3.charAt(i + j - 1 )) || (dp[i][j - 1 ] && s2.charAt(j - 1 ) == s3.charAt(i + j - 1 )); } } return dp[s1.length()][s2.length()]; } }
最长公共前缀 (Longest Common Prefix) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class StringProblems { public static String longestCommonPrefix (String[] strs) { if (strs == null || strs.length == 0 ) { return "" ; } String prefix = strs[0 ]; for (int i = 1 ; i < strs.length; i++) { while (strs[i].indexOf(prefix) != 0 ) { prefix = prefix.substring(0 , prefix.length() - 1 ); if (prefix.isEmpty()) { return "" ; } } } return prefix; } }
字符串压缩 (String Compression) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class StringProblems { public static String compressString (String str) { if (str == null || str.isEmpty()) { return "" ; } StringBuilder compressed = new StringBuilder (); int count = 1 ; for (int i = 1 ; i <= str.length(); i++) { if (i < str.length() && str.charAt(i) == str.charAt(i - 1 )) { count++; } else { compressed.append(str.charAt(i - 1 )).append(count); count = 1 ; } } if (compressed.length() >= str.length()) { return str; } return compressed.toString(); } }
寻找重复数 (Find the Duplicate Number) 给定一个包含 n+1
个整数的数组,数字都在 1
到 n
的范围内。找出这个重复的数字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class ArrayProblems { public static int findDuplicate (int [] nums) { int slow = nums[0 ]; int fast = nums[nums[0 ]]; while (slow != fast) { slow = nums[slow]; fast = nums[nums[fast]]; } int p1 = nums[0 ]; int p2 = slow; while (p1 != p2) { p1 = nums[p1]; p2 = nums[p2]; } return p1; } }
链表、栈与队列 合并两个有序链表 使用双指针 方法,创建一个新的链表,遍历两个输入链表,比较节点值并依次添加到新链表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public class LinkedListProblems { public static ListNode mergeTwoLists (ListNode l1, ListNode l2) { ListNode dummy = new ListNode (0 ); ListNode current = dummy; while (l1 != null && l2 != null ) { if (l1.val <= l2.val) { current.next = l1; l1 = l1.next; } else { current.next = l2; l2 = l2.next; } current = current.next; } if (l1 != null ) { current.next = l1; } if (l2 != null ) { current.next = l2; } return dummy.next; } }
链表倒数第 k 个节点 使用快慢指针 ,只遍历一次。快指针先走 k
步,然后快慢指针同时前进,当快指针到达末尾时,慢指针正好在倒数第 k
个位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class LinkedListProblems { public static ListNode findKthFromEnd (ListNode head, int k) { if (head == null || k <= 0 ) { return null ; } ListNode fast = head; ListNode slow = head; for (int i = 0 ; i < k; i++) { if (fast == null ) { return null ; } fast = fast.next; } while (fast != null ) { fast = fast.next; slow = slow.next; } return slow; } }
删除重复节点 给定一个已排序的链表,删除所有重复的节点,使得每个元素只出现一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class LinkedListProblems { public static ListNode deleteDuplicates (ListNode head) { if (head == null ) { return null ; } ListNode current = head; while (current != null && current.next != null ) { if (current.val == current.next.val) { current.next = current.next.next; } else { current = current.next; } } return head; } }
判断链表是否有环 使用快慢指针 。如果链表有环,快指针最终会追上慢指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class LinkedListProblems { public static boolean hasCycle (ListNode head) { if (head == null || head.next == null ) { return false ; } ListNode slow = head; ListNode fast = head.next; while (slow != fast) { if (fast == null || fast.next == null ) { return false ; } slow = slow.next; fast = fast.next.next; } return true ; } }
链表求和 两个非负整数由链表表示,每个节点包含一个数字。计算它们的和。这里假设链表按逆序 存储数字(个位在前)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class LinkedListProblems { public static ListNode addTwoNumbers (ListNode l1, ListNode l2) { ListNode dummy = new ListNode (0 ); ListNode current = dummy; int carry = 0 ; while (l1 != null || l2 != null || carry > 0 ) { int sum = carry; if (l1 != null ) { sum += l1.val; l1 = l1.next; } if (l2 != null ) { sum += l2.val; l2 = l2.next; } carry = sum / 10 ; current.next = new ListNode (sum % 10 ); current = current.next; } return dummy.next; } }
栈实现队列 使用两个栈,一个用于入队 (inStack
),一个用于出队 (outStack
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import java.util.Stack;class MyQueue { private Stack<Integer> inStack; private Stack<Integer> outStack; public MyQueue () { inStack = new Stack <>(); outStack = new Stack <>(); } public void push (int x) { inStack.push(x); } public int pop () { if (outStack.isEmpty()) { transfer(); } return outStack.pop(); } public int peek () { if (outStack.isEmpty()) { transfer(); } return outStack.peek(); } private void transfer () { while (!inStack.isEmpty()) { outStack.push(inStack.pop()); } } public boolean empty () { return inStack.isEmpty() && outStack.isEmpty(); } }
队列实现栈 使用两个队列,一个用于入栈 (q1
),一个用于辅助 (q2
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.util.LinkedList;import java.util.Queue;class MyStack { private Queue<Integer> q1; private Queue<Integer> q2; public MyStack () { q1 = new LinkedList <>(); q2 = new LinkedList <>(); } public void push (int x) { q1.offer(x); } public int pop () { while (q1.size() > 1 ) { q2.offer(q1.poll()); } int result = q1.poll(); Queue<Integer> temp = q1; q1 = q2; q2 = temp; return result; } public int top () { int result = pop(); q1.offer(result); return result; } public boolean empty () { return q1.isEmpty(); } }
有效括号 使用一个栈 来检查括号是否匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.Stack;public class StackProblems { public static boolean isValid (String s) { Stack<Character> stack = new Stack <>(); for (char c : s.toCharArray()) { if (c == '(' || c == '[' || c == '{' ) { stack.push(c); } else { if (stack.isEmpty()) { return false ; } char top = stack.pop(); if ((c == ')' && top != '(' ) || (c == ']' && top != '[' ) || (c == '}' && top != '{' )) { return false ; } } } return stack.isEmpty(); } }
树与递归 定义一个简单的二叉树节点类:
1 2 3 4 5 6 public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } }
二叉树前序遍历 (Preorder Traversal) 递归方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.ArrayList;import java.util.List;public class TreeTraversal { public static List<Integer> preorderTraversalRecursive (TreeNode root) { List<Integer> result = new ArrayList <>(); preorder(root, result); return result; } private static void preorder (TreeNode node, List<Integer> list) { if (node == null ) { return ; } list.add(node.val); preorder(node.left, list); preorder(node.right, list); } }
非递归方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.ArrayList;import java.util.List;import java.util.Stack;public class TreeTraversal { public static List<Integer> preorderTraversalIterative (TreeNode root) { List<Integer> result = new ArrayList <>(); if (root == null ) { return result; } Stack<TreeNode> stack = new Stack <>(); stack.push(root); while (!stack.isEmpty()) { TreeNode node = stack.pop(); result.add(node.val); if (node.right != null ) { stack.push(node.right); } if (node.left != null ) { stack.push(node.left); } } return result; } }
二叉树中序遍历 (Inorder Traversal) 递归方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.ArrayList;import java.util.List;public class TreeTraversal { public static List<Integer> inorderTraversalRecursive (TreeNode root) { List<Integer> result = new ArrayList <>(); inorder(root, result); return result; } private static void inorder (TreeNode node, List<Integer> list) { if (node == null ) { return ; } inorder(node.left, list); list.add(node.val); inorder(node.right, list); } }
非递归方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.util.ArrayList;import java.util.List;import java.util.Stack;public class TreeTraversal { public static List<Integer> inorderTraversalIterative (TreeNode root) { List<Integer> result = new ArrayList <>(); Stack<TreeNode> stack = new Stack <>(); TreeNode current = root; while (current != null || !stack.isEmpty()) { while (current != null ) { stack.push(current); current = current.left; } current = stack.pop(); result.add(current.val); current = current.right; } return result; } }
二叉树后序遍历 (Postorder Traversal) 递归方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.ArrayList;import java.util.List;public class TreeTraversal { public static List<Integer> postorderTraversalRecursive (TreeNode root) { List<Integer> result = new ArrayList <>(); postorder(root, result); return result; } private static void postorder (TreeNode node, List<Integer> list) { if (node == null ) { return ; } postorder(node.left, list); postorder(node.right, list); list.add(node.val); } }
非递归方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Stack;public class TreeTraversal { public static List<Integer> postorderTraversalIterative (TreeNode root) { List<Integer> result = new ArrayList <>(); if (root == null ) { return result; } Stack<TreeNode> stack = new Stack <>(); stack.push(root); while (!stack.isEmpty()) { TreeNode node = stack.pop(); result.add(node.val); if (node.left != null ) { stack.push(node.left); } if (node.right != null ) { stack.push(node.right); } } Collections.reverse(result); return result; } }
二叉树层序遍历 (Level Order Traversal) 使用队列 实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.Queue;public class TreeTraversal { public static List<List<Integer>> levelOrder (TreeNode root) { List<List<Integer>> result = new ArrayList <>(); if (root == null ) { return result; } Queue<TreeNode> queue = new LinkedList <>(); queue.offer(root); while (!queue.isEmpty()) { int levelSize = queue.size(); List<Integer> currentLevel = new ArrayList <>(); for (int i = 0 ; i < levelSize; i++) { TreeNode node = queue.poll(); currentLevel.add(node.val); if (node.left != null ) { queue.offer(node.left); } if (node.right != null ) { queue.offer(node.right); } } result.add(currentLevel); } return result; } }
对称二叉树 (Symmetric Tree) 使用递归 ,检查根节点的左右子树是否是镜像对称的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class TreeProblems { public static boolean isSymmetric (TreeNode root) { if (root == null ) { return true ; } return isMirror(root.left, root.right); } private static boolean isMirror (TreeNode t1, TreeNode t2) { if (t1 == null && t2 == null ) { return true ; } if (t1 == null || t2 == null ) { return false ; } return (t1.val == t2.val) && isMirror(t1.right, t2.left) && isMirror(t1.left, t2.right); } }
最大深度 (Maximum Depth of Binary Tree) 使用递归 (深度优先搜索)。
1 2 3 4 5 6 7 8 9 10 public class TreeProblems { public static int maxDepth (TreeNode root) { if (root == null ) { return 0 ; } int leftDepth = maxDepth(root.left); int rightDepth = maxDepth(root.right); return Math.max(leftDepth, rightDepth) + 1 ; } }
验证二叉搜索树 (Validate Binary Search Tree) 使用递归 或中序遍历 。二叉搜索树的中序遍历结果是升序的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class TreeProblems { public static boolean isValidBST (TreeNode root) { return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE); } private static boolean isValidBST (TreeNode node, long min, long max) { if (node == null ) { return true ; } if (node.val <= min || node.val >= max) { return false ; } return isValidBST(node.left, min, node.val) && isValidBST(node.right, node.val, max); } }
其他 最大子数组和 (Maximum Subarray Sum) 使用动态规划 (Kadane’s algorithm)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class ArrayProblems { public static int maxSubArray (int [] nums) { if (nums == null || nums.length == 0 ) { return 0 ; } int maxSoFar = nums[0 ]; int maxEndingHere = nums[0 ]; for (int i = 1 ; i < nums.length; i++) { maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]); maxSoFar = Math.max(maxSoFar, maxEndingHere); } return maxSoFar; } }
两数相加 (Large Number Addition) 将两个超长正整数字符串相加,模拟小学加法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class StringProblems { public static String addStrings (String num1, String num2) { StringBuilder sb = new StringBuilder (); int i = num1.length() - 1 ; int j = num2.length() - 1 ; int carry = 0 ; while (i >= 0 || j >= 0 || carry > 0 ) { int digit1 = (i >= 0 ) ? num1.charAt(i--) - '0' : 0 ; int digit2 = (j >= 0 ) ? num2.charAt(j--) - '0' : 0 ; int sum = digit1 + digit2 + carry; sb.append(sum % 10 ); carry = sum / 10 ; } return sb.reverse().toString(); } }
位运算实现加法 (Addition with Bitwise Operators) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MathProblems { public static int add (int a, int b) { int sum; int carry; while (b != 0 ) { sum = a ^ b; carry = (a & b) << 1 ; a = sum; b = carry; } return a; } }
全排列 (Permutations) 使用回溯算法 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.ArrayList;import java.util.List;public class BacktrackingProblems { public static List<List<Integer>> permute (int [] nums) { List<List<Integer>> result = new ArrayList <>(); backtrackPermute(result, new ArrayList <>(), nums, new boolean [nums.length]); return result; } private static void backtrackPermute (List<List<Integer>> result, List<Integer> tempList, int [] nums, boolean [] used) { if (tempList.size() == nums.length) { result.add(new ArrayList <>(tempList)); return ; } for (int i = 0 ; i < nums.length; i++) { if (used[i]) { continue ; } used[i] = true ; tempList.add(nums[i]); backtrackPermute(result, tempList, nums, used); tempList.remove(tempList.size() - 1 ); used[i] = false ; } } }
子集 (Subsets) 使用回溯算法 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.ArrayList;import java.util.List;public class BacktrackingProblems { public static List<List<Integer>> subsets (int [] nums) { List<List<Integer>> result = new ArrayList <>(); backtrackSubsets(result, new ArrayList <>(), nums, 0 ); return result; } private static void backtrackSubsets (List<List<Integer>> result, List<Integer> tempList, int [] nums, int start) { result.add(new ArrayList <>(tempList)); for (int i = start; i < nums.length; i++) { tempList.add(nums[i]); backtrackSubsets(result, tempList, nums, i + 1 ); tempList.remove(tempList.size() - 1 ); } } }
跳台阶 (Climbing Stairs) 这是经典的动态规划 问题,与斐波那契数列类似。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MathProblems { public static int climbStairs (int n) { if (n <= 2 ) { return n; } int [] dp = new int [n + 1 ]; dp[1 ] = 1 ; dp[2 ] = 2 ; for (int i = 3 ; i <= n; i++) { dp[i] = dp[i - 1 ] + dp[i - 2 ]; } return dp[n]; } }
实现一个简单的 Trie (前缀树) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import java.util.HashMap;import java.util.Map;class TrieNode { Map<Character, TrieNode> children; boolean isEndOfWord; public TrieNode () { children = new HashMap <>(); isEndOfWord = false ; } } class Trie { private TrieNode root; public Trie () { root = new TrieNode (); } public void insert (String word) { TrieNode current = root; for (char c : word.toCharArray()) { current = current.children.computeIfAbsent(c, k -> new TrieNode ()); } current.isEndOfWord = true ; } public boolean search (String word) { TrieNode current = root; for (char c : word.toCharArray()) { if (!current.children.containsKey(c)) { return false ; } current = current.children.get(c); } return current.isEndOfWord; } public boolean startsWith (String prefix) { TrieNode current = root; for (char c : prefix.toCharArray()) { if (!current.children.containsKey(c)) { return false ; } current = current.children.get(c); } return true ; } }
反转链表 II (Reverse Linked List II) 反转链表从位置 m
到 n
的部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class LinkedListProblems { public static ListNode reverseBetween (ListNode head, int m, int n) { ListNode dummy = new ListNode (0 ); dummy.next = head; ListNode pre = dummy; for (int i = 0 ; i < m - 1 ; i++) { pre = pre.next; } ListNode start = pre.next; ListNode then = start.next; for (int i = 0 ; i < n - m; i++) { start.next = then.next; then.next = pre.next; pre.next = then; then = start.next; } return dummy.next; } }
LRU 缓存 (LRU Cache) 使用 LinkedHashMap
可以非常方便地实现 LRU 缓存,因为它本身就维护了插入顺序或访问顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.util.LinkedHashMap;import java.util.Map;class LRUCache <K, V> extends LinkedHashMap <K, V> { private final int capacity; public LRUCache (int capacity) { super (capacity, 0.75f , true ); this .capacity = capacity; } @Override protected boolean removeEldestEntry (Map.Entry<K, V> eldest) { return size() > capacity; } public V get (K key) { return super .getOrDefault(key, null ); } public void put (K key, V value) { super .put(key, value); } }
判断回文链表 (Palindrome Linked List) 可以使用快慢指针找到中点,然后反转后半部分,最后比较两部分是否相等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class LinkedListProblems { public static boolean isPalindrome (ListNode head) { if (head == null || head.next == null ) { return true ; } ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null ) { slow = slow.next; fast = fast.next.next; } ListNode secondHalf = reverseList(slow); ListNode firstHalf = head; while (secondHalf != null ) { if (firstHalf.val != secondHalf.val) { return false ; } firstHalf = firstHalf.next; secondHalf = secondHalf.next; } return true ; } private static ListNode reverseList (ListNode head) { ListNode prev = null ; ListNode current = head; while (current != null ) { ListNode nextTemp = current.next; current.next = prev; prev = current; current = nextTemp; } return prev; } }
ReadWriteLock
ReadWriteLock
允许多个线程同时进行读操作,但只允许一个线程进行写操作,从而提高读多写少的场景下的并发性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;class SharedResource { private final ReadWriteLock rwLock = new ReentrantReadWriteLock (); private String data = "初始数据" ; public String readData () { rwLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 正在读取数据..." ); Thread.sleep(100 ); return data; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null ; } finally { rwLock.readLock().unlock(); } } public void writeData (String newData) { rwLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 正在写入数据..." ); Thread.sleep(1000 ); this .data = newData; System.out.println(Thread.currentThread().getName() + " 写入完成。" ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { rwLock.writeLock().unlock(); } } }
CountDownLatch
CountDownLatch
允许一个线程等待,直到其他线程都完成了某项工作。它就像一个倒计时器,一旦计数为零,等待的线程就会被唤醒。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class CountDownLatchDemo { public static void main (String[] args) throws InterruptedException { final int workerCount = 5 ; CountDownLatch latch = new CountDownLatch (workerCount); ExecutorService executor = Executors.newFixedThreadPool(workerCount); System.out.println("主线程:启动 " + workerCount + " 个工作线程..." ); for (int i = 0 ; i < workerCount; i++) { final int workerId = i; executor.execute(() -> { System.out.println("工作线程 " + workerId + " 开始执行任务..." ); try { Thread.sleep((long ) (Math.random() * 2000 )); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { System.out.println("工作线程 " + workerId + " 任务完成。" ); latch.countDown(); } }); } System.out.println("主线程:等待所有工作线程完成..." ); latch.await(); System.out.println("主线程:所有工作线程已完成,继续执行下一步。" ); executor.shutdown(); } }
CyclicBarrier
CyclicBarrier
允许一组线程在到达一个共同的屏障点后,再一起继续执行。它就像赛跑的起跑线,所有选手都准备好后,发令枪才响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class CyclicBarrierDemo { public static void main (String[] args) { final int runnerCount = 3 ; CyclicBarrier barrier = new CyclicBarrier (runnerCount, () -> { System.out.println("\n所有赛跑者都已就位,发令枪响!\n" ); }); ExecutorService executor = Executors.newFixedThreadPool(runnerCount); for (int i = 0 ; i < runnerCount; i++) { final int runnerId = i; executor.execute(() -> { try { System.out.println("赛跑者 " + runnerId + " 正在走向起跑线..." ); Thread.sleep((long ) (Math.random() * 3000 )); System.out.println("赛跑者 " + runnerId + " 到达起跑线,准备就绪。" ); barrier.await(); System.out.println("赛跑者 " + runnerId + " 开始跑步!" ); } catch (InterruptedException | BrokenBarrierException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown(); } }
Semaphore
Semaphore
(信号量)用来控制对某个资源的并发访问数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.util.concurrent.Semaphore;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class SemaphoreDemo { private final Semaphore semaphore = new Semaphore (3 ); public void park (int carId) { try { System.out.println("汽车 " + carId + " 正在寻找车位..." ); semaphore.acquire(); System.out.println("汽车 " + carId + " 成功进入停车场。" ); Thread.sleep((long ) (Math.random() * 3000 )); System.out.println("汽车 " + carId + " 离开停车场。" ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); } } public static void main (String[] args) { SemaphoreDemo demo = new SemaphoreDemo (); ExecutorService executor = Executors.newFixedThreadPool(10 ); for (int i = 0 ; i < 10 ; i++) { final int carId = i; executor.execute(() -> demo.park(carId)); } executor.shutdown(); } }
死锁预防 (按锁顺序) 通过资源有序分配 来打破死锁的循环等待 条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class DeadlockPrevention { private static final Object lockA = new Object (); private static final Object lockB = new Object (); public void threadOne () { synchronized (lockA) { System.out.println("线程 1: 已获得 lockA,尝试获取 lockB..." ); try { Thread.sleep(100 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } synchronized (lockB) { System.out.println("线程 1: 已获得 lockB。" ); } } } public void threadTwo () { synchronized (lockA) { System.out.println("线程 2: 已获得 lockA,尝试获取 lockB..." ); try { Thread.sleep(100 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } synchronized (lockB) { System.out.println("线程 2: 已获得 lockB。" ); } } } public static void main (String[] args) { DeadlockPrevention demo = new DeadlockPrevention (); new Thread (demo::threadOne, "Thread-1" ).start(); new Thread (demo::threadTwo, "Thread-2" ).start(); } }
线程池异常 使用 Future.get()
或在任务中捕获异常来处理线程池中任务抛出的异常。execute()
方法无法直接捕获异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.util.concurrent.*;class ThreadPoolExceptionDemo { public static void main (String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(2 ); Future<?> future = executor.submit(() -> { System.out.println("任务开始执行..." ); throw new RuntimeException ("这是一个模拟的任务执行异常。" ); }); try { future.get(); } catch (ExecutionException e) { System.err.println("捕获到任务执行异常:" + e.getCause().getMessage()); } executor.execute(() -> { System.out.println("另一个任务开始执行..." ); throw new IllegalArgumentException ("这个异常会被吞掉。" ); }); executor.shutdown(); executor.awaitTermination(1 , TimeUnit.SECONDS); } }
ThreadLocal
ThreadLocal
为每个线程提供了独立的变量副本,实现了数据隔离。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class ThreadLocalDemo { private static final ThreadLocal<String> threadLocal = new ThreadLocal <>(); public void setAndPrint (String value) { threadLocal.set(value); try { Thread.sleep(1000 ); System.out.println(Thread.currentThread().getName() + " 的变量值: " + threadLocal.get()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { threadLocal.remove(); } } public static void main (String[] args) { ThreadLocalDemo demo = new ThreadLocalDemo (); new Thread (() -> demo.setAndPrint("线程 A 的数据" ), "Thread-A" ).start(); new Thread (() -> demo.setAndPrint("线程 B 的数据" ), "Thread-B" ).start(); } }
LockSupport
LockSupport.park()
和 LockSupport.unpark()
提供了更灵活的线程阻塞和唤醒机制,类似于 wait()
和 notify()
,但不需要依赖锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import java.util.concurrent.locks.LockSupport;class LockSupportDemo { public static void main (String[] args) throws InterruptedException { Thread workerThread = new Thread (() -> { System.out.println("工作线程: 任务准备就绪,即将阻塞..." ); LockSupport.park(); System.out.println("工作线程: 被唤醒,继续执行。" ); }); workerThread.start(); Thread.sleep(2000 ); System.out.println("主线程: 唤醒工作线程。" ); LockSupport.unpark(workerThread); } }
线程通信 (按顺序打印) 使用 wait()
和 notifyAll()
实现三个线程按顺序打印。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 class SequentialPrinter { private int state = 0 ; private final Object lock = new Object (); public void printA () { printLetter("A" , 0 ); } public void printB () { printLetter("B" , 1 ); } public void printC () { printLetter("C" , 2 ); } private void printLetter (String letter, int expectedState) { for (int i = 0 ; i < 10 ; i++) { synchronized (lock) { while (state != expectedState) { try { lock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return ; } } System.out.print(letter); state = (state + 1 ) % 3 ; lock.notifyAll(); } } } public static void main (String[] args) throws InterruptedException { SequentialPrinter printer = new SequentialPrinter (); Thread threadA = new Thread (printer::printA); Thread threadB = new Thread (printer::printB); Thread threadC = new Thread (printer::printC); threadA.start(); threadB.start(); threadC.start(); threadA.join(); threadB.join(); threadC.join(); System.out.println("\n打印完成。" ); } }
线程池 ThreadPoolExecutor
的七个参数
corePoolSize
: 核心线程数。线程池中常驻的线程数量,即使空闲也不会被销毁。
maximumPoolSize
: 最大线程数。当工作队列已满,且任务量继续增加时,线程池可以创建的最大线程数。
keepAliveTime
: 空闲线程存活时间。当线程数大于 corePoolSize
时,非核心线程的空闲存活时间。
unit
: keepAliveTime
的时间单位。
workQueue
: 工作队列。用于存放等待执行的任务,常用的有 ArrayBlockingQueue
、LinkedBlockingQueue
等。
threadFactory
: 线程工厂。用于创建新线程,可以自定义线程的名称、优先级等。
handler
: 拒绝策略。当线程池和工作队列都已满时,用于处理新来的任务,例如抛出异常、由调用者执行等。
手写一个自定义线程池 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.util.concurrent.*;public class CustomThreadPool { public static void main (String[] args) { ExecutorService executor = new ThreadPoolExecutor ( 2 , 5 , 60L , TimeUnit.SECONDS, new LinkedBlockingQueue <>(10 ), Executors.defaultThreadFactory(), new ThreadPoolExecutor .CallerRunsPolicy() ); for (int i = 0 ; i < 20 ; i++) { final int taskId = i; executor.execute(() -> { System.out.println("任务 " + taskId + " 正在由线程 " + Thread.currentThread().getName() + " 执行。" ); try { Thread.sleep(100 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown(); } }
原子类 使用 AtomicBoolean
或 AtomicReference
解决并发问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.concurrent.atomic.AtomicBoolean;class AtomicBooleanDemo { private static final AtomicBoolean initialized = new AtomicBoolean (false ); public static void initialize () { if (initialized.compareAndSet(false , true )) { System.out.println(Thread.currentThread().getName() + ": 开始执行初始化操作..." ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(Thread.currentThread().getName() + ": 初始化完成。" ); } else { System.out.println(Thread.currentThread().getName() + ": 初始化已被其他线程执行,跳过。" ); } } public static void main (String[] args) { for (int i = 0 ; i < 5 ; i++) { new Thread (AtomicBooleanDemo::initialize, "Thread-" + i).start(); } } }
volatile
内存语义volatile
确保了可见性 和有序性 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class VolatileMemorySemantics { private static volatile boolean ready = false ; private static int number = 0 ; public static class WriterThread extends Thread { @Override public void run () { number = 42 ; ready = true ; } } public static class ReaderThread extends Thread { @Override public void run () { while (!ready) { } System.out.println("读取到的 number 值: " + number); } } public static void main (String[] args) throws InterruptedException { new WriterThread ().start(); new ReaderThread ().start(); } }
中断机制 一个线程通过响应 interrupt()
调用来正确停止自身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class InterruptibleThreadDemo { public static void main (String[] args) { Thread worker = new Thread (() -> { while (!Thread.currentThread().isInterrupted()) { try { System.out.println("线程正在执行..." ); Thread.sleep(1000 ); } catch (InterruptedException e) { System.out.println("线程被中断,即将退出..." ); Thread.currentThread().interrupt(); break ; } } System.out.println("线程已优雅地退出。" ); }); worker.start(); try { Thread.sleep(3500 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("主线程: 发送中断信号。" ); worker.interrupt(); } }
ConcurrentHashMap
ConcurrentHashMap
的并发原理是**分段锁( 7)**或 CAS + Synchronized( 8) ,只对操作的桶进行锁定,大大提高了并发性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;class ConcurrentHashMapDemo { public static void main (String[] args) throws InterruptedException { ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap <>(); ExecutorService executor = Executors.newFixedThreadPool(10 ); for (int i = 0 ; i < 10 ; i++) { final int threadId = i; executor.execute(() -> { for (int j = 0 ; j < 1000 ; j++) { String key = "key-" + (threadId * 1000 + j); map.put(key, j); } }); } executor.shutdown(); executor.awaitTermination(1 , TimeUnit.MINUTES); System.out.println("最终 map 的大小: " + map.size()); } }
ForkJoinPool
ForkJoinPool
是一个用于分治任务的线程池,RecursiveTask
是可返回结果的分治任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import java.util.concurrent.ForkJoinPool;import java.util.concurrent.RecursiveTask;class SumTask extends RecursiveTask <Long> { private final long [] array; private final int start; private final int end; private static final int THRESHOLD = 10000 ; public SumTask (long [] array, int start, int end) { this .array = array; this .start = start; this .end = end; } @Override protected Long compute () { if (end - start <= THRESHOLD) { long sum = 0 ; for (int i = start; i < end; i++) { sum += array[i]; } return sum; } else { int mid = start + (end - start) / 2 ; SumTask leftTask = new SumTask (array, start, mid); SumTask rightTask = new SumTask (array, mid, end); leftTask.fork(); long rightResult = rightTask.compute(); long leftResult = leftTask.join(); return leftResult + rightResult; } } public static void main (String[] args) { long [] array = new long [1000000 ]; for (int i = 0 ; i < array.length; i++) { array[i] = i; } ForkJoinPool pool = new ForkJoinPool (); SumTask task = new SumTask (array, 0 , array.length); long result = pool.invoke(task); System.out.println("大数组的和为: " + result); pool.shutdown(); } }
1. 语言基础 变量与数据类型 的基本数据类型决定了变量可以存储的数据范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class DataTypesDetailed { public static void main (String[] args) { byte b = 100 ; short s = 10000 ; int i = 100000 ; long l = 10000000000L ; float f = 3.14f ; double d = 3.1415926535 ; char c1 = 'A' ; char c2 = 65 ; System.out.println("c1 和 c2 是否相等? " + (c1 == c2)); boolean isFun = true ; System.out.println("学Java有趣吗?" + isFun); } }
类型转换 中,从小范围类型向大范围类型转换是自动 的(隐式转换);从大范围向小范围转换需要强制 转换(显式转换),可能造成数据丢失。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class TypeCasting { public static void main (String[] args) { int myInt = 100 ; long myLong = myInt; System.out.println("隐式转换后的 long 类型: " + myLong); double myDouble = 9.99 ; int myInteger = (int ) myDouble; System.out.println("显式转换后的 int 类型: " + myInteger); } }
数组 数组是存储固定大小 同类型元素的集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class ArrayExample { public static void main (String[] args) { int [] numbers = new int [5 ]; numbers[0 ] = 10 ; numbers[1 ] = 20 ; String[] fruits = {"Apple" , "Banana" , "Cherry" }; System.out.println("所有水果:" ); for (String fruit : fruits) { System.out.println(fruit); } } }
2. 面向对象编程 (OOP) 构造方法与方法重载 构造方法 是一种特殊方法,用于创建对象时初始化。方法重载 是指在同一个类中,方法名相同但参数列表不同(参数类型、数量或顺序)的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Student { String name; int age; String id; public Student () { this ("未知" , 0 , "000" ); } public Student (String name, int age) { this .name = name; this .age = age; this .id = "000" ; } public Student (String name, int age, String id) { this .name = name; this .age = age; this .id = id; } public int add (int a, int b) { return a + b; } public double add (double a, double b) { return a + b; } }
继承、多态与抽象 继承实现代码复用,多态实现行为多样化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 abstract class Vehicle { protected String brand; public Vehicle (String brand) { this .brand = brand; } public abstract void run () ; public void displayBrand () { System.out.println("品牌是: " + brand); } } class Car extends Vehicle { public Car (String brand) { super (brand); } @Override public void run () { System.out.println("汽车正在路上行驶..." ); } } class Bicycle extends Vehicle { public Bicycle (String brand) { super (brand); } @Override public void run () { System.out.println("自行车正在骑行..." ); } } public class PolymorphismDemo { public static void main (String[] args) { Vehicle myCar = new Car ("BMW" ); Vehicle myBicycle = new Bicycle ("Giant" ); myCar.run(); myBicycle.run(); } }
3. 核心类库 好的,这次我们将把各种方法的使用代码 直接嵌入到每个知识点的解释中,让您在学习概念的同时,就能看到具体的代码实现和效果。我们将专注于数组、字符串和集合 这三大核心部分,把它们的创建、遍历、和各种常用方法的代码示例都清晰地展示出来。
1. 数组(Array) 数组是一种固定大小的、用于存储同类型元素的容器。
创建和遍历 这里展示两种最常见的创建数组的方式,并使用两种循环进行遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.util.Arrays;public class ArrayExample { public static void main (String[] args) { int [] intArray = new int [3 ]; intArray[0 ] = 10 ; intArray[1 ] = 20 ; intArray[2 ] = 30 ; System.out.println("数组 intArray 的第一个元素是: " + intArray[0 ]); String[] stringArray = {"Hello" , "World" , "Java" }; System.out.println("\n--- 使用 for 循环遍历 ---" ); for (int i = 0 ; i < stringArray.length; i++) { System.out.println("stringArray[" + i + "] = " + stringArray[i]); } System.out.println("\n--- 使用增强 for 循环遍历 ---" ); for (String element : stringArray) { System.out.println("元素: " + element); } } }
Arrays
类的常用方法java.util.Arrays
类提供了很多静态方法,方便我们对数组进行操作,比如排序、查找、复制等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.util.Arrays;public class ArraysMethodExample { public static void main (String[] args) { int [] numbers = {4 , 2 , 8 , 1 , 6 }; Arrays.sort(numbers); System.out.println("排序后: " + Arrays.toString(numbers)); int index = Arrays.binarySearch(numbers, 6 ); System.out.println("元素 6 的索引是: " + index); int [] newArray = new int [5 ]; Arrays.fill(newArray, 99 ); System.out.println("填充后: " + Arrays.toString(newArray)); int [] copiedArray = Arrays.copyOf(numbers, 3 ); System.out.println("复制前3个元素: " + Arrays.toString(copiedArray)); int [] anotherArray = {1 , 2 , 4 , 6 , 8 }; System.out.println("两个数组是否相等: " + Arrays.equals(numbers, anotherArray)); } }
2. 字符串(String) String
是一个不可变的字符序列,这意味着一旦创建,它的内容就不能被修改。所有修改操作都会返回一个新的 String
对象。
创建和常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class StringExample { public static void main (String[] args) { String str1 = " Java is a great language. " ; String str2 = new String ("Java is a great language." ); int length = str1.length(); System.out.println("字符串长度: " + length); char firstChar = str1.charAt(2 ); System.out.println("第3个字符是: " + firstChar); String sub = str1.substring(5 , 7 ); System.out.println("截取子串: " + sub); int index = str1.indexOf("great" ); System.out.println("'great' 的索引: " + index); boolean contains = str1.contains("language" ); System.out.println("是否包含 'language': " + contains); boolean startsWith = str1.startsWith(" Java" ); System.out.println("是否以 ' Java' 开头: " + startsWith); String replacedStr = str1.replace("great" , "wonderful" ); System.out.println("替换后: " + replacedStr); String trimmedStr = str1.trim(); System.out.println("去除首尾空格: '" + trimmedStr + "'" ); System.out.println("转为大写: " + trimmedStr.toUpperCase()); String data = "apple,banana,orange" ; String[] fruits = data.split("," ); System.out.println("分割后: " + Arrays.toString(fruits)); String joinedString = String.join(" - " , fruits); System.out.println("连接后: " + joinedString); } }
StringBuilder
和 StringBuffer
对于需要频繁修改字符串的场景,应使用 StringBuilder
或 StringBuffer
以提高性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class StringBuilderExample { public static void main (String[] args) { StringBuilder sb = new StringBuilder ("Hello" ); sb.append(" World" ); System.out.println("追加后: " + sb); sb.insert(6 , "Beautiful " ); System.out.println("插入后: " + sb); sb.delete(6 , 15 ); System.out.println("删除后: " + sb); } }
3. 集合(Collections) Java 集合框架提供了强大的数据结构来存储和操作对象。
List (列表)List
是一种有序、可重复的集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.ArrayList;import java.util.LinkedList;import java.util.List;public class ListExample { public static void main (String[] args) { List<String> fruits = new ArrayList <>(); fruits.add("Apple" ); fruits.add("Banana" ); fruits.add("Apple" ); System.out.println("列表元素: " + fruits); System.out.println("第一个元素: " + fruits.get(0 )); System.out.println("列表大小: " + fruits.size()); fruits.set(1 , "Grape" ); System.out.println("替换后: " + fruits); fruits.remove(1 ); System.out.println("删除后: " + fruits); } }
Set (集)Set
是一种无序、不可重复的集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.util.HashSet;import java.util.Set;public class SetExample { public static void main (String[] args) { Set<String> uniqueFruits = new HashSet <>(); uniqueFruits.add("Apple" ); uniqueFruits.add("Banana" ); uniqueFruits.add("Apple" ); System.out.println("集合元素: " + uniqueFruits); boolean containsBanana = uniqueFruits.contains("Banana" ); System.out.println("是否包含 'Banana': " + containsBanana); uniqueFruits.remove("Banana" ); System.out.println("删除后: " + uniqueFruits); } }
Map (映射)Map
存储键值对,键是唯一的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.util.HashMap;import java.util.Map;public class MapExample { public static void main (String[] args) { Map<String, Integer> studentScores = new HashMap <>(); studentScores.put("Alice" , 95 ); studentScores.put("Bob" , 88 ); studentScores.put("Alice" , 100 ); System.out.println("Alice 的分数: " + studentScores.get("Alice" )); System.out.println("\n--- 遍历键集 ---" ); for (String name : studentScores.keySet()) { System.out.println("姓名: " + name + ", 分数: " + studentScores.get(name)); } System.out.println("\n--- 遍历值集 ---" ); for (Integer score : studentScores.values()) { System.out.println("分数: " + score); } System.out.println("\n--- 遍历键值对集 ---" ); for (Map.Entry<String, Integer> entry : studentScores.entrySet()) { System.out.println("姓名: " + entry.getKey() + ", 分数: " + entry.getValue()); } } }