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是一个抽象类…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
