java实战(五):理解多线程与多线程实现冒泡排序及可视化
多线程
- 1.多线程理解
- 1.1线程概念
- 1.2线程的创建和启动
- 1.3线程的同步与互斥
- 1.4线程的状态和生命周期
- 1.5线程间的通信
- 1.6处理线程的异常和错误
- 1.7实践
- 2.效果
- 3.代码
1.多线程理解
1.1线程概念
线程
:计算机中能够执行独立任务的最小单位。在操作系统中,每个程序都运行在一个或多个线程中。线程可以同时执行多个任务,使得程序能够并发执行,提高了程序的效率和响应能力。
与进程不同,线程是在进程内部创建和管理
的。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间和文件句柄等。每个线程有自己的执行路径和状态,可以独立执行不同的任务。
线程的创建和调度由操作系统负责,它会为每个线程分配资源,并按照一定的调度策略来决定线程的执行顺序。线程之间可以通过共享内存或消息传递
等方式进行通信和同步。
多线程编程可以提高程序的性能和响应能力,特别适用于需要同时处理多个任务或需要实时交互的应用程序。然而,多线程编程也带来了一些挑战,如线程安全性、竞态条件和死锁等问题,需要仔细考虑和处理。
1.2线程的创建和启动
线程的创建和启动可以通过继承Thread类或实现Runnable接口来实现。
- 继承Thread类:
- 创建一个继承自Thread类的自定义线程类,重写run()方法,在run()方法中定义线程的执行逻辑。
- 在自定义线程类中,可以添加其他成员变量和方法,用于线程的控制和数据传递。
- 在主程序中,创建自定义线程类的实例,并调用start()方法启动线程。
示例代码如下:
public class MyThread extends Thread {@Overridepublic void run() {// 线程执行的代码逻辑// ...}
}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}
- 实现Runnable接口:
- 创建一个实现了Runnable接口的类,实现run()方法,在run()方法中定义线程的执行逻辑。
- 在主程序中,创建Runnable接口实现类的实例,并将其作为参数传递给Thread类的构造方法。
- 调用Thread类的start()方法启动线程。
示例代码如下:
public class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行的代码逻辑// ...}
}public class Main {public static void main(String[] args) {MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start(); // 启动线程}
}
无论是继承Thread类
还是实现Runnable接口
,都需要重写run()
方法,在run()
方法中定义线程的执行逻辑。线程的实际执行逻辑应该写在run()
方法中。
通过调用start()
方法来启动线程,start()
方法会在后台创建一个新的线程,并调用run()
方法来执行线程的逻辑。
需要注意的是,不要直接调用run()
方法来启动线程,这样只会在当前线程中执行run()
方法,而不会创建新的线程。
1.3线程的同步与互斥
线程的同步和互斥是为了保证多个线程之间的正确协作和共享资源的安全访问。
-
同步:线程同步是指多个线程按照一定的顺序执行,以达到协作的目的。常用的同步机制有:
- 使用
synchronized
关键字:通过在方法或代码块前加上synchronized
关键字,可以确保同一时间只有一个线程可以执行被synchronized
修饰的代码段。 - 使用
Lock接口
和ReentrantLock类
:Lock接口
提供了更灵活的锁定机制,可以使用lock()
方法获取锁,使用unlock()
方法释放锁。
- 使用
-
互斥:线程互斥是指多个线程之间对共享资源的访问进行控制,保证同一时间只有一个线程可以访问共享资源,避免数据的不一致性和冲突。常用的互斥机制有:
- 使用
synchronized
关键字:通过在方法或代码块前加上synchronized
关键字,可以确保同一时间只有一个线程可以执行被synchronized
修饰的代码段。 - 使用
Lock接口
和ReentrantLock类
:Lock接口
提供了更灵活的锁定机制,可以使用lock()
方法获取锁,使用unlock()
方法释放锁。 - 使用信号量(
Semaphore
):信号量可以控制同时访问某个资源的线程数量,通过acquire()
方法获取信号量,release()
方法释放信号量。
- 使用
同步和互斥机制可以保证线程之间的协作和共享资源的安全访问,避免了数据竞争和不一致性的问题。
需要注意的是,在使用同步和互斥机制时,要避免死锁和活锁等问题,合理设计和使用锁定机制。
当多个线程同时访问共享资源时,可以使用同步和互斥机制来确保数据的一致性和避免冲突。以下是两个简单的例子:
- 使用
synchronized
关键字:
public class Counter {private int count;public synchronized void increment() {count++;}
}public class Main {public static void main(String[] args) {Counter counter = new Counter();Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(counter.getCount()); // 输出结果应为2000}
}
在上述例子中,Counter类
中的increment()
方法使用了synchronized
关键字,确保了对count
变量的访问是互斥的。两个线程分别执行increment()
方法,通过对count
进行加一操作,最终得到的结果应为2000。
- 使用
Lock接口
和ReentrantLock类
:
public class Counter {private int count;private Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}
}public class Main {public static void main(String[] args) {Counter counter = new Counter();Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(counter.getCount()); // 输出结果应为2000}
}
在上述例子中,Counter类
中的increment()
方法使用了Lock接口
和ReentrantLock类
,通过lock()
方法获取锁,使用unlock()
方法释放锁。两个线程分别执行increment()
方法,通过对count
进行加一操作,最终得到的结果应为2000。
这些例子展示了如何使用同步和互斥机制来确保多个线程对共享资源的安全访问。通过使用synchronized
关键字、Lock接口
和ReentrantLock类
等机制,可以避免数据竞争和不一致性的问题。
1.4线程的状态和生命周期
-
新建状态(New):线程对象被创建,但还没有调用start()方法。
-
就绪状态(Runnable):调用线程对象的start()方法后,线程进入就绪状态,等待CPU分配时间片。
-
运行状态(Running):当线程获得CPU时间片后,进入运行状态,执行run()方法中的代码。
-
阻塞状态(Blocked):线程在某些情况下会进入阻塞状态,暂时停止执行,直到满足某个条件后才能继续执行。
-
等待状态(Waiting):线程在某些情况下会进入等待状态,等待其他线程的唤醒。
-
计时等待状态(Timed Waiting):线程在某些情况下会进入计时等待状态,等待一段时间或满足某个条件后继续执行。
-
终止状态(Terminated):线程执行完run()方法或发生异常导致线程终止后,进入终止状态。
需要注意的是,线程的状态不是固定的,线程可以在不同的状态之间转换。例如,一个线程在运行状态下可能被阻塞或进入等待状态,然后再次回到运行状态。
1.5线程间的通信
线程间的通信是指多个线程之间通过共享的内存或其他方式进行信息交换和数据传递的过程。在Java中,线程间的通信可以通过以下几种方式实现:
-
共享变量:多个线程可以通过共享的变量进行通信。通过对共享变量的读写操作,线程可以传递信息和数据。需要注意的是,对于共享变量的读写操作需要进行同步,以确保线程安全。
-
等待/通知机制:通过使用
Object类
的wait()
、notify()
和notifyAll()
方法,线程可以进行等待和唤醒操作。一个线程可以调用wait()
方法进入等待状态,等待其他线程调用notify()
或notifyAll()
方法来唤醒它。
下面是一个简单的例子,演示了线程间通过共享变量和等待/通知机制进行通信:
public class Message {private String content;private boolean isAvailable = false;public synchronized void send(String message) {while (isAvailable) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}content = message;isAvailable = true;notifyAll();}public synchronized String receive() {while (!isAvailable) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}String message = content;isAvailable = false;notifyAll();return message;}
}public class Sender implements Runnable {private Message message;public Sender(Message message) {this.message = message;}@Overridepublic void run() {String[] messages = {"Hello", "World", "Goodbye"};for (String msg : messages) {message.send(msg);System.out.println("Sent: " + msg);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Receiver implements Runnable {private Message message;public Receiver(Message message) {this.message = message;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {String receivedMsg = message.receive();System.out.println("Received: " + receivedMsg);}}
}public class Main {public static void main(String[] args) {Message message = new Message();Thread senderThread = new Thread(new Sender(message));Thread receiverThread = new Thread(new Receiver(message));senderThread.start();receiverThread.start();}
}
在上述例子中,Message类
表示一个消息对象,包含一个共享的字符串变量content
和一个标志位isAvailable
。Sender线程
通过调用send()
方法向Message对象
发送消息,Receiver线程
通过调用receive()
方法接收消息。通过使用synchronized
关键字和wait()
、notify()
方法,实现了线程间的等待和唤醒操作,确保了消息的正确传递。
1.6处理线程的异常和错误
在处理线程的异常和错误时,我们可以采取以下几种方式:
- 使用
try-catch
块捕获异常:在线程的run()
方法中,可以使用try-catch
块来捕获可能发生的异常,并在catch
块中进行相应的处理。这样可以确保异常不会导致线程终止,而是继续执行后续的代码。
public class MyThread implements Runnable {@Overridepublic void run() {try {// 执行可能抛出异常的代码} catch (Exception e) {// 处理异常}}
}
- 在线程内部抛出异常:如果在线程的
run()
方法中抛出了异常,可以通过在run()
方法中直接抛出异常,然后在线程的调用方(例如主线程)中捕获并处理异常。
public class MyThread implements Runnable {@Overridepublic void run() {// 执行可能抛出异常的代码throw new RuntimeException("Something went wrong");}
}public class Main {public static void main(String[] args) {try {Thread myThread = new Thread(new MyThread());myThread.start();myThread.join();} catch (Exception e) {// 处理异常}}
}
- 使用
UncaughtExceptionHandler
处理未捕获的异常:如果在线程中的异常没有被捕获,可以通过设置线程的UncaughtExceptionHandler
来处理未捕获的异常。UncaughtExceptionHandler
是一个接口,可以自定义实现来处理异常。
public class MyThread implements Runnable {@Overridepublic void run() {// 执行可能抛出异常的代码throw new RuntimeException("Something went wrong");}
}public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e) {// 处理未捕获的异常}
}public class Main {public static void main(String[] args) {Thread myThread = new Thread(new MyThread());myThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());myThread.start();}
}
通过设置UncaughtExceptionHandler
,可以在发生未捕获的异常时进行处理,例如记录日志、发送通知等。
1.7实践
- 生产者-消费者模型:
这是一个经典的多线程问题,其中一个线程(生产者)生成数据,另一个线程(消费者)消费数据。这个模型可以用于解决生产者和消费者之间的数据交互问题。
关键部分:
- 创建一个共享的缓冲区,用于生产者和消费者之间的数据交换。
- 使用
synchronized
关键字来确保在访问共享缓冲区时的线程安全。 - 生产者线程在缓冲区未满时生成数据,并通知消费者线程。
- 消费者线程在缓冲区非空时消费数据,并通知生产者线程。
- 并行计算:
多线程可以用于并行计算,将一个大任务分解成多个小任务,然后并行执行这些小任务,最后将结果合并。
关键部分:
- 创建一个线程池,用于管理并发执行的任务。
- 将大任务分解成多个小任务,每个小任务实现
Runnable
接口。 - 将小任务提交给线程池进行并行执行。
- 等待所有任务执行完成后关闭线程池。
- 多线程网络编程:
多线程可以用于处理并发的网络请求,每个请求都可以在独立的线程中进行处理,提高系统的并发处理能力。
关键部分:
- 创建一个服务器套接字,监听指定的端口。
- 当有客户端连接时,为每个客户端创建一个独立的线程来处理请求。
- 在线程中处理客户端的输入和输出流,实现具体的业务逻辑。
- 多线程图像处理:
多线程可以用于图像处理,例如对一张大图进行分块处理,每个线程处理一个块,最后将处理后的块合并成最终的图像。
关键部分:
- 读取图像文件并创建
BufferedImage
对象。 - 将图像分成多个块,每个块由一个线程处理。
- 在每个线程中,对指定的图像块进行处理,例如应用滤镜、调整亮度等操作。
- 最后将处理后的图像块合并成最终的图像。
当然,下面是四个经典的实际应用程序,涉及到Java多线程的实践和练习。我将提供简要的代码示例,以帮助您更好地理解。
- 生产者-消费者模型:
这是一个经典的多线程问题,其中一个线程(生产者)生成数据,另一个线程(消费者)消费数据。这个模型可以用于解决生产者和消费者之间的数据交互问题。
import java.util.LinkedList;class ProducerConsumer {private LinkedList<Integer> buffer = new LinkedList<>();private int capacity = 5;public void produce() throws InterruptedException {int value = 0;while (true) {synchronized (this) {while (buffer.size() == capacity) {wait();}System.out.println("Producer produced: " + value);buffer.add(value++);notify();Thread.sleep(1000);}}}public void consume() throws InterruptedException {while (true) {synchronized (this) {while (buffer.isEmpty()) {wait();}int value = buffer.removeFirst();System.out.println("Consumer consumed: " + value);notify();Thread.sleep(1000);}}}
}public class Main {public static void main(String[] args) {ProducerConsumer pc = new ProducerConsumer();Thread producerThread = new Thread(() -> {try {pc.produce();} catch (InterruptedException e) {e.printStackTrace();}});Thread consumerThread = new Thread(() -> {try {pc.consume();} catch (InterruptedException e) {e.printStackTrace();}});producerThread.start();consumerThread.start();}
}
- 并行计算:
多线程可以用于并行计算,将一个大任务分解成多个小任务,然后并行执行这些小任务,最后将结果合并。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;class Task implements Runnable {private int taskId;public Task(int taskId) {this.taskId = taskId;}@Overridepublic void run() {System.out.println("Task " + taskId + " is running.");// 执行任务的逻辑}
}public class Main {public static void main(String[] args) {int numTasks = 10;ExecutorService executor = Executors.newFixedThreadPool(numTasks);for (int i = 0; i < numTasks; i++) {executor.submit(new Task(i));}executor.shutdown();try {executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);} catch (InterruptedException e) {e.printStackTrace();}}
}
- 多线程网络编程:
多线程可以用于处理并发的网络请求,每个请求都可以在独立的线程中进行处理,提高系统的并发处理能力。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;class ClientHandler implements Runnable {private Socket clientSocket;public ClientHandler(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {try {InputStream input = clientSocket.getInputStream();OutputStream output = clientSocket.getOutputStream();// 处理客户端请求的逻辑} catch (IOException e) {e.printStackTrace();}}
}public class Main {public static void main(String[] args) {int port = 8080;try {ServerSocket serverSocket = new ServerSocket(port);while (true) {Socket clientSocket = serverSocket.accept();Thread clientThread = new Thread(new ClientHandler(clientSocket));clientThread.start();}} catch (IOException e) {e.printStackTrace();}}
}
- 多线程图像处理:
多线程可以用于图像处理,例如对一张大图进行分块处理,每个线程处理一个块,最后将处理后的块合并成最终的图像。
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;class ImageProcessor implements Runnable {private BufferedImage image;private int startX;private int startY;private int width;private int height;public ImageProcessor(BufferedImage image, int startX, int startY, int width, int height) {this.image = image;this.startX = startX;this.startY = startY;this.width = width;this.height = height;}@Overridepublic void run() {// 图像处理逻辑,例如对指定区域进行滤镜处理等}
}public class Main {public static void main(String[] args) {String imagePath = "path/to/image.jpg";try {BufferedImage image = ImageIO.read(new File(imagePath));int numThreads = 4;int imageWidth = image.getWidth();int imageHeight = image.getHeight();int blockWidth = imageWidth / numThreads;int blockHeight = imageHeight / numThreads;for (int i = 0; i < numThreads; i++) {for (int j = 0; j < numThreads; j++) {int startX = i * blockWidth;int startY = j * blockHeight;Thread thread = new Thread(new ImageProcessor(image, startX, startY, blockWidth, blockHeight));thread.start();}}} catch (IOException e) {e.printStackTrace();}}
}
2.效果
3.代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class BubbleSortVisualization extends JFrame {private int[] array;private int[] sortedArray;private JPanel originalBarPanel;private JPanel sortedBarPanel;private JTextField lengthField;private JTextField threadField;public BubbleSortVisualization() {setTitle("冒泡排序可视化");setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLayout(new BorderLayout());// 顶部输入框和按钮JPanel inputPanel = new JPanel();inputPanel.setLayout(new GridLayout(1,6));JLabel lengthLabel = new JLabel("数组长度:");lengthField = new JTextField(10);JLabel threadLabel = new JLabel("线程数:");threadField = new JTextField(10);JButton generateButton = new JButton("生成数组");JButton sortButton = new JButton("开始排序");generateButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {generateArray();}});inputPanel.add(lengthLabel);inputPanel.add(lengthField);inputPanel.add(threadLabel);inputPanel.add(threadField);inputPanel.add(generateButton);inputPanel.add(sortButton);// 中间柱状图面板originalBarPanel = new JPanel() {@Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);drawOriginalBars(g);}};sortedBarPanel = new JPanel() {@Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);drawSortedBars(g);}};// 底部排序按钮sortButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {int threads = Integer.parseInt(threadField.getText());sortArray(threads);}});JPanel barPanelContainer = new JPanel(new GridLayout(2, 1));barPanelContainer.add(originalBarPanel);barPanelContainer.add(sortedBarPanel);add(inputPanel, BorderLayout.NORTH);add(barPanelContainer, BorderLayout.CENTER);setSize(1500,600);setLocationRelativeTo(null);setVisible(true);array= new int[]{2, 5, 1, 6, 8, 7, 9, 11, 15, 17};originalBarPanel.repaint();sortedArray=new int[]{1,2,5,6,7,8,9,11,15,17};sortedBarPanel.repaint();}private void generateArray() {try {int length = Integer.parseInt(lengthField.getText());array = new int[length];sortedArray = new int[length];Random random = new Random();for (int i = 0; i < length; i++) {int num = random.nextInt(100);array[i] = num;sortedArray[i] = num;}originalBarPanel.repaint();} catch (NumberFormatException e) {JOptionPane.showMessageDialog(this, "请输入有效的数组长度", "错误", JOptionPane.ERROR_MESSAGE);}lengthField.setText(" ");}private void sortArray(int threads) {ExecutorService executorService = Executors.newFixedThreadPool(threads);int chunkSize = array.length / threads;for (int i = 0; i < threads; i++) {int start = i * chunkSize;int end = (i == threads - 1) ? array.length : (i + 1) * chunkSize;executorService.execute(new BubbleSortRunnable(start, end));}executorService.shutdown();while (!executorService.isTerminated()) {// 等待所有线程完成排序}bubbleSort(sortedArray); // 主线程进行冒泡排序sortedBarPanel.repaint();threadField.setText(" ");}private void bubbleSort(int[] arr) {int n = arr.length;for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}}private void drawOriginalBars(Graphics g) {int barWidth = originalBarPanel.getWidth() / array.length;int barHeightScale = originalBarPanel.getHeight() / Arrays.stream(array).max().getAsInt();g.setColor(Color.BLUE);for (int i = 0; i < array.length; i++) {int barHeight = array[i] * barHeightScale;g.fillRect(i * barWidth, originalBarPanel.getHeight() - barHeight, barWidth, barHeight);}}private void drawSortedBars(Graphics g) {int barWidth = sortedBarPanel.getWidth() / sortedArray.length;int barHeightScale = sortedBarPanel.getHeight() / Arrays.stream(sortedArray).max().getAsInt();g.setColor(Color.RED);for (int i = 0; i < sortedArray.length; i++) {int barHeight = sortedArray[i] * barHeightScale;g.fillRect(i * barWidth, sortedBarPanel.getHeight() - barHeight, barWidth, barHeight);}}private class BubbleSortRunnable implements Runnable {private int start;private int end;public BubbleSortRunnable(int start, int end) {this.start = start;this.end = end;}@Overridepublic void run() {for (int i = start; i < end - 1; i++) {for (int j = start; j < end - i - 1; j++) {if (sortedArray[j] > sortedArray[j + 1]) {int temp = sortedArray[j];sortedArray[j] = sortedArray[j + 1];sortedArray[j + 1] = temp;}}}}}public static void main(String[] args) {SwingUtilities.invokeLater(new Runnable() {@Overridepublic void run() {new BubbleSortVisualization();}});}
}
相关文章:

java实战(五):理解多线程与多线程实现冒泡排序及可视化
多线程 1.多线程理解1.1线程概念1.2线程的创建和启动1.3线程的同步与互斥1.4线程的状态和生命周期1.5线程间的通信1.6处理线程的异常和错误1.7实践 2.效果3.代码 1.多线程理解 1.1线程概念 线程:计算机中能够执行独立任务的最小单位。在操作系统中,每个…...
mysql-binlog,redolog 和 undolog区别
binlog MySQL的binlog(二进制日志 或 归档日志)是一种记录数据库的更改操作的日志。它包含了对数据库进行的插入、更新和删除操作的详细信息。binlog是以二进制格式存储,可以用于恢复数据库、数据复制和数据同步等操作。具体来说,…...

Redis SDS 源码
struct sdshdr {int len;int free;char buf[]; }; 底层数据结构的好处: 杜绝缓冲区溢出。减少修改字符串长度时所需的内存重分配次数。二进制安全。兼容部分C字符串函数。 常用命令: set key value、get key 等 应用场景:共享 session、分…...

肖sir__mysql之单表练习题2__(2)
mysql之单表练习题 一.建表语句 create table grade(class int(4),chinese int(8),english int(4),math int(8),name varchar(20),age int(8),sid int(4)primary key auto_increment) DEFAULT charsetutf8; insert into grade(class,chinese,english,math,name,age)values(1833…...

nuxt、vue实现PDF和视频文件的上传、下载、预览
上传 上传页面 <el-form-item :label"(form.ququ3 1 ? 参培 : form.ququ3 2 ? 授课 : ) 证明材料" prop"ququ6"><PdfUpload v-model"form.ququ6" :fileType"[pdf, mp4, avi, ts]"></PdfUpload> </el-form-i…...
c++ 写成.h .cpp main.cpp 多文件形式
1 .h 声明方法/函数 用于连接定义和实例使用 // max.h #ifndef MAX_H #define MAX_Hint max(int a, int b);#endif /* 在#ifndef和#define中使用的MAX_H就是指的max.h这个头文件的名字。具体来说,#ifndef MAX_H中MAX_H代表了max.h这个头文件的一个唯一的标识符。#define MAX_H…...
组合总和(回溯)
题目描述 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件: 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。 样例输入 示例 1: 输入: k 3, n 7 …...

【代码】微电网两阶段鲁棒优化经济调度方法(完美复现)matlab-yalmip-cplex/gurobi
程序名称:两阶段鲁棒优化—微电网两阶段鲁棒优化经济调度方法_刘一欣 实现平台:matlab-yalmip-cplex/gurobi 简介:针对微电网内可再生能源和负荷的不确定性,建立了 min-max-min 结构的两阶段鲁棒优化模型,可得到最恶…...

关于无线测温系统在海上石油平台的应用探讨-安科瑞 蒋静
摘要:海上石油平台的封闭式中高压配电盘在平台电力系统起着十分重要的作用,通过统计其配电盘的 大部分故障为前期的热效应引起,由于配电盘内部空间封闭狭小,所以无法进行人工巡查测温,这给油田的供电系统埋下了一定的潜…...

CSS 滚动捕获 scroll-padding
scroll-padding 非滚动捕获容器滚动捕获容器语法兼容性 CSS 滚动捕获 scroll-padding 设置元素的滚动内边距, 就像 padding 所做的那样. 但并不影响布局. 非滚动捕获容器 我们先来看看不影响布局到底是什么意思. 我们平时会见到左侧是内容, 右侧是内容导航的页面, 比如下图 这…...
asp.net core webpi 结合jwt实现登录鉴权
1.安装jwt nuget包 <PackageReference Include"Microsoft.AspNetCore.Authentication.JwtBearer" Version"6.0.25" /><PackageReference Include"System.IdentityModel.Tokens.Jwt" Version"7.0.3" />1.1创建jwt配置类 n…...

【香橙派】实战记录2——烧录安卓镜像及基本功能
文章目录 一、安卓烧录二、安卓基本功能1、蓝牙2、相机功能3、投屏 一、安卓烧录 检查环境:检查PC系统,确保有Microsoft Visual C 2008 Redistrbutable - x86,否则在官网下载的官方工具 - 安卓镜像烧录工具里运行vcredist_x86.exe。 插入存储…...

【spring(六)】WebSocket网络传输协议
🌈键盘敲烂,年薪30万🌈 目录 核心概要: 概念介绍: 对比HTTP协议:⭐ WebSocket入门案例:⭐ 核心概要: websocket对比http 概念介绍: WebSocket是Web服务器的一个组件…...

MidJourney笔记(6)-Niji模式
Niji模式 回顾一下,在讲解settings命令时,我们可以看到一个Niji字眼。 而且是在Midjourney V4之后才有的,那Niji到底是什么? Niji是MidJourney中用于绘制二次元/动漫风格的模型,那Niji的V4和V5有什么区别呢?...
Linux命令(139)之ab
linux命令之ab 1.ab介绍 linux命令ab(E.g:apachebench)是apache自带的压力测试工具。ab命令会创建多个并发访问线程,模拟多个访问者同时对某一URL进行访问。由于ab命令测试是基于URL的,因此,它既可以用来测试apache httpd的负载压力&#x…...
笔记----单纯剖分----1
笔记----单纯剖分 定义 线性组合仿射组合: 线性组合的系数为1凸组合: 仿射组合所有的系数都是正数 凸集 R^m 的 任意有限个点的凸组合仍在其中的子集仿射子空间 R^m 的 任意有限个点的仿射组合仍在其中的子集凸包 conv(A) A是R^m的一个子集 A的所有有限凸…...

mybatis源码(五)springboot pagehelper实现查询分页
1、背景 springboot的pagehelper插件能够实现对mybatis查询的分页管理,而且在使用时只需要提前声明即可,不需要修改已有的查询语句。使用如下: 之前对这个功能一直很感兴趣,但是一直没完整看过,今天准备详细梳理下。按…...
【BUG】SpringBoot项目Long类型数据返回前端精度丢失问题
问题描述 后端再给前端返回数据,使用Long类型的时候存在精度丢失问题。 原因分析: 分布式项目中广泛使用雪花算法生成ID作为数据库表的主键,Long类型的雪花ID有19位,而前端接收Long类型用的是number类型,但是number…...

UI自动化Selenium find_elements和find_element的区别
# 如果获取的element是list,那么需要用find_elements方法;此方法会返回list,然后使用len() 方法,计算对象的个数; # find_element方法返回的不是list对象,所以导致没办法计算对象个数 # 1.返回值类型不同…...
【Android】Window和WindowManager
文章目录 理解Window和WindowManagerWindow和WindowManagerWindow的内部机制Window的添加过程Window的删除过程Window的更新过程 Window的创建过程Activity的Window创建过程Dialog的Window创建过程Toast的Window创建过程 理解Window和WindowManager Window是一个抽象类…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...