线程(一)

news2024/12/23 2:06:06

线程

1. 线程

定义:线程是进程的组成部分,不同的线程执行不同的任务,不同的功能模块,同时线程使用的资源师由进程管理,主要分配CPU和内存。

​ 在进程中,线程执行的方式是抢占式执行操作,需要考虑的是当前的线程是否持有CPU的执行权和等待权,同时关注线程的状态。

多线程:有了多线程,我们就可以让程序同时处理多个事件。作用能够快速提高效率。

并发和并行的区别

	并发:表示的是在同一时刻,多个任务在单个的CPU上的交替执行的操作。
	并行:表示的是在单位时间内,多个处理器或者多核处理器的同时处理的方式,是真正意义上的同时进行。
    串行:表示的是有N个任务,通过一个线程进行顺序的执行操作,由于任务中只存在一个任务,方法都是在一个线程的执行的,所以不存在线程的不安全的情况,也就是不存在临界区的问题。比如两个人排队使用一台电脑。

1.1 多线程的优点

有利于提高CPU的利用效率,允许单个程序创建多个并行执行的线程来完成各自的任务

1.2 多线程的缺点

  1. 线程也是程序,所以线程是需要进行内存的占用的,线程越多,内存的占用越多
  2. 多线程需要进行协调和管理,所以需要CPU时间跟踪线程
  3. 线程之间对于共享资源的访问是相互影响的,必须解决竞用共享资源的问题。

1.3 守护线程

	守护线程是运行在后台的,为其他的前台的线程提供服务的,也可以说守护线程是JVM中非守护线程的“佣人”一旦所有的线程都结束运行,守护线程会随着JVM进行运行操作。

2. 线程的创建

2.1 通过继承Thread类的方式

​ Tread类是线程类,一种反方是将类声明为Thread的子类的方式,该子类的应该通过重写run方法的方式来创建线程。

​ 使用的步骤格式:

  1. 自己定义一个类继承Thread
  2. 重写run方法
  3. 创建子类对象,而且进行线程的启动
class MyThread1 extends Thread {
    @Override
    public void run() {
        // 线程代码
    }
}

main() {
    // 实例化自定义继承 Thread 类子类对象
    MyThread1 mt1 = new MyThread1();
    
    // 利用继承父类 Thread 中的 start 方法,启动线程。【切记不可以直接调用 run 方法】
    mt1.start();
}

【重点】

为什么我们通过调用start方法来执行run方法,而不是直接调用run方法

  1. 首先new一个Tread线程,线程进入到新创建的状态中,调用start方法,会启动一个线程并且将线程进入到就绪的状态,当分配到时间片的时候,就能够进行工作了。start() 方法会执行线程的准备工作,然后自己进行执行run() 方法的内容,这才是真正的多线程的工作。
  2. 直接执行run方法的时候,main方法会将其看做一个普通的反方进行执行操作,并不会在某个线程中去执行它,所以不是多线程的工作。

2.2 通过实现Runnable接口

多线程的创建的步骤:

  1. 自己定义一个类实现Runnable接口
  2. 重写里面的run方法
  3. 创建自己的类对象
  4. 创建一个Tread类的对象,Runnable作为参数传入,并且开启线程

Runnable 接口是一种函数式接口,@FuncationalInterface ,能够使用lambda表达式。

class MyThread2 implements Runnable {
	@Override
    public void run() {
        // 线程代码
    }
}

main() {
    /*
    可以利用 Thread 类构造方法,实例化目标线程对象,执行对应线程目标
    将 Runnable 接口的实现类对象作为 Thread 构造方法参数
    构造方法 Thread(Runnable target);
    	构造方法的作用是以 :Runnable 接口实现类为当前线程的执行目标,new 关键字 + 构造方法实例化线程对象
    */
    Thread t1 = new Thread(new MyThread2());
    
    t1.start();
}
真实案例的代码实现:
 class MyThread1 extends Thread {
    @Override
    public void run() {
         for (int i = 0; i < 100; i++) {
           System.out.println("自定义线程继承 Thread 类型完成线程类");
            }
        }
    }

class MyThread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("自定义线程遵从 Runnable 接口实现线程类");
        }
    }
}

/**
 * Thread 自定义线程类实现的两种方式
 *
 * @author Anonymous 2023/3/8 10:06
 */
