学习笔记071——Java中的【线程】

文章目录

  • 1、基础
  • 2、进程和线程
  • 3、什么是多线程
  • 4、Java 中线程的使用
  • 5、Java 中创建线程的方式
    • 5.1、继承 Thread 类
    • 5.2、实现 Runnable 接口
    • 5.3、继承 Thread 和实现 Runnable 接口的区别
    • 5.4、实现 Runnable 接口的优化
  • 6、线程的状态
  • 7、线程调度
    • 7.1、线程休眠
    • 7.2、线程合并
    • 7.3、线程礼让
  • 8、线程同步
    • 8.1、线程同步的实现
  • 9、线程安全的单例模式
    • 9.1、字符串常量池
    • 9.2、包装类常量池
  • 10、死锁
  • 11、重入锁

1、基础

提升程序性能非常重要的一种方式,使用多线程可以让程序充分利用 CPU 的资源,提高 CPU 的使用率。

多线程的优点:

1、CPU 资源得到更合理的利用

2、程序设计更加简洁

3、程序响应更快,运行效率更高

多线程的缺点:

1、需要更多的内存空间来支持多线程

2、多线程并发访问可能会影响数据的准确性

3、数据被多线程共享可能会出现死锁

2、进程和线程

什么是进程?计算机正在运行的一个独立的应用程序。

什么是线程?线程是组成进程的基本单位,可以完成特定的功能,一个进程是由一个或多个线程组成。

进程和线程都是应用程序在执行过程中的概念,动态的,如果应用程序没有运行起来,那么就不存在进程或线程的概念。

进程和线程的区别?

进程在运行的时候,会有自己独立的内存空间,每个进程所占用的内存都是独立的,互不影响。

多个线程是共享内存空间的,但是线程的执行是相互独立的,线程必须依赖于进程才能执行,单独的线程是无法执行的,由进程来控制多个线程的执行。

3、什么是多线程

在同一个进程中,多个线程同时执行,这里的同时执行是一个假象,并不是真正的同时执行。

系统会为每个线程分配 CPU 资源,在某个具体的时间段内 CPU 资源会被某个线程所占用,在下一个时间段内被另外一个线程所占用,多个线程交替占用 CPU 资源,因为线程运行速度很快,所以看起来是同时在执行的。

package com.htl.test;

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println("++++++++++main");
        }

        Test2 test2 = new Test2();
        test2.test();

    }
}

Java 程序的 main 方法就是一个主线程,无论在 main 方法中创建多个对象,调用多少个方法,它们都是单线程,有主线程来依次排队完成多个任务的执行。

程序中写的业务代码可以看作是一个个任务,这些任务是需要通过线程来执行的。

package com.htl.test;

public class Test {
    public static void main(String[] args) {

        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println("thread------------------");
            }
        }).start();

        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println("===============OK================");
            }
        }).start();

    }
}

取快递的例子

package com.htl.test;

public class Express {
    private Integer id;

    public Express(Integer id) {
        this.id = id;
    }

    public void get() {
        System.out.println("开始取快递...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("完成取快递...");
    }
}
package com.htl.test;

public class Test3 {
    public static void main(String[] args) {
        //100个快递,单线程
        //100S全部取完
        for (int i = 0; i < 100; i++) {
            Express express = new Express(i);
            express.get();
        }
//        for (int i = 0; i < 100; i++) {
//            Express express = new Express(i);
//            new Thread(()->{
//                express.get();
//            }).start();
//        }
    }
}

4、Java 中线程的使用

Java 中创建线程有几种方式?

  • 继承 Thread
  • 实现 Runnable
  • 实现 Callable

继承 Thread

Thread 类是 JDK 提供的专门用来创建线程对象的类。

创建一个自定义的线程类,继承 Thread

线程和任务

任务就是具体的事情,线程是完成这件事情的具体的对象

张三去取快递

取快递就是任务

张三就是线程

在这里插入图片描述

Java 中的 Thread 类如何绑定任务?

把当前这个线程要执行的任务定义到 run 方法中即可。

package com.htl.test2;

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread.........");
        }
    }
}
package com.htl.test2;

public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //启动
        myThread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("+++++++++++++main++++++++++++++++++");
        }
    }
}

注意:创建子线程对象之后,一定要调用 start 方法,才能让线程启动,去争夺 CPU 资源,才是多线程,如果调用 run 方法,那么线程根本没有启动,不会去争夺 CPU 资源,所以仍然是单线程。