public class Demo1 {
    public static void main(String[] args) {
        MyThread1 mt1 = new MyThread1();
        Thread mt2 = new Thread(new MyThread2());

        mt1.start();
        mt2.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main方法内容...");
        }
    }
}

2.3 通过实现Callable接口(有返回结果)

多线程的第三种的实现的方式:

  1. 创建一个 自定义 类实现java.util.concurrent包Callable接口
  2. 重写call方法(有返回值,表示的是多线程的运行的结果)
  3. 创建MyCallable的对象(表示多线程要进行执行的任务)
  4. 创建FutureTask的对象(作用是管理多线程的运行的结果)
  5. 创建Thread类的对象,并且进行启动的操作(表示线程)

【注意】:其中的callable是存在泛型的格式的,其中的泛型表示的是返回值的泛型

//方法步骤
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        /*
         * 用来求解1~100 的和
         */
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            sum += i;
        }
        return sum;
    }
}


// 测试实现操作
public class TestMain {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*
         * 1. 创建MyCallable的对象(表示的是多线程要执行的任务)
         */
        MyCallable myCallable = new MyCallable();

        /*
         2. 创建FutureTask的对象,作用是用来管理多线程的运行的结果
         */
        FutureTask<Integer> integerFutureTask = new FutureTask<>(myCallable);

        /*
         * 3. 创建线程的对象
         */
        Thread thread = new Thread(integerFutureTask);

        /*
            4.启动线程
         */
        thread.start();

        /*
         * 获取多线程的运行的结果
         */
        Integer integer = integerFutureTask.get();
        System.out.println(integer);


    }
}

总结

  1. Tread 继承的操作,编码比较简单,可以直接使用Tread类中的方法,可以拓展的可能性比较差,不能够继承其他的类
  2. 接口的方式是可拓展性是比较强的,实现接口的同时还可以继承其他的类,编程相对来说比较复杂,不能够直接使用Tread类中方法。

2.4 线程中操作方法

方法名称作用
String getName()获取指定线程对象的名称
void setName(String name)针对于线程的名称获取和设置
int getPriority()获取指定线程的优先级
void setPriority(int newPriority)获取指定线程的优先级 默认是5 只能够提供被执行的概率,不能确保一定会执行操作
final void setDaemon(boolean flag)设置指定线程为守护线程
boolean isDaemon()设置和判断当前线程是否为守护线程
static void sleep(long time)单位是ms强迫一个线程睡眠的时间,单位是毫秒级别的
static Thread currentThread();在哪一个线程代码中执行,获取当前线程代码对应的线程对象。
static void yield()出让线程,礼让线程的操作
static void join()插入线程、插队线程的操作
构造方法:
	Thread();Thread(Runnable target);Runnable 接口实现类为当前线程的执行目标,new 关键字 + 构造方法实例化线程对象
	Thread(Runnable target, String threadName);Runnable 接口实现类为当前线程的执行目标,同时明确当前线程的名字,
	new 关键字 + 构造方法实例化线程对象
对于指定的方法进行分析的操作
    
	1. String getName()  : 表示的是获取指定的线程的名称,如果没有设置名称,线程也会自动生成名称的,通过源码我们能够看到是this当前线程 + nextThreadNum(): 是一个变量的自动递增。
 	2. void setName(String name):对于线程进行名字的设置除了调用方法的设置的方式之外,还能够通过调用构造方法的方式来进行实现:比如说Thread(String threadName)    以及   Thread(Runnable target, String threadName)两种方法来实现线程名称的命名。
    3.static Tread currentThread() :获取当前线程的对象。细节是:当JVM虚拟机进行启动的时候,会自动的启动多条线程,其中有一条线程是main线程,它的作用是调用main方法,并且执行里面的代码,在以前的时候,我们所写的代码其实都是运行在main线程中
    4.static void sleep(long time):那条线程执行到这个反方,那么那条线程就会在这里进行停留时间。方法的参数,就表示的是睡眠的时间,时间的单位毫秒。1s = 1000ms 当时间到了之后,线程会自动的醒来,继续执行下面的其他的代码。
    5.final void setDaemon(boolean flag) 当其他的非守护线程执行完毕之后,守护线程会陆续的结束,既当其他线程结束操作之后,守护线程也就没有存在的必要了。