5、Java 中创建线程的方式

5.1、继承 Thread 类

package com.htl.test2;

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread.........");
        }
    }
}
package com.htl.test2;

public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //启动
        myThread.start();
    }
}

5.2、实现 Runnable 接口

package com.htl.test3;

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}
package com.htl.test3;

public class Test {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

1、自定义一个类,实现 Runnable 接口。

2、创建一个 Thread,将第一步创建的 Runnable 对象注入到 Thread 对象中,启动 Thread 对象。

Java 中线程的使用需要两个参与角色,一个是线程,一个是任务。

任务是描述要做的事情,线程是具体执行任务的对象。

张三取快递

张三就是线程 Thread

取快递就是任务 Runnable

5.3、继承 Thread 和实现 Runnable 接口的区别

无论哪种形式,最终都要将任何和线程进行集成

集成 Thread 的方式在定义类的时候已经将线程和任务整合到了一起

实现 Runnable 接口的方式是将任务和线程分开的,使用的时候再整合到一起

继承

实现接口

实际开发中推荐使用实现接口的方式进行开发,可以做到解耦合。

5.4、实现 Runnable 接口的优化

1、单独定义一个类实现接口

package com.htl.test4;

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
        }
    }
}
package com.htl.test4;

public class Test {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

2、不用单独定义一个类,而使用内部类

package com.htl.test4;

public class Test2 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
        }
    }
}

3、匿名内部类

package com.htl.test4;

public class Test3 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "--" + i);
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
    }
}

4、lambda 表达式

package com.htl.test4;

public class Test4 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
        });
        thread.start();
    }
}

Lambda 表达式是指将方法的实现作为参数进行传递

某个类中,需要使用到接口,构造器中需要传入接口来创建对象

接口只能有一个抽象方法,因为如果有多个方法,那么 Lambda 传入的方法实现就无法确定到底是接口中的哪个方法了。

6、线程的状态

线程一共有 5 种状态,线程可以在不同的状态之间进行切换。

  • 创建状态:实例化一个新的线程对象,还未启动。
new Thread();
  • 就绪状态:启动线程对象,调用 start() 方法,进入线程池等待抢占 CPU 资源。
new Thread().start();
  • 运行状态:线程对象获取到了 CPU 资源,在某个时间段内执行任务。

  • 阻塞状态:正在运行的线程暂停执行任务,释放所占用的 CPU 资源,并且在解除阻塞状态之后也不能直接回到运行状态,而是重新回到就绪状态,等待获取 CPU 资源。

  • 终止状态:线程运行完毕或者因为异常导致线程中断。

在这里插入图片描述

package com.htl.test6;

public class Test {
    public static void main(String[] args) {
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-------"+i);
            }
        },"张三").start();

        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(i + "================" + Thread.currentThread().getName());
            }
        },"李四").start();
    }
}

在这里插入图片描述

7、线程调度

7.1、线程休眠

让线程暂停执行,从运行状态进入阻塞状态,让出 CPU 资源给其他线程。

调用 sleep 方法实现线程休眠。

sleep(long millis)

package com.htl.test6;

public class Test2 {
    public static void main(String[] args) {
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
                if(i == 50){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}
package com.htl.test6;

public class Test2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
        });
        try {
            thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("======main======");
        }

    }
}

上述代码表示,先等待 2 s ,然后主线程的循环和子线程的循环交替执行。

package com.htl.test6;

public class Test2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        });

        thread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("======main======");
        }

    }
}

直接运行主线程的循环,再每隔 2s 执行一次子线程的循环。

package com.htl.test6;

public class Test2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
        });

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

        thread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("======main======");
        }

    }
}

直接运行主线程的循环,间隔 2s ,运行子线程的循环。

sleep 方法在哪调用,就是让当前线程休眠,无论通过哪种形式调用

主线程做了两件事

1、启动子线程

2、循环

7.2、线程合并

将指定的某个线程加入到当前线程中,合并为一个线程,由原本的两个线程交替执行变成一个线程中的两个任务顺序执行。

join()

线程甲和线程乙,线程甲在执行到某个时间节点的时候,调用线程乙的 join 方法,从当前时间节点开始,CPU 资源全部交给线程乙,被它独占,线程甲就进入阻塞状态,直到乙执行完毕,甲进入就绪状态,继续等待争夺 CPU 资源。

package com.htl.test;

public class Test {
    public static void main(String[] args) {
        JoinRunnbale joinRunnbale = new JoinRunnbale();
        Thread thread = new Thread(joinRunnbale);
        thread.start();
        for (int i = 0; i < 100; i++) {

            if(i == 20){
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println(i + "+++++++++++++++main");
        }
    }
}

/**
 * 任务类
 */
class JoinRunnbale implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i + "-----------------JoinRunnable");
        }
    }
}

join() 存在重载:join(long millis),某个时间节点调用了线程乙的 join(long millis),从这一时刻起,CPU 资源被乙独占,线程甲进入阻塞状态,持续时间为 millis,到点之后,无论乙是否执行完毕,它不再独占资源,而是恢复两个线程争夺资源。

package com.htl.test;

public class Test {
    public static void main(String[] args) {
        JoinRunnbale joinRunnbale = new JoinRunnbale();
        Thread thread = new Thread(joinRunnbale);
        thread.start();
        for (int i = 0; i < 100; i++) {
            if(i == 20){
                try {
                    thread.join(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(i + "+++++++++++++++main");
        }
    }
}

/**
 * 任务类
 */
class JoinRunnbale implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i + "-----------------JoinRunnable");
        }
    }
}

7.3、线程礼让

在某个时间点,让线程暂停抢占 CPU 资源的行为,将 CPU 资源让给其他线程使用。

只是某个时间点(瞬间)礼让一次,过了这个时间点,又开始参与争夺 CPU 资源。

yield()

package com.htl.test;

public class Test2 {
    public static void main(String[] args) {
        YieldThread1 thread1 = new YieldThread1();
        thread1.setName("线程甲");
        YieldThread2 thread2 = new YieldThread2();
        thread2.setName("线程乙");
        thread1.start();
        thread2.start();
    }
}

class YieldThread1 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i == 5){
                Thread.yield();
            }
            System.out.println(Thread.currentThread().getName() + "-----------" + i);
        }
    }
}

class YieldThread2 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-----------" + i);
        }
    }
}

8、线程同步

8.1、线程同步的实现

异步和同步

有两个任务 A 和 B

异步指 A 和 B 同时进行,互不影响。

同步指 A 和 B 顺序执行,同一时间内只能执行一个任务,排队。

线程同步存在的意义是什么?

当多个线程同时访问一个共享数据的时候,可能会造成数据的不安全。

package com.htl.test2;

public class Test {
    public static void main(String[] args) {
        Account account = new Account();
        Thread thread1 = new Thread(account,"张三");
        Thread thread2 = new Thread(account,"李四");
        thread1.start();
        thread2.start();
    }
}

class Account implements Runnable{

    private static int num;

    @Override
    public void run() {
        num++;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "是当前的第" + num + "位访客");
    }
}

在这里插入图片描述

上述就是多个线程同时访问共享数据造成的数据不安全,如何解决?

只需要让多个线程排队(同步)即可。

最简单的同步方式就是使用 synchronized 关键字来实现。

相当于给资源上锁,要访问资源就必须拿到锁,如果锁被其他线程拿走了,那么当前线程就只能处于阻塞状态,等待锁的释放。

package com.htl.test2;

public class Test {
    public static void main(String[] args) {
        Account account = new Account();
        Thread thread1 = new Thread(account,"张三");
        Thread thread2 = new Thread(account,"李四");
        thread1.start();
        thread2.start();
    }
}

class Account implements Runnable{

    private static int num;

    @Override
    public synchronized void run() {
        num++;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "是当前的第" + num + "位访客");
    }
}

synchronized 可以修饰实例方法(非 static),也可以修饰静态方法,两者有区别。

synchronized 除了可以锁定方法之外,还可以锁定代码块。

package com.htl.test3;

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(()->{
//                Test.test();
                Test test = new Test();
                test.test();
            });
            thread.start();
        }
    }

    public void test(){
        synchronized (this){
            System.out.println("start...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end...");
        }
    }
}
package com.htl.test3;

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(()->{
                Test.test();
            });
            thread.start();
        }
    }

    public static void test(){
        synchronized (Test.class){
            System.out.println("start...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end...");
        }
    }
}

synchronized 关键字是用来实现线程同步的,可以锁方法,也可以锁代码块,无论锁谁,底层的原理都是一样的,就是看锁定的东西在内存中是一个还是多个,如果是一个,就会实现同步,5 个人 1 个厕所。

如果锁定的东西内存中是多个,就不会同步,5 个人 5 个厕所,同时进行。

9、线程安全的单例模式