/**
* 通过实现runnable接口实现线程的创建
*/
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"____"+i);
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        /*
         * 创建线程需要执行的参数对象
         */
        Runnable runnable = new MyRunnable();

        /*
         *  创建线程对象
         */
        Thread thread = new Thread(runnable, "张三");
        Thread thread1 = new Thread(runnable, "李四");

        /*
         * 通过更改优先级的大小,提高线程优先运行的概率,不能够保证一定是优先执行的操作。
         */
        thread.setPriority(10);
        thread1.setPriority(6);


        /*
         * 进行线程的执行的操作,需要进行开启线程操作
         */
        thread.start();
        thread1.start();

    }
}

运行的结果:
李四____0
张三____0
张三____1
张三____2
张三____3
张三____4
张三____5
张三____6
张三____7
张三____8
张三____9
李四____1
李四____2
李四____3
李四____4
李四____5
李四____6
李四____7
李四____8
李四____9

2.5 线程的生命周期

sleep 方法会让线程睡眠,睡眠的时间到了之后,不会立刻执行下面的代码:不会直接执行,会进入到就绪的状态上,然后进行线程资源的抢夺,如果抢夺到资源后能够运行。

3. 线程安全

​ 线程安全的问题:对于线程安全来说,所有的隐患都是出现多个线程访问的情况下产生的,我们能够确保在多条线程访问的情况下,我们的程序能够按照我们预期的行为去执行。

​ 需求:某电影院中目前需要上映国产的大片,共有100张票,需要三个窗口进行售票的行为,请设计一个程序模拟电影院进行买票的行为。

// 采用继承的方式创建对象
/**
 * 通过继承的方式操作线程
 */
public class MyThread extends Thread{

    /*表示的是这个类中的所有的对象,都在共享tickets的数据
     */
    static int ticket = 0;

    @Override
    public void run() {
         while (true) {
             if (ticket < 100){

                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }

                 ticket ++ ;
                 System.out.println(getName()+"正在卖"+ticket+"张票");
             } else {
                 break;
             }
         }
    }
}

// 进行检验
public class Demo1 {
    /**
     *检验多线程的问题
     *
     * @param args
     */
    public static void main(String[] args) {

        /*
         * 1. 创建线程对象
         */
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        MyThread myThread3 = new MyThread();

        /*
            通过调用方法起名字
         */
        myThread1.setName("窗口1");
        myThread2.setName("窗口2");
        myThread3.setName("窗口3");

        /*
        开启线程的操作
         */
        myThread1.start();
        myThread2.start();
        myThread3.start();

    }
}

运行的结果:
    ......
窗口1正在卖96张票
窗口3正在卖97张票
窗口1正在卖99张票
窗口2正在卖99张票
窗口3正在卖100张票
窗口1正在卖101张票
窗口2正在卖102张票
    分析:出现重复的数据卖出,同时出现数据的溢出操作

总结:在进行循环的时候,出现线程满足循环的条件,所以进行执行的操作,出现线程争夺资源的情况,所以造成的问题是关于线程的安全的问题。

3.1 同步代码块

为了解决上述的问题的情况,我们可以引入的方法是同步的代码块的操作,把操作共享数据的资源进行加锁的操作

特点

  1. 锁默认的是打开的关系,有一个线程进去,锁自动关闭
  2. 里面的代码全部执行完毕,线程出来,锁自动打开。
格式:
    synchronized(){
    	操作共享数据的代码
}
// 改进的代码操作如下所示:
public class MyThread extends Thread{

    /*表示的是这个类中的所有的对象,都在共享tickets的数据
     */
    static int ticket = 0;
    /**
    *
    针对于第一种的创建的方式,可能出现创建多种的线程对象,因此将之加上static 将数据共享   ,如果是对于方法二而言:创建的只有一种Runnable对象。而且是当做参数,创建线程的操作。因此不需要进行共享的声明。
    */

    @Override
    public void run() {
         while (true) {
             
             //此处是更改之后的数据的操作
             
             synchronized ("锁对象要求是唯一"){
                 if (ticket < 100){
    
                     try {
                         Thread.sleep(100);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
    
                     ticket ++ ;
                     System.out.println(getName()+"正在卖"+ticket+"张票");
                 } else {
                     break;
                 }
             }
         
           
         }
    }
}

改进后的运行的效果是:
    ......
窗口3正在卖95张票
窗口2正在卖96张票
窗口2正在卖97张票
窗口3正在卖98张票
窗口1正在卖99张票
窗口3正在卖100张票

【重点操作】:

​ 在锁结构中,我们能够使用的过程中加上唯一的锁对象,对于锁对象的选择上来说