一个类只能有一个实例化对象,由多个线程共享该对象资源。

package com.htl.test;

public class SingletonDemo {

    private static SingletonDemo instance;

    public static SingletonDemo getInstance(){
        if(instance == null){
            instance = new SingletonDemo();
        }
        return instance;
    }

    private SingletonDemo(){

    }


}
package com.htl.test;

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(SingletonDemo.getInstance());
        }
    }
}

多线程情况下的单例模式,需要用 synchronized 关键字

package com.htl.test;

public class SingletonDemo {

    private static SingletonDemo instance;

    public synchronized static SingletonDemo getInstance(){
        if(instance == null){
            instance = new SingletonDemo();
        }
        return instance;
    }

    private SingletonDemo(){
        System.out.println("创建了SingletonDemo对象");
    }

}

9.1、字符串常量池

内存中的一块特定的区域,专门用来存放字符串对象,为了节省空间。

String str = new String(“abc”);

String str2 = new String(“abc”);

通过构造器的方式创建字符串对象,直接将创建好的对象在堆内存中存放,不考虑是否有重复,而使用字符串常量池,获取一个对象的时候,首先在字符串常量池中查找是否存在,如果存在,直接返回,否则再创建新的对象。

9.2、包装类常量池

跟字符串常量池类似,使用包装类对象的时候也可以使用常量池来节约资源。

除了通过构造器,其他创建对象的方式都会使用常量池。

包装类使用常量池有取值区间的限制,-128到127之间会使用常量池,一旦超过这个范围,不会使用常量池,而是使用堆。

10、死锁

在这里插入图片描述

几个人围着一张桌子吃饭,每人发一根筷子,要求每个人必须凑够两根筷子才能吃饭,每个人都希望得到别人的筷子,同时都不愿意把自己的筷子让出来,就形成一个互斥的状态,导致程序无法执行,卡住。

package com.htl.test2;

public class Data {
}
package com.htl.test2;

public class DeadLockRunnable implements Runnable {
    public int num;
    private static Data data1 = new Data();
    private static Data data2 = new Data();
    @Override
    public void run() {
        if(num == 1){
            System.out.println(Thread.currentThread().getName() + "获取到了data1,等待获取data2");
            //获取data1
            synchronized (data1){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //获取data2
                synchronized (data2){
                    System.out.println(Thread.currentThread().getName() + "用餐完毕");
                }
            }
        }
        if(num == 2){
            System.out.println(Thread.currentThread().getName() + "获取到了data2,等待获取data1");
            synchronized (data2){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (data1){
                    System.out.println(Thread.currentThread().getName() + "用餐完毕");
                }
            }
        }
    }
}
package com.htl.test2;

public class Test {
    public static void main(String[] args) {
        DeadLockRunnable runnable1 = new DeadLockRunnable();
        runnable1.num = 1;
        DeadLockRunnable runnable2 = new DeadLockRunnable();
        runnable2.num = 2;
        new Thread(runnable1,"张三").start();
        new Thread(runnable2,"李四").start();
    }
}

如何解决死锁问题,不要让线程同时执行,按照先后顺序来执行,不要构成互斥条件。

package com.htl.test2;

public class Test {
    public static void main(String[] args) {
        DeadLockRunnable runnable1 = new DeadLockRunnable();
        runnable1.num = 1;
        DeadLockRunnable runnable2 = new DeadLockRunnable();
        runnable2.num = 2;
        new Thread(runnable1,"张三").start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(runnable2,"李四").start();
    }
}

11、重入锁

ReentrantLock 是对 synchronized 的升级,synchronized 是通过 JVM 实现,ReentrantLock 是通过 JDK 实现,让开发者可以用面向对象的思想来操作同步锁。

重入锁是指可以多次上锁,上锁和解锁方式和 synchronized 不同

synchronized 是自动上锁,自动解锁,只需要添加关键字,不需要开发者手动操作上锁和解锁。

ReentrantLock 需要开发者手动上锁、手动解锁。

package com.htl.test3;

import java.util.concurrent.locks.ReentrantLock;

public class Account implements Runnable {

    private static int num;
    private ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        //上锁
        reentrantLock.lock();
        num++;
        System.out.println(Thread.currentThread().getName() + "是当前的第" + num + "位访客");
        //解锁
        reentrantLock.unlock();
    }
}
package com.htl.test3;

public class Test {
    public static void main(String[] args) {
        Account account = new Account();
        new Thread(account).start();
        new Thread(account).start();
    }
}