  1. 必须保证唯一的特性
  2. 通常是一个类内会设置一个独立的变量作为锁对象使用
  3. 包装类不得用于锁对象使用
  4. 当前类型的Class对象可以作为锁对象,但是不推荐

3.2 同步方法

  1. 使用的关键字是synchronized

  2. 限制对应的方法是有且只能通过一个线程进入到方法中执行目标的任务

  3. 重点进行区分的是有static修饰的方法是静态方法,需要进行考虑的是当前锁对象的情况,以及同步范围的情况。

同步方法的格式:
    修饰符 synchronized 返回值类型 方法名称(方法参数){
    
}

1.同步方法是锁住方法中的所有的代码
2. 锁对象不能够自己指定
 // 注意:非静态的同步方法对象是当前调用方法的对象 -> this
public synchronized void sale() {...}

// 静态同步方法锁对象是当前类.class  类名.class 唯一的锁对象的特征
public static synchronized void staticSale() {...}

3.2.1 同步方法的范围

非静态的成员方法使用的是 synchronized 同步的约束,同步的范围

重点:通过当前的实例化对象作为锁对象,实例化对象调用任意 synchronized 修饰的同步方法,所有其他的 synchronized 同步方法,一律不能够进行执行的操作

java中经常使用的是:
    	ArrayListVector 
		StringBuilderStringBuffer
		HashMapHashTable

​ 静态的成员方法使用 synchronized 同步进行约束的操作,锁对象是 类名.class .无论那一条线程,任何一个对象来执行目标方法,都会被 类名.class 锁住,其他的任意的线程,任意的对象,都无法执行所有其他 static 修饰的 synchronized 约束的同步方法。

例子一:
    
public static synchronized void test1() {
    for (int i = 0; i < 10; i++) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Test1 方法");
    }
}

/*
等价于
synchronized (TypeB.class) {
    for (int i = 0; i < 10; i++) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Test1 方法");
    }
}
 */
例子二:
    电影院售票的操作套路
    
  采用的是通过实现接口的方式来创建线程 
public class MyRunnable implements Runnable{
int ticket = 0;

/**
 * 执行的套路是:
 *  1. 循环
 *  2. 同步代码块
 *  3.判断共享数据是否到了末尾,到末尾的情况
 *  4. 判断共享数据是否到了末尾,没有到末尾的情况
 */

@Override
public void run() {
	while (true) {
		synchronized (MyRunnable.class) {
			if (ticket == 100){
				break;
			} else {
				ticket ++;
				System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
			}
		}
	}
}
}



更改成为同步方法的格式为:
public class MyRunnable implements Runnable{

    int ticket = 0;

    /**
     * 执行的套路是:
     *  1. 循环
     *  2. 同步代码块
     *  3.判断共享数据是否到了末尾,到末尾的情况
     *  4. 判断共享数据是否到了末尾,没有到末尾的情况
     */

    @Override
    public void run() {
        while (true) {
            if (extracted()) break;
        }
    }

    /**
     * 其中的锁对象是惟一的,其中的对象表示的是this,即为当前的对象
     *
     * @return
     */
    private synchronized boolean extracted() {
        if (ticket == 100){
            return true;
        } else {
            ticket ++;
            System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
        }
        return false;
    }
}


通过创建接口对象,并且将接口对象作为参数进行传入的操作
     */
public class TicketDemo {
    public static void main(String[] args) {
        /**
         * 通过接口的实现,来进行操作,将接口名作为参数,创建线程
         */
        Runnable runnable = new MyRunnable();

        Thread thread0 = new Thread(runnable);
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);

        thread0.setName("窗口1");
        thread1.setName("窗口2");
        thread2.setName("窗口3");

        thread0.start();
        thread1.start();
        thread2.start();


    }
}

3.3 Lock锁

  1. Lock 实现提供的比 synchronized 方法和语句可以获取更为广泛的锁定操作
  2. Lock中提供了获的锁和释放锁的方法
    1. void lock() 获得锁
    2. void unlock() 释放锁
  3. lock接口是不能够进行直接的实例化操作的,这里采用的它的实现类ReentrantLock 来实例化 ReentrantLock的构造方法。类ReentrantLock() 创建一个类ReentrantLock的实例。
  4. 总结: 手动上锁,手释放锁。
手动锁的结构是:
    
static Lock mylock = new ReentrantLock();
mylock.lock;

try{
    
}finally{
    mylcok.unlock();
    // 如果在临界区的代码抛出一个异常,锁必须释放。否则其他的线程将永远阻塞。
    
    
}
这个结构的优点是确保任何时刻,都只有一个线程进入临界区,一旦一个线程锁定了锁对象,其他的任何线程都无法通过lock语句,当其他的语句调用lock语句的时候,他们会暂停,直到第一个线程释放这个锁对象。
出现的问题:
    1. 创建三个线程对象,lock对象的创建是在类里面
    2. 所以出现的是三种不同的锁,所以有可能出现的是没有加锁成功
    3. 改正的方法可以在前面加上  static 修饰符

public class MyThread extends Thread{

    /*表示的是这个类中的所有的对象,都在共享tickets的数据
     */
    static int ticket = 0;

    Lock lock = new ReentrantLock();

    @Override
    public void run() {
         while (true) {

             /*
               调用获得锁的方法操作,开始上锁
              */
             lock.lock();
             if (ticket < 100){
                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 ticket ++ ;
                 System.out.println(getName()+"正在卖"+ticket+"张票");
             } else {
                 break;
             }
             // 关锁的操作
             lock.unlock();
         }
    }
}
public class Demo1 {
    /**
     *检验多线程的问题
     *
     * @param args
     */
    public static void main(String[] args) {

        /*
         * 1. 创建线程对象
         */
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        MyThread myThread3 = new MyThread();
        /*
            通过调用方法起名字
         */
        myThread1.setName("窗口1");
        myThread2.setName("窗口2");
        myThread3.setName("窗口3");

        /*
        开启线程的操作
         */
        myThread1.start();
        myThread2.start();
        myThread3.start();

    }
}
改进之后的作用:
 

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 通过继承的方式操作线程
 */
public class MyThread extends Thread{

    /*表示的是这个类中的所有的对象,都在共享tickets的数据
     */
    static int ticket = 0;

    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
         while (true) {

             /*
               调用获得锁的方法操作,开始上锁
              */
             lock.lock();
             
             try {
                 if (ticket < 100){
                     Thread.sleep(100);
                     ticket ++ ;
                     System.out.println(getName()+"正在卖"+ticket+"张票");
                 } else {
                     break;
                 }
             } catch (InterruptedException e) {
                 e.printStackTrace();
             } finally {
	              // 关锁的操作
                 lock.unlock();
             }
            
         }
    }
}

3.4 死锁(错误)

​ 死锁实际上是:指的是两个或者两个以上的进程在执行的过程中,由于竞争资源或者由于彼此的铜线而造成的一种阻塞的现象。若没有外力的作用,它们无法推进下去。

3.4.1 死锁的四个必要的条件

  1. 互斥条件:在一定的时间某种资源只有一个线程占用。如果此时还有其他的进程请求资源,就只能够等待,直到占有资源的进程用完释放
  2. 占有且等待条件:指进程已经保持至少一个资源,但是又提出了新的资源的请求,而且该资源已经被其他进程占用,此时请求进程阻塞,但是对于自己已经获得的其他的资源保持不放
  3. 不可抢占资源:别人已经占有额某项资源,你不能够因为自己也需要该资源,就去把别人的资源抢过来。
  4. 循环等待的条件:若干个进程之间形成一种头尾相接的循环等待的资源关系。

3.4.2 如何避免死锁

  1. 避免一个线程同时获得多个锁
  2. 避免一个线程在锁内同时占用多个资源,尽量保持每个锁只占用一种资源
  3. 尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制

3.5 线程安全

  • 临界资源:共享资源(同一个对象),一次只可以有一个线程操作,才能够保证准确性
  • 原子操作:不可拆分的步骤,被视为一个整体,其中的步骤不能够被打乱。

3.6 线程不安全

  1. 完整的步骤可能会被破坏
  2. 线程内的数据可能会被别的线程修改

举例