使用 ReentrantLock 可以让锁定的粒度更细,不需要上锁的代码就可以分离出来。

ReentrantLock 除了在使用上更加灵活之外,还具备限时性的特点,是指在等待某个资源的时候可以设置一个等待时间,一旦超出这个时间还没有拿到锁,则中断线程,不会一直等下去,而 sychronized 就会一直等下去。

tryLock(long time,TimeUnit unit)

package com.htl.test3;

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

public class TimeLock implements Runnable {

    private ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        try {
            if(reentrantLock.tryLock(3, TimeUnit.SECONDS)){
                System.out.println(Thread.currentThread().getName() + "获取到了锁");
                Thread.sleep(5000);
            }else{
                System.out.println(Thread.currentThread().getName() + "没有拿到锁");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(reentrantLock.isHeldByCurrentThread()){
                reentrantLock.unlock();
            }
        }
    }
}
package com.htl.test3;

public class Test {
    public static void main(String[] args) {
        TimeLock timeLock = new TimeLock();
        new Thread(timeLock,"张三").start();
        new Thread(timeLock,"李四").start();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

devops-部署Harbor实现私有Docker镜像仓库

文章目录 概述下载配置安装安装后生成的文件使用和维护Harbor参考资料 概述 Harbor是一个开源注册中心&#xff0c;它使用策略和基于角色的访问控制来保护工件&#xff0c;确保镜像被扫描并且没有漏洞&#xff0c;并将镜像签名为可信的。Harbor是CNCF的一个毕业项目&#xff0…

快速上手Neo4j图关系数据库

参考视频&#xff1a; 【IT老齐589】快速上手Neo4j网状关系图库 1 Neo4j简介 Neo4j是一个图数据库&#xff0c;是知识图谱的基础 在Neo4j中&#xff0c;数据的基本构建块包括&#xff1a; 节点(Nodes)关系(Relationships)属性(Properties)标签(Labels) 1.1 节点(Nodes) 节点…

Polkadot 11 月生态月报:3900万交易量、69%增长率,技术与社区齐头并进

原文&#xff1a;https://x.com/Polkadot/status/1865118662069490074 编译&#xff1a;OneBlock 上个月对 Polkadot 生态来说可谓是跌宕起伏&#xff0c;从创下交易记录到开创性合作&#xff0c;Polkadot 热度不断。展现出强大的技术实力和蓬勃发展的社区活力。在回顾本月亮点…

基坑表面位移沉降倾斜自动化监测 非接触式一体化解决机器视觉

基于变焦视觉位移监测仪的基坑自动化监测新方案是一种集成了光学、机械、电子、边缘计算、AI识别以及云平台软件等技术的自动化系统。该方案利用变焦机器视觉原理&#xff0c;结合特殊波段成像识别技术和无源靶标&#xff0c;实现了非接触式大空间、多断面、多测点的高精度水平…

CSS学习-第三天

css链接 链接样式&#xff0c;可以使用任何css属性 特别的样式&#xff0c;可以有不同的样式 a:link - 正常&#xff0c;未访问过的链接 a:visited - 用户已访问过的链接 a:hover - 当用户鼠标放在链接上时 a:active - 链接被点击的那一刻 a:hover必须跟在a:link和a:visited后…

电脑显示器选购指南2024

选择显示器是五花八门的显示参数&#xff0c;如何选择&#xff0c;以下给出参数说明&#xff0c;及部分参考&#xff1a; 1. 尺寸和分辨率 尺寸&#xff08;英寸&#xff09; 根据使用距离和用途选择合适的屏幕尺寸&#xff1a; 21-24 英寸&#xff1a;适合小桌面空间、日常…

快速掌握C语言——数据结构【创建顺序表】多文件编译

1.数据结构脑图&#xff08;未完&#xff09; 2.顺序表其他操作 3.功能函数封装 4 完整代码 1>头文件test.h #ifndef __TEST_H__ #define __TEST_H__#include<stdlib.h> #include<stdio.h> #include<string.h>//宏定义 线性表的最大容量 #define MAX 3…

Linux 中的 mkdir 命令:深入解析

在 Linux 系统中&#xff0c;mkdir 命令用于创建目录。它是文件系统管理中最基础的命令之一&#xff0c;广泛应用于日常操作和系统管理中。本文将深入探讨 mkdir 命令的功能、使用场景、高级技巧&#xff0c;并结合 GNU Coreutils 的源码进行详细分析。 1. mkdir 命令的基本用法…

STM32F407+LAN8720A +LWIP +FreeRTOS UDP通讯

STM32F407+LAN8720A +LWIP +FreeRTOS ping通 上一篇实现了LWIP ping 通 本篇实现UDP通讯 实现如下功能: 串口1空闲中断+DMA接收,收到数据用UDP发送UDP接收,收到数据用串口1发送STM32CUBEIDE配置和代码 1. 配置UARAT1的空闲中断+DMA接收 UART1接收到数据,释放信号量,在任…

【蓝桥杯选拔赛真题94】Scratch巡逻的直升机 第十五届蓝桥杯scratch图形化编程 少儿编程创意编程选拔赛真题解析

目录 scratch巡逻的直升机 一、题目要求 编程实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、python资料 sc…

非前后端分离项目,通过拦截器,对前端发送网络访问地址

一、背景 在非前后端分离的web应用开发过程中&#xff0c;在html中不可避免的需要请求项目地址去访问接口或者静态资源&#xff0c;但是开发环境、测试环境以及生产环境不同&#xff0c;地址也不同&#xff0c;避免频繁修改代码&#xff0c;可以通过自动获取访问地址通过拦截器…

人工智能大语言模型起源篇(一),从哪里开始

序言&#xff1a;许多人最初接触人工智能都是在ChatGPT火热之际&#xff0c;并且大多停留在应用层面。对于希望了解其技术根源的人来说&#xff0c;往往难以找到方向。因此&#xff0c;我们编写了《人工智能大语言模型起源篇》&#xff0c;旨在帮助读者找到正确的学习路径&…

FFmpeg功能使用

步骤&#xff1a;1&#xff0c;安装FFmpeg Download FFmpeg 在这里点击->Windows builds from gyan.dev&#xff1b;如下图 会跳到另外的下载界面&#xff1a; 在里面下拉选择点击ffmpeg-7.1-essentials_build.zip&#xff1a; 即可下载到FFmpeg&#xff1b; 使用&#…

【Unity基础】Unity中如何实现图形倒计时

为了在Unity中实现一个图形倒计时&#xff0c;除了代码部分&#xff0c;还需要一些UI元素的创建和设置。本文以环形倒计时为例&#xff0c;以下是完整的步骤&#xff0c;涵盖了如何创建UI元素、设置它们&#xff0c;以及如何编写控制环形倒计时进度的脚本。 1. 创建UI元素 创建…

2024 中央企业数据资产化及数据资产入表场景建设白皮书

本文介绍了中央企业数据资产化的重要性及其入表场景建设。白皮书详细阐述了数据资产化的概念、实施步骤和关键挑战&#xff0c;提出了一系列解决方案。文章还强调了数据资产入表的重要性&#xff0c;以提高数据的透明度和质量&#xff0c;推动企业数字化转型。 重点内容&#…

电力场景输电线本体分割数据集labelme格式1629张1类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;1629 标注数量(json文件个数)&#xff1a;1629 标注类别数&#xff1a;1 标注类别名称:["csv_ct"] 每个类别标注的框数&…

【机器学习】机器学习的基本分类-无监督学习-t-SNE(t-分布随机邻域嵌入)

t-SNE&#xff08;t-分布随机邻域嵌入&#xff09; t-SNE&#xff08;t-distributed Stochastic Neighbor Embedding&#xff09;是一种用于降维的非线性技术&#xff0c;常用于高维数据的可视化。它特别适合展示高维数据在二维或三维空间中的分布结构&#xff0c;同时能够很好…

哈尔滨工业大学《2024年801自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《哈尔滨工业大学801自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

数据可视化:提升年度报表分析效率的新路径

在当今复杂多变的商业环境中&#xff0c;企业年度报表不仅是反映企业过去一年经营成果的重要文件&#xff0c;更是指导未来战略规划的基石。它如同一面镜子&#xff0c;既映照出企业的辉煌成就&#xff0c;也不避讳地揭示了存在的问题与挑战。本文将从企业年度报表的编制原则、…

探秘Redis哨兵模式:原理、运行与风险全解析

一、引言 Redis 概述 在当今的数据存储领域&#xff0c;Redis 占据着十分重要的地位。它是一个内存中的数据存储&#xff0c;凭借其出色的性能和丰富的功能&#xff0c;被数百万开发人员广泛应用于诸多场景之中&#xff0c;已然成为构建高性能、可扩展应用程序的得力工具。 从…