public class Printer extends Thread{

    // 锁需要是同一对象
    private Object obj = new Object();

    // 打印机1号
    public synchronized void print1() {
        //synchronized (obj){
        System.out.print(1+" ");
        System.out.print(2+" ");
        System.out.print(3+" ");
        System.out.print(4+" ");
        System.out.print("\r\n");
        //}
    }


    // 打印机2号
    public synchronized void print2() {
        //synchronized (obj) {
        System.out.print("一 ");
        System.out.print("二 ");
        System.out.print("三 ");
        System.out.print("四 ");
        System.out.print("\r\n");
        //}
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Printer printer = new Printer( );

        // 线程1
        new Thread(() -> {
            while(true){
                printer.print1();
            }
        }).start();

        // 线程2
        new Thread(() -> {
            while(true){
                printer.print2();
            }
        }).start();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/404236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

33--Vue-前端开发-使用Vue脚手架快速搭建项目

一、vue脚手架搭建项目 node的安装: 官方下载,一路下一步 node命令类似于python npm命令类似于pip 使用npm安装第三方模块,速度慢一些,需换成淘宝镜像 以后用cmpm代替npm的使用 npm install -g cnpm --registry=https://registry.npm.taobao.org安装脚手架: cnpm inst…

汉诺塔--课后程序(Python程序开发案例教程-黑马程序员编著-第6章-课后作业)

实例3&#xff1a;汉诺塔 汉诺塔是一个可以使用递归解决的经典问题&#xff0c;它源于印度一个古老传说&#xff1a;大梵天创造世界的时候做了三根金刚石柱子&#xff0c;其中一根柱子从下往上按照从大到小的顺序摞着64片黄金圆盘&#xff0c;大梵天命令婆罗门把圆盘从下面开始…

C++回顾(二十)—— vector容器 和 deque容器

20.1 vector容器 20.1.1 vector容器简介 vector是将元素置于一个动态数组中加以管理的容器。vector可以随机存取元素&#xff08;支持索引值直接存取&#xff0c; 用[]操作符或at()方法&#xff09;。vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比…

es6的class(类)

目录 一、class&#xff08;类&#xff09;的语法 二、代码 三、效果 一、class&#xff08;类&#xff09;的语法 ES6 提供了更接近传统语言的写法&#xff0c;引入了 Class&#xff08;类&#xff09;这个概念&#xff0c;作为对象的模板。通过class关键字&#xff0c;可以…

Java基础(二):原码、反码、补码及进制之间的运算

Java基础系列文章 Java基础(一)&#xff1a;语言概述 Java基础(二)&#xff1a;原码、反码、补码及进制之间的运算 Java基础(三)&#xff1a;数据类型与进制 目录一、不同进制的表示方式二、二进制三、进制之间的转换四、byte的取值范围一、不同进制的表示方式 所有数字在计…

Leetcode 141.环形链表 142环形链表II

141环形链表 文章目录快慢指针快慢指针 代码思路&#xff1a; slow 和fast 指向 head slow走一步&#xff0c;fast走两步 没有环&#xff1a; fast每次走2步 &#xff0c;如果 fast 最终遇到NULL(链表中的元素是 偶数)或者fast->next(链表中的元素是 奇数)遇到NULL&#xf…

【ArcGIS Pro二次开发】(12):txt文件和Excel文件的读写

在Arcgis Pro的工作流中&#xff0c;数据的输入是很常见的。这里以TXT和Excel两种文件为例&#xff0c;在SDK中实现数据的读取和写入。 一、txt文件的读写 txt文件的读写相对简单&#xff0c;可以用Arcgis Pro自带的OpenItemDialog打开txt文件&#xff0c;并直接读取&#xff…

浙江大学海宁IMBA提面经验分享

先来介绍一下我的个人情况&#xff1a;本人毕业于浙江一所普通的本科院校&#xff0c;毕业已经6年了&#xff0c;在一家互联网公司担任市场部经理。其实在参加浙大IMBA项目提面之前&#xff0c;我也参加了浙大MBA项目的提面&#xff0c;可惜只拿到了良好的结果&#xff0c;所以…

力扣-每天的领导和合伙人

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1693. 每天的领导和合伙人二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.…

SWMM从入门到实践教程 04 快速入门案例的模拟执行

文章目录1 模拟时间的设置2 模拟执行3 报告查看3.1 完整报告3.2 总结报告4 纵断面查看5 结果播放1 模拟时间的设置 在左侧双击Options中的Dates&#xff0c;即可弹出时间的设置。此处为了教学&#xff0c;建议仅模拟6个小时&#xff0c;加快结果的生成。实际项目中&#xff0c;…

Gradle 的下载安装教程

Gradle 8.0.1 下载安装教程笔者的环境&#xff1a; Java 17.0.1 Gradle 8.0.1 Windows 10 教育版 64位 在继续阅读本教程之前&#xff0c;需要先完成 JDK 的安装。JDK 需要选择 8 及以上的版本。关于 JDK 的安装&#xff0c;可见笔者的另一篇博客&#xff1a; Java 的下载安…

卷积神经网络之AlexNet

目录概述AlexNet特点激活函数sigmoid激活函数ReLu激活函数数据增强层叠池化局部相应归一化DropoutAlexnet网络结构网络结构分析AlexNet各层参数及其数量模型框架形状结构关于数据集训练学习keras代码示例概述 由于受到计算机性能的影响&#xff0c;虽然LeNet在图像分类中取得了…

【轻轻松松学MongoDB】操作命令02:插入/删除

文档&#xff08;document&#xff09;的数据结构和 JSON 基本一样。所有存储在集合中的数据都是 BSON 格式。 一、文档插入 db.collection.insert( <document or array of documents>, { writeConcern: <document>, ordered: <boolean> } ) 例子&#xff…

【编程基础】009.输入两个正整数m和n,求其最大公约数和最小公倍数。

最大公约数与最小公倍数 题目描述 输入两个正整数m和n&#xff0c;求其最大公约数和最小公倍数。 输入格式 两个整数 输出格式 最大公约数&#xff0c;最小公倍数 样例输入 5 7 样例输出 1 35 题目思路 在这里我们用m表示较大的那个数&#xff0c;n表示较小的数。求…

三分钟拥有自己的 chat-gpt (开发到上线)

三分钟拥有自己的 chat-gpt (开发到上线) 首先你需要有一个 laf 账号&#xff0c;如果你还不知道 laf 是什么&#xff0c;点击这里三分钟学会然后你还需要有一个 chat-gpt 的账号并且生成一个 apiKey (这一步可以问 Google ) 云函数 具备了上面这两个条件我们就可以开始啦。…

CNCF x Alibaba云原生技术公开课 第八章 应用配置管理

Pod配置管理分类 可变配置就用 ConfigMap&#xff1b;敏感信息是用 Secret&#xff1b;身份认证是用 ServiceAccount&#xff1b;资源配置是用 Resources&#xff1b;安全管控是用 SecurityContext&#xff1b;前置校验是用 InitContainers。 1、ConfigMap 概念&#xff1a;…

Java高级技术:单元测试、反射、注解

目录 单元测试 单元测试概述 单元测试快速入门 单元测试常用注解 反射 反射概述 反射获取类对象 反射获取构造器对象 反射获取成员变量对象 反射获取方法对象 反射的作用-绕过编译阶段为集合添加数据 反射的作用-通用框架的底层原理 注解 注解概述 自定义注解 …

堆的结构与实现

堆的结构与实现二叉树的顺序结构堆的概念及结构堆的实现堆的创建向上调整建堆向下调整建堆堆的操作链接二叉树的顺序结构 堆其实是具有一定规则限制的完全二叉树。 普通的二叉树是不太适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费。而完全二叉树会更适合使用顺…

C++语法规则3(C++面向对象)

多态 C多态意味着调用成员函数时&#xff0c;会根据调用函数的对象的类型来执行不同的函数&#xff1b; 形成多态必须具备三个条件&#xff1a; 必须存在继承关系&#xff1b;继承关系必须有同名虚函数&#xff08;其中虚函数是在基类中使用关键字 virtual 声明的函数&#…

网络编程、通信

目录 网络通信三要素 三要素概述、要素一&#xff1a;IP地址 IP地址操作类-InetAddress 要素二&#xff1a;端口号 要素三&#xff1a;协议 UDP通信 UDP通信&#xff1a;快速入门 UDP通信&#xff1a;多发多收 UDP通信-广播、组播 TCP通信-快速入门 编写客户端代码 …