JavaSE高阶篇-多线程问题研究

news2025/1/10 2:36:19

一、进程与线程

1)进程

程序:静态的,就是存放在磁盘里的可执行文件,是一系列指令的集合

进程:进入到内存中执行的应用程序,是系统运行的基本单位,有独立的运行空间

具体点说:一个程序如果被CPU多次读取到内存中,变成多个独立的进程。将程序运行起来,就称之为进程。进程是执行程序的一次性过程,他是动态的概念。进程存在生命周期,也就是说程序随着程序的终止而销毁。进程之间是通过TCP/IP端口实现交互的。

一句话总结:

一个应用程序(一个进程就是一个软件),一个程序至少包含一个进程,一个进程中至少包含一条线程

 

 【进程的三个状态】

进程在运行的过程中不断的改变其运行状态。通常一个运行的进程必须有三个状态,
就绪态 运行态 阻塞态
就绪态:当进程获取出 CPU 外所有的资源后,只要再获得 CPU 就能执行程序,这时的状态叫做就绪态。在一个系统中处于就绪态的进程会有多个,通常把这些排成一个队列,这个就叫就绪队列。
运行态:当进程已获得 CPU 操作权限,正在运行,这个时间就是运行态。在单核系统中,同一个时间只能有一个运行态,多核系统中,会有多个运行态。
阻塞态:正在执行的进程,等待某个事件而无法继续运行时,便被系统剥夺了 CPU 的操作权限,这时就 是阻塞态。引起阻塞的原因有很多,比如:等待 I/O 操作、被更高的优先级的进程剥夺了 CPU 权限等。

 2)线程

        是系统分配资源的基本单位,一个进程可以包含多个线程,堆空间和方法区共享,但是每个线程的栈空间和程序计数器是独立的,同样的线程消耗的资源也是小于进程的。
        线程是CPU 调度和执行的最小单位。
也可以讲,就是说,是进程中的执行单元,在进程下进行,作用是负责当前进程中的程序运行,一个进程中是可以有多个线程的,这样的应用程序称之为多线程,很多线程都是模拟出来的,真正的多线程是 指有多个 CPU 即,多核,如服务器,如果是模拟出来的多线程,即一个 CPU 的情况下,在同一个时间 点, CPU 只能执行一个代码,因为切换很快,所以就有同时执行的错觉

 

 对于java程序来说,当在DOS命令窗口中输入:

java HelloWorld 回车之后。会先启动 JVM ,而 JVM 就是一个进程。
JVM 再启动一个主线程调用 main 方法( main 方法就是主线程)。
同时再启动一个垃圾回收线程负责看护,回收垃圾。
注意 :使用多线程机制之后, main 方法结束只是主线程结束了,其他线程还没结束,但没有主线程也不能运行。最起码,现在的 java 程序中至少有两个线程并发,
        一个是 垃圾回收线程,一个是 执行main 方法的主线程

 总结:

1) 线程在进程下进行
2) 进程之间不会相互影响,主线程结束将会导致整个进程结束
3) 不同的进程数据很难共享,两个不同进程的内存也是不共享的,也是独立的
4) 同线程下的不同线程之间数据很容易共享
5) 进程使用内存地址可以限定使用量
6) 可以把进程看成是现实生活当中的公司,线程可以看作是公司当中的某个员工

 二、并发与并行与串行

1)并发

同一个时刻多个线程同时操作了同一个数据。
(这是一种假并行。即一个 CPU 的情况下,在同一个时间点, CPU 只能执行一个代码,因为切换的很快,所以就有同时执行的错觉)。
特点:同时安排若干个任务,这些任务可以彼此穿插着进行;有些任务可能是并行的,比如买菜、发邮 件和去洗脚的某些路是重叠的,这是确实是在做三件事;但进菜市场和发邮件和接娃三者是互斥的,每 个时刻只能完成一件(并发允许两个任务之间彼此干扰)

 2)并行

同一个时刻,多个线程同时执行了不同的程序
多个任务同时进行。并行必须有多核才能实现,否则只能是并发。
你(线程)做你的事,我(线程)做我的事,咱们互不干扰并同时进行。

 3)串行

一个程序处理完当前进程,按照顺序接着处理下一个进程,一个接着一个进行。

、CPU调度与主线程介绍

1)CPU调度

1 ):分时调度
        指的是,让所有的线程轮流获取CPU 的使用权,并且平均分配每个线程占用的 CPU 的时间片
2 ):抢占式调度
        指的是,多个线程轮流抢占CPU 的使用权,随机性比较高,哪个线程的优先级越高,先执行的几率就比较大,不是每次都是优先级高的线程先抢到 ------->Java 程序为抢占式

2)主线程介绍

/**专门运行main方法的线程叫做主线程**/
public class Demo01Thread {
    /**单线程程序**/
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println("我是一个for循环");
        }
        System.out.println(Math.abs(-1));
    }
}

 四、创建线程的方式

1)第一种方式_extends Thread

步骤:

​        1)创建一个类,继承Thread类

​        2)重写Thread中的run方法,设置线程任务(该线程要执行啥代码)

​        3)创建自定义的线程类对象

​        4)调用thread中的start方法(开启线程,JVM自动调用重写的run方法执行线程任务)

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("MyThread...执行了............."+i);
        }
    }
}
public class Demo02Thread {
    public static void main(String[] args) {
        //创建线程对象
        MyThread myThread = new MyThread();
        //调用Thread中的start方法,开启线程,jvm自动执行run方法
        myThread.start();
        myThread.start();//不能多次调用

        for (int i = 0; i < 10; i++) {
            System.out.println("main方法.............执行了"+i);
        }
    }
}

 2)Thread类中的方法

public void run() :                         此线程要执行的任务在此处定义代码。

public String getName() :             获取当前线程名称。

public void setName(线程名字):   给线程设置名字

public static Thread currentThread() :返回对当前正在执行的线程对象的引用。此方法在哪个线程中用获取的就是哪个线程对象

static void sleep(long millis) : 线程睡眠,设置的是毫秒值,如果超时,线程会自动醒来,继续执行 void start() 使该线程开始执行; Java 虚拟机调用该线程的 run 方法

 

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程开始执行了........"+i);
        }
    }
}
public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.setName("曼曼");
        myThread.start();//开启时才抢占,
        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000L);//降低for循环的执行速度,线程睡眠
            System.out.println(Thread.currentThread().getName()+"线程开始执行............"+i);
        }
    }
}

Thread.yield()方法:暂停当前正在执行的线程对象,把执行机会让給相同或者更高优先级的线程。

/**
 * 线程让步
 */
public class Text05 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 15; i++) {
                    Thread.yield();
                    System.out.println(Thread.currentThread().getName() + "-->" + i);
                }
            }
        });
        t.setName("t");
        t.start();

        // 主线程
        for (int i = 0; i < 15; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
            // 等待子线程执行完毕再输出
            if (i == 7) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

/**
运行结果:让主线程走完了才运行
**/
sleep() yield() 的区别】
1.sleep() 使当前线程进入停滞状态,所以执行 sleep() 的线程在指定的时间内肯定不会被执行; yield() 只是使当前线程重新回到可执行状态,所以执行 yield() 的线程有可能在进入到可执行状态后马上又被执 行。
2.sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的, yield 方法使当前线程让出 CPU 占有权,但让出的时间是不可设定的。
3. 实际上, yield() 方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把 CPU 的占有权交给此线程,否则,继续运行原来的线程。所以 yield() 方法称为 退让 ,它把运 行机会让给了同等优先级的其他线程
4. 另外, sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU 占有权。在一个运行系统中,如果较高优 先级的线程没有调用 sleep 方法,又没有受到 I\O 阻塞,那么,较低优先级线程只能等待所有较高优先 级的线程运行结束,才有机会运行。

补充:

Thread.join() 方法:用于等待其他线程终止
    如果线程A中调用了线程B的join方法,那么线程A阻塞,直到线程B执行完后,线程A从阻塞状态转为就绪状态,等待获取CPU的使用权。join方法要在start方法调用后调用才有效,线程必须启动,再加入。

 

package com.thread;


public class Text06 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new JoinThread("t1"));
        Thread t2 = new Thread(new JoinThread("t2"));
        t1.start();
        t1.join();
        t2.start();
    }
}

class JoinThread implements Runnable {
    private String name;

    // 构造函数
    public JoinThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(name + "-->" + i);
        }
    }
}

 主线程一定会等子线程都结束了才结束

3)第二种方式_实现Runnable接口

1.创建一个类,实现Runnable接口
2.重写run方法,设置线程任务
3.创建线程类对象
4.创建Thread对象,将线程类对象封装到Thread中   ->  Thread(Runnable target)
5.调用Thread中的start方法

 

 4)两种实现多线程方式的区别

1. 继承 Thread, 有局限性 , 因为继承支持单继承
2. 实现 Runnable, 解决了单继承的局限性
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runnable接口的话,则很容易的实现资源贡共享。

 【总结】:

 实现Runnable接口比继承Thread类具有的有优势,适合多个相同的程序代码的线程去处理统一资源,可以避免java中的单继承的限制增加程序的健壮性,代码可以被多个线程共享,代码和数据独立,线程池只能放入实现Runnable或callable类线程,不能直接放入继承Therad的类。

main方法其也是一个线程。在java中所有的线程都是同时启动的,至于什么时候、那哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实就是在操作系统中启动了一个进程。

 5)匿名内部类创建多线程

1.匿名内部类回顾:
  a.匿名内部类代表的是子类对象或者实现类对象
  b.格式1:
    new 接口/抽象类(){
        重写方法
    }.重写的方法();

  c.格式2:
    接口/抽象类 对象名 = new 接口/抽象类(){
        重写方法
    }
    对象名.重写的方法();
java

Java

public class Test02 {
    /**
     * 匿名内部类创建多线程的方式
     **/
    public static void main(String[] args) {
        // 创建并启动第一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        // 线程休眠100毫秒
                        Thread.sleep(100L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 输出线程名和执行次数
                    System.out.println(Thread.currentThread().getName() + "...执行了" + i);
                }
            }
        }, "杨童").start();

        // 创建并启动第二个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        // 线程休眠100毫秒
                        Thread.sleep(100L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 输出线程名和执行次数
                    System.out.println(Thread.currentThread().getName() + "...执行了" + i);
                }
            }
        }, "曼曼").start();
    }
}

 五、线程的生命周期

1 、新建状态( new ):新建一个线程对象
2 、就绪状态( Runnable ):线程对象创建后,其他线程调用了该对象的 start ()方法。该状态的线程
位于可运行的线程池中,变得可运行,等待获取 CPU 的使用权
3 、运行状态( Running ):就绪状态的线程获取了 CPU ,执行程序代码
4 、阻塞状态( Blocked ):阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直到线程进
入就绪状态,才有机会转到运行状态
阻塞的情况分为三种 :
等待阻塞:运行的线程执行 wait ()方法, JVM 会把该线程放入等待池中。( wait 方法会释放持有的锁)
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池中。
其他阻塞:运行的线程执行 sleep ()或 join ()方法,或者发出了 I/O 请求时, JVM 会把该线程程置为阻塞状态。当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪 状。( sleep 是不会释放持有的锁)
死亡状态( Dead ):线程执行完了或者因异常退出了 run ()方法,该线程结束生命周期。

 六、线程同步

1)概念

        即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多种。

2)为啥要创建多线程

        在一般情况下,创建一个线程是不能提高程序执行效率的,所以要创建多个线程。

3-为什么要线程同步

        多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。

4-什么叫线程同步

        同步就是协同步调,按预定的先后次序进行运行。如:你做完,我再做。

错误理解 :

        “同”字从字面上容易理解为一起动作,其实不是。“同”字应是指协同、协助、互相配合。

正确理解 :

        所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其他线程也不能调用这个方法。

线程同步的作用 :

        线程有可能和其他线程共享一些资源,比如:内存、文件、数据库等
        当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
        线程同步的真是意思其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

5-类锁和对象锁

(1)基本概念:

对象锁:在java中每个对象都有一个唯一的锁,对象锁用于对象实例方法或者一个对象实例上面的

                        —— 一个对象一把锁,100个对象100把锁。

类锁:是用于一个类静态方法或者class对象的,一个类的实例对象可以有多个,但是只有一个class对象

                        —— 100个对象,也只是1把锁。

注意上述第三种方式synchronized同步代码块实现:

在静态方法添加synchronized这把锁属于类了,所有这个类的对象都共享这把锁,这就叫类锁。

(2)释放锁的时机

        如果一个方法或者代码块被synchronized关键字修饰,当线程获取到该方法或代码块的锁,其他线程是不能继续访问该方法或代码块的。而其他线程想访问该方法或代码块,就必须要等待获取到锁的线程释放这个锁,而在这里释放锁只有两种情况 :

        线程执行完代码块,自动释放锁;

        程序报错,JVM让线程自动释放锁;

        如果一个方法或者代码块被synchronized关键字修饰,当线程获取到该方法或代码块的锁,其他线程是不能继续访问该方法或代码块的。而其他线程想访问该方法或代码块,就必须要等待获取到锁的线程释放这个锁,而在这里释放锁只有两种情况 :

        线程执行完代码块,自动释放锁;

        程序报错,JVM让线程自动释放锁;

public static void main(String[] args) {
		new Text07().myThread1(new Thread());
		new Text07().myThread2(new Thread());
	}

七、 线程安全问题

1)什么时候会发生线程安全问题

多个线程访问同一个资源(数据)时

2)问题

public class MyTicket implements Runnable{
    int ticket = 100;
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (ticket>0){//也有可能出现负数的情况
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
                ticket--;
            }
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        MyTicket myTicket = new MyTicket();
        Thread t1 = new Thread(myTicket,"曼曼");
        Thread t2 = new Thread(myTicket,"童童");
        Thread t3 = new Thread(myTicket,"雪雪");
        t1.start();
        t2.start();
        t3.start();
    }
}

3)解决方案1(同步代码块)

同步互斥访问(synchronized)

基本上所有解决线程安全问题的方式都是采用“序列化临界资源访问”的方式,即在同一时刻只有一个线程操作临界资源,操作完了才能让其他线程进行操作,也称同步互斥访问。

在Java中一般采用synchronized和Lock来实现同步互斥访问。

Synchronized关键字

首先了解一下互斥锁 :就是能达到互斥访问目的的锁

        1)线程有可能和其他线程共享一些资源,比如:内存、文件、数据库等
        2)当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
        3) 线程同步的真是意思其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

 synchronized同步代码块实现
1. 第一种:synchronized代码块方式(灵活)
2. 第二种:在实例方法上使用synchronized
3. 第三种 : 在静态方法上使用synchroniz

 1.

        1.关键字:synchronized
        2.格式:
             synchronized(任意对象){
                 可能出现线程不安全的代码
             }
      3.线程进了synchronized代码块相当于上锁了,其他线程只能在代码块外面等待,等到执行的线程执行完毕,出了synchronized,相当于将锁释放了,此时其他等待的线程才有机会拿到锁,上锁,进去执行
   4.注意:想要实现线程安全,线程同步,多个线程之间用的锁对象需要是同一个锁对象 

 

/**
 * 实现 Runnable 接口的 MyTicket 类,用于模拟多线程售票系统
 */
public class MyTicket implements Runnable {
    // 剩余票数
    int ticket = 100;
    // 创建对象锁
    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            try {
                // 线程休眠
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 同步代码块
            synchronized (new Object()) {
                if (ticket > 0) {
                    // 打印当前买票信息
                    System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
                    // 票数减一
                    ticket--;
                }
            }
        }
    }
}
/**

 * 测试类 Test01,用于测试多线程售票系统
   */
   public class Test01 {
   public static void main(String[] args) {
       // 创建 MyTicket 实例
       MyTicket myTicket = new MyTicket();
       // 创建线程并指定线程名
       Thread t1 = new Thread(myTicket, "曼曼");
       Thread t2 = new Thread(myTicket, "童童");
       Thread t3 = new Thread(myTicket, "雪雪");
       // 启动线程
       t1.start();
       t2.start();
       t3.start();
   }
   }

4)解决方案2(同步方法)

1.普通同步方法

1.格式:
  public synchronized 返回值类型 方法名(参数){
      可能出现线程不安全的代码
  }

2.默认锁:
  this

/**
 * 实现 Runnable 接口的 MyTicket 类,用于模拟多线程售票系统
 */
public class MyTicket implements Runnable {
    // 剩余票数
    int ticket = 100;

    @Override
    public void run() {
        while (true) {
            try {
                // 线程休眠
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            method();
        }
    }
    /**
     * 同步方法
     */
    public synchronized void method() {//可能出现线程不安全的代码
        /*// 注释掉原有的同步方法
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
            ticket--;
        }*/
        if (ticket > 0) {
            // 打印当前买票信息
            System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
            // 票数减一
            ticket--;
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        MyTicket myTicket = new MyTicket();
        Thread t1 = new Thread(myTicket,"曼曼");
        Thread t2 = new Thread(myTicket,"童童");
        Thread t3 = new Thread(myTicket,"雪雪");
        t1.start();
        t2.start();
        t3.start();
    }
}

2.静态同步方法

1.格式:
  public static synchronized 返回值类型 方法名(参数){
      可能出现线程不安全的代码
  }

2.默认锁:
  当前类.class

 

public class MyTicket implements Runnable {
    static int ticket = 100;

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            method();
        }
    }
     /* public static synchronized void method() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
            ticket--;
        }
    }*/

    public static void method(){
        synchronized (MyTicket.class){
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
                ticket--;
            }
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        MyTicket myTicket = new MyTicket();
        Thread t1 = new Thread(myTicket,"曼曼");
        Thread t2 = new Thread(myTicket,"童童");
        Thread t3 = new Thread(myTicket,"雪雪");
        t1.start();
        t2.start();
        t3.start();
    }
}

八、死锁

1.死锁(锁嵌套就有可能产生死锁)

指的是两个或者两个以上的线程在执行的过程中,由于竞争同步锁而产生的一种阻塞现象;如果没有外力的作用,他们将无法继续执行下去,这种情况就称之为死锁.

 

 根据上图所示:线程T1正在持有R1锁,但是T1线程必须再拿到R2锁,才能继续执行
而线程T2正在持有R2锁,但是T2线程需要再拿到R1锁,才能继续执行
此时两个线程处于互相等待的状态,就是死锁,在程序中的死锁将出现在同步代码块的嵌套中

 

 

public class LockA {
    public static LockA lockA = new LockA();
}

public class LockB {
    public static LockB lockB = new LockB();
}

/**
 * 循环死锁类 DieLock,实现了 Runnable 接口
 */
public class DieLock implements Runnable {
    private boolean flag;

    public DieLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            // 如果 flag 为 true,获取 lockA 锁
            synchronized (LockA.lockA) {
                System.out.println("if...lockA");
                // 获取 lockB 锁
                synchronized (LockB.lockB) {
                    System.out.println("if...lockB");
                }
            }
        } else {
            // 如果 flag 为 false,获取 lockB 锁
            synchronized (LockB.lockB) {
                System.out.println("else...lockB");
                // 获取 lockA 锁
                synchronized (LockA.lockA) {
                    System.out.println("else...lockA");
                }
            }
        }
    }
}

public class Test01 {
    public static void main(String[] args) {
        DieLock dieLock1 = new DieLock(true);
        DieLock dieLock2 = new DieLock(false);

        new Thread(dieLock1).start();
        new Thread(dieLock2).start();
    }
}

 直接卡死

九、线程状态

1)线程状态介绍

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中java.lang.Thread.State这个枚举中给出了六种线程状态:
  这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析。

 2)线程状态图

 可能不详细

再贴一张

 十、等待唤醒

练习1:

 

public class BaoZiPu {
//    定义一个count 计数
//    生产包子 count++   消费包子:直接输出count
    private int count;
    private boolean flag;
//    定义一个boolean的flag
//   flag = true----->证明有包子    flag = false----->证明没有包子
    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    /**
     * getCount专门给消费线程用
     */
    public void getCount() {
        System.out.println("消费了第......"+count+"个包子");
    }

    /**
     * setCount专门给生产线程生产包子用
     */
    public void setCount() {
        count++;
        System.out.println("生成了第..."+count+"个包子");
    }
    public boolean isFlag() {//看看是不是有包子
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

 

public class Product implements Runnable {
    private BaoZiPu baoZiPu;
    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (baoZiPu) {
                try {
                    Thread.sleep(100L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                //1.判断flag如果是true,证明有包子,生产线程等待
                if (baoZiPu.isFlag() == true) {
                    try {
                        baoZiPu.wait();//必须要锁对象调用
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //2.如果flag为false,证明没有包子,需要生产包子
                baoZiPu.setCount();
                //3.改变flag状态,为true,证明有包子了
                baoZiPu.setFlag(true);
                //4.唤醒消费线程
                baoZiPu.notify();//必须要锁对象调用
            }
        }
    }
}
public class Consumer implements Runnable {
    private BaoZiPu baoZiPu;
    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (baoZiPu) {
                try {
                    // 线程休眠
                    Thread.sleep(100L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // 1.判断标志 flag,如果为 false,表示没有包子,消费线程等待
                if (!baoZiPu.isFlag()) {
                    try {
                        baoZiPu.wait(); // 等待并释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 2.如果 flag 为 true,表示有包子,需要消费包子
                baoZiPu.getCount();

                // 3.改变标志 flag 状态为 false,表示没有包子了
                baoZiPu.setFlag(false);

                // 4.唤醒生产线程
                baoZiPu.notify(); // 唤醒在此对象监视器上等待的单个线程
            }
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();
        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);
        new Thread(product).start();
        new Thread(consumer).start();
    }
}

同步方法改造写法

public class BaoZiPu {
    private int count;
    private boolean flag;

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    /**
     * getCount专门给消费线程用
     * @return
     */
    public synchronized void getCount() {
        //1.判断flag如果是false,证明没有包子,消费线程等待
        if (flag == false) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //2.如果flag为true,证明有包子,需要消费包子
        System.out.println("消费了第......" + count + "个包子");
        //3.改变flag状态,为false,证明没有包子了
        flag = false;
        //4.唤醒生产线程
        this.notify();
    }

    /**
     * setCount专门给生产线程生产包子用
     *
     * @param
     */
    public synchronized void setCount() {
        //1.判断flag如果是true,证明有包子,生产线程等待
        if (flag == true) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //2.如果flag为false,证明没有包子,需要生产包子
        count++;
        System.out.println("生成了第..." + count + "个包子");
        //3.改变flag状态,为true,证明有包子了
        flag = true;
        //4.唤醒消费线程
        this.notify();
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
/**
 * 生产线程
 */
public class Product implements Runnable {
    private BaoZiPu baoZiPu;

    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            baoZiPu.setCount();
        }

    }
}
public class Consumer implements Runnable {
    private BaoZiPu baoZiPu;

    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            baoZiPu.getCount();
        }

    }
}
public class Test01 {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();
        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);
        new Thread(product).start();
        new Thread(consumer).start();
    }
}

多等待多唤醒

public class BaoZiPu {
    private int count;
    private boolean flag;

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    /**
     * getCount专门给消费线程用
     */
    public synchronized void getCount() {
        //1.判断flag如果是false,证明没有包子,消费线程等待
        while (flag == false) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //2.如果flag为true,证明有包子,需要消费包子
        System.out.println("消费了第......" + count + "个包子");
        //3.改变flag状态,为false,证明没有包子了
        flag = false;
        //4.唤醒生产线程
        this.notifyAll();
    }

    /**
     * setCount专门给生产线程生产包子用
     *
     * @param
     */
    public synchronized void setCount() {
        //1.判断flag如果是true,证明有包子,生产线程等待
        while (flag == true) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //2.如果flag为false,证明没有包子,需要生产包子
        count++;
        System.out.println("生成了第..." + count + "个包子");
        //3.改变flag状态,为true,证明有包子了
        flag = true;
        //4.唤醒消费线程
        this.notifyAll();
    }

    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
public class Consumer implements Runnable {
    private BaoZiPu baoZiPu;

    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            baoZiPu.getCount();
        }

    }
}

 

public class Product implements Runnable {
    private BaoZiPu baoZiPu;

    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

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

            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            baoZiPu.setCount();
        }

    }
}
public class Test01 {
    public static void main(String[] args) {
        /**
         * 两个生产者,两个消费者
         * **/
        BaoZiPu baoZiPu = new BaoZiPu();
        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);
        new Thread(product).start();
        new Thread(product).start();
        /*********************************/
        new Thread(consumer).start();
        new Thread(consumer).start();
        /**
         * 会出现连续消费,连续生产的情况
         *notify 如果多条线程在等待唤醒,就会随机唤醒一个
         *假如第一个生产者线程抢到了锁,生产完第一个包子,然后随机唤醒一个锁,然后释放锁
         * 然后第二个生产线程抢到了锁,flag还是为true,所以生产线程等待,再次释放锁
         * 然后第一个消费者线程抢到锁,然后消费一个包子,同时改flag为false 随机唤醒一个生产者线程,再次释放拿到的锁
         *同时刚刚等待的第二个生产者线程再次拿到锁,生产一个包子,改变flag值,再次随机唤醒一个生产者线程,同时释放手里的锁
         *这时第一个生产线程也可能再次拿到锁,再次生产一个包子,前一个已经生产了,会造成同时生产2个包子的情况
         *反之,也可能出现同时消费2个包子的情况
         * 解决办法:
         *直接唤醒所有线程,但是需要保证第二个自己的线程能再次拿到锁,所以必须循环回去,
         * 将所有if改成while
         *这样就保证了第一次在生产完包子后,改了flag值,即使第二个生产者拿到锁,flag不符合也不会继续生产
         * **/
    }
}

十一、lock锁

1)简单实现案例:

1.概述:是一个接口,作为锁对象使用
2.实现类:ReentrantLock
3.方法:
  void lock() :获取锁
  void unlock() :释放锁 

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

/**
 * 实现 Runnable 接口的 MyTicket 类,用于模拟多线程售票系统
 */
public class MyTicket implements Runnable {
    int ticket = 100;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 获取锁
            lock.lock();
            try {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
                    ticket--;
                }
            } finally {
                // 释放锁
                lock.unlock();
            }
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        MyTicket myTicket = new MyTicket();
        Thread t1 = new Thread(myTicket,"曼曼");
        Thread t2 = new Thread(myTicket,"童童");
        Thread t3 = new Thread(myTicket,"雪雪");
        t1.start();
        t2.start();
        t3.start();
    }
}

 2)lock与synchronized区别

1. 在java.util.concurrent.atomic 包下有很多"原子类",这些类都是对某一个值进行修改,进行操作的,底层实现原理都是"乐观锁"
    AtomicXXX

比如:AtomicInteger -> 以原子形式,修改int的值
2.构造:
  a.AtomicInteger()创建具有初始值 0 的新 AtomicInteger -> int i = 0
  b.AtomicInteger(int initialValue)创建具有给定初始值的新 AtomicInteger -> int i = 10
      
3.方法:
  int addAndGet(int delta)  以原子方式将给定值与当前值相加
  int getAndIncrement()-> 加1
  int getAndDecrement()-> 减1    

public class Test02 {
    public static void main(String[] args) {
        AtomicInteger i = new AtomicInteger(10);
        System.out.println(i);
        int sum = i.addAndGet(10);
        System.out.println("sum = " + sum);
    }
}

 

十二、线程池

1.问题描述:
  我们将来在操作多线程的时候,我们为了执行线程任务会频繁创建线程对象,销毁线程对象,这样的话比较耗费内存资源
      
  所以,我们想,能不能创建几个线程对象,让这几个线程对象循环利用

1.概述:Executors线程池对象
2.创建:
  static ExecutorService newFixedThreadPool(int nThreads) -> 创建线程池,指定最多创建多少条线程对象
      
3.ExecutorService:管理线程的
  Future<?> submit(Runnable task) -> 提交线程任务
  void shutdown()  -> 启动一次顺序关闭,执行以前提交的任务,但不接受新任务  
4.Future接口:
  用来接收执行run方法后的返回值的值,但是run方法没有返回值,所以不需要Future接收

 

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"执行了");
    }
}

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<String> future = es.submit(new MyCallable());
        System.out.println("future = " + future.get());
    }
}

十三、Callable接口

1)基本概念

1.概述:Callable<V>接口,类似于Runnable
2.方法:V call()
3.<V>:叫做泛型
  a.泛型作用:规定类型的,统一数据类型的
  b.泛型只能传递引用数据类型,如果操作基本类型数据,需要传递包装类,如果不写泛型,默认类型为Object类型
4.call方法 ->设置线程任务的,类似于run方法
5.call方法和run方法区别
  run:设置线程任务,没有返回值,不能throws异常
  call:设置线程任务,有返回值,能直接throws异常,
       返回值类型是啥类型?
           实现Callable时泛型写什么类型,重写的call方法返回值类型就是啥类型
6.提交线程任务:ExecutorService中的方法:
  Future<T> submit(Callable<T> task)
  返回值:Future->用于接收call方法的返回值  
7.获取call的返回值:
  需要用到Future接口中的方法:V get()  

public class Test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<String> future = es.submit(new MyCallable());
        System.out.println("future = " + future.get());
    }
}

 练习1

需求:创建两个线程任务,一个线程任务完成1-100的和,一个线程任务返回一个字符串

public class MyString implements Callable<String> {
    @Override
    public String call() throws Exception {
        int[] arr = {1,2,3,4,5};
        String str = "[";
        for (int i = 0; i < arr.length; i++) {
            if (i== arr.length-1){
                str+=arr[i]+"]";
            }else {
                str+=arr[i]+", ";
            }
        }
        return str;
    }
}
public class MySum implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum+=i;
        }
        return sum;
    }
}
public class Test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建一个固定大小为 2 的线程池
        ExecutorService es = Executors.newFixedThreadPool(2);

        // 提交 MyString 任务,并获取其执行结果
        Future<String> f1 = es.submit(new MyString());

        // 提交 MySum 任务,并获取其执行结果
        Future<Integer> f2 = es.submit(new MySum());

        // 输出 MyString 任务的执行结果
        System.out.println(f1.get());

        // 输出 MySum 任务的执行结果
        System.out.println(f2.get());

        // 关闭线程池
        es.shutdown();
    }
}

 

十四、定时器

1.作用:用来定时执行线程任务的
2.构造:Timer()
3.方法:
 void schedule(TimerTask task, Date firstTime, long period)  
                task:设置线程任务
                firstTime:从什么时间开始
                period:每隔多长时间执行一次线程任务

 

public class Test01 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("柳岩对涛哥说:涛哥,该起床了!");
            }
        },new Date(),2000L);//当前系统时间开始算,每隔两秒
    }
}

十五、枚举

1)枚举介绍

1.引用数据类型:
  类 数组 接口 枚举 注解
      
2.所有的枚举类的父类->Enum   
  public enum 枚举类类名{
      
  }  

3.枚举中的成员:
  a.所有成员默认都是static final修饰的常量,但是不用写static final,被static final修饰的变量名习惯上大写
      
  b.每一个枚举成员都是所在枚举的对象
  c.问题:枚举类中的成员都是什么类型? -> 本类类型
      
4.枚举的使用场景:一般都是表示状态的   
5.注意:枚举类中的构造都是private的,不写private,默认也是private 

 

package com.atguigu.enumtest;

public enum Status {
    //WEIFUKUAN,// Status WEIFAHUO = new Status()
    //YIFUKUAN,// Status YIFUKUAN = new Status()
    //WEIFAHUO,// Status WEIFAHUO = new Status()
    //YIFAHUO;// Status YIFAHUO = new Status()
    WEIFUKUAI("未付款"),// Status WEIFAHUO = new Status("未付款")
    YIFUKUAN("已付款"), //Status YIFUKUAN = new Status("已付款")
    WEIFAHUO("未发货"), //Status WEIFAHUO = new Status("未发货")
    YIFAHUO("已发货");//Status YIFAHUO = new Status("已发货")

    //枚举类中的构造要求是private的,不写也是private

    String name;
    Status(){

    }

    Status(String name){
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
public class Test01 {
    public static void main(String[] args) {
        Status weifahuo = Status.WEIFAHUO;
        System.out.println("weifahuo = " + weifahuo);//默认调用toString方法
        System.out.println("weifahuo = " + weifahuo.toString());//默认调用toString方法

        Status weifahuo1 = Status.WEIFAHUO;
        System.out.println(weifahuo1.getName());
    }
}

2)枚举的方法

public enum Status {
    //WEIFUKUAN,// Status WEIFAHUO = new Status()
    //YIFUKUAN,// Status YIFUKUAN = new Status()
    //WEIFAHUO,// Status WEIFAHUO = new Status()
    //YIFAHUO;// Status YIFAHUO = new Status()

    WEIFUKUAI("未付款"),// Status WEIFAHUO = new Status("未付款")
    YIFUKUAN("已付款"), //Status YIFUKUAN = new Status("已付款")
    WEIFAHUO("未发货"), //Status WEIFAHUO = new Status("未发货")
    YIFAHUO("已发货");//Status YIFAHUO = new Status("已发货")

    //枚举类中的构造要求是private的,不写也是private

    String name;
    Status(){

    }

    Status(String name){
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
public class Test02 {
    public static void main(String[] args) {
        Status weifahuo = Status.WEIFAHUO;
        System.out.println(weifahuo);
        System.out.println(weifahuo.toString());

        System.out.println("=======================");

        Status[] values = Status.values();
        for (Status value : values) {
            System.out.println(value.getName());
        }

        System.out.println("=======================");

        Status yifahuo = Status.valueOf("YIFAHUO");
        System.out.println(yifahuo);
    }
}

写的不好,请你给我指出来,谢谢

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

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

相关文章

clevercsv,一个非常实用的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个非常实用的 Python 库 - CleverCSV。 Github地址&#xff1a;https://github.com/alan-turing-institute/CleverCSV CSV&#xff08;Comma-Separated Values&#xff09;是一种常见的数据格式…

【C语言__结构体__复习篇5】

目录 前言 一、结构体基础知识 1.1 结构体的语法形式 1.2 创建结构体变量 1.3 结构体变量的初始化 1.4 点(.)操作符和箭头(->)操作符 二、匿名结构体 三、结构体自引用 四、结构体内存对齐 4.1 内存对齐的规则 4.2 出现结构体内存对齐的原因 4.3 修改默认对齐数 五、结…

网络工程师----第六天

三层网络架构&#xff1a; 1.核心层 含义&#xff1a;核心层是网络主干部分&#xff0c;是整个网络性能的保障 设备&#xff1a;路由器、防火墙、核心层交换机 特点&#xff1a;冗余能力、可靠性和高速的传输 特殊要求&#xff1a;核心层设备采用双机冗余热备份是非常必要的&am…

螺栓拧紧角度如何检验?——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 螺栓拧紧角度检验方法是确保螺栓连接紧固可靠的关键步骤之一。正确的拧紧角度可以确保螺栓的预紧力达到要求&#xff0c;防止松动和失效。下面将详细介绍螺栓拧紧角度检验的步骤和注意事项。 一、螺栓拧紧角度的重要性 …

Redis从入门到精通(二十)Redis最佳实践(一)优雅的Key结构、拒绝BigKey

文章目录 第7章 Redis最佳实践7.1 Redis键值设计7.1.1 优雅的Key结构7.1.2 拒绝BigKey7.1.2.1 何为BigKey7.1.2.2 BigKey的危害7.1.2.3 如何发现BigKey7.1.2.4 如何删除BigKey 7.1.3 恰当的数据类型7.1.3.1 存储Java对象7.1.3.2 存储hash数据 7.1.4 小结 第7章 Redis最佳实践 …

SQL表连接详解:JOIN与逗号(,)的使用及其性能影响

省流版 在这个详细的解释中&#xff0c;我们将深入探讨SQL中表连接的概念&#xff0c;特别是JOIN和逗号&#xff08;,&#xff09;在连接表时的不同用法及其对查询性能的影响。通过实际示例和背后的逻辑分析&#xff0c;我们将揭示在不同场景下选择哪种连接方式更为合适。 1.…

RuoYi-Cloud部署实战(手动部署)

RuoYi-Cloud部署实战 语雀 1. 若依源码和架构 RuoYi-Cloud: &#x1f389; 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统&#xff0c;同时提供了 Vue3 的版本 若依项目结构 带端口号的是需要启动的服务 com.ruoyi ├── ruoyi-ui …

(亲测有效)win7安装nodejs高版本(18.8.0)

现在学习vue3,vite,使用pnpm创建项目都需要高版本的nodejs了&#xff0c;win7最高只能安装13版本&#xff0c;好多已经不支持了。当然此篇只是以安装18.8.0为例&#xff0c;可以替换成更高的18或者20版本&#xff0c;只是太高的话可能出现冲突&#xff0c;够用就好。希望对各位…

管理能力学习笔记五:识别团队角色,因才施用

识别团队角色&#xff0c;因才施用&#xff0c;需要做到以下三点 扬长避短 管理者要学会问自己员工能把什么做好&#xff0c;而不是想方设法改造他们的短处 。 – 彼得德鲁克 人岗匹配 将合适的人放在合适的位置 人才多样化 团队需要各式各样的人才&#xff0c;才能高效配合…

【深度学习】Fine-Grained Face Swapping via Regional GAN Inversion高保真换脸范式

文章目录 代码介绍实践效果 帮助、问询 代码 https://github.com/e4s2022/e4s 介绍 Fine-Grained Face Swapping via Regional GAN Inversion 提出一种新的高保真换脸范式&#xff0c;能够保留期望的微妙几何和纹理细节。从微观面部编辑的角度重新思考换脸任务&#xff0c;基…

IO基础-传统I/O模型

关于IO数据流有两种形式&#xff0c;来源于网络和磁盘分别叫做网络IO、磁盘IO。 客户端通过TCP和UDP协议将数据流发往服务端&#xff0c;服务端接收数据这个过程称为网络IO。 服务端读取本地文件数据到服务中的过程称为磁盘IO。 基于 Linux 一切皆文件的理念&#xff0c;在内…

【Docker】Linux开放2735端口实现远程访问Docker

【Docker】Linux开放2735端口实现远程访问Docker 大家好 我是寸铁&#x1f44a; 总结了一篇【Docker】Linux开放2735端口实现远程访问Docker ✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 今天需要远程操作Linux服务器的docker&#xff0c;这时就需要开放出docker的端口给我…

TQ15EG开发板教程:在MPSOC上运行ADRV9371(vivado2018.3)

首先需要在github上下载两个文件&#xff0c;本例程用到的文件以及最终文件我都会放在网盘里面&#xff0c; 地址放在本文最后。首先在github搜索hdl选择第一个&#xff0c;如下图所示 GitHub网址&#xff1a;https://github.com/analogdevicesinc/hdl/releases 点击releases…

h2o-3机器学习平台连接mysql数据库

Getting Data into Your H2O Cluster — H2O 3.46.0.1 documentation 官方文档是这么说的&#xff01; 具体实操发现&#xff1a; java -jar build/h2o.jar 以上命令只能正常运行平台&#xff0c;无法连接数据库。 想要连接mysql数据集&#xff0c;首先需要自己下载mysql的…

【InternLM 实战营第二期-笔记3】茴香豆:搭建你的 RAG 智能助理

书生浦语是上海人工智能实验室和商汤科技联合研发的一款大模型,很高兴能参与本次第二期训练营&#xff0c;我也将会通过笔记博客的方式记录学习的过程与遇到的问题&#xff0c;并为代码添加注释&#xff0c;希望可以帮助到你们。 记得点赞哟(๑ゝω╹๑) 茴香豆&#xff1a;搭建…

损失函数:Cross Entropy Loss (交叉熵损失函数)

损失函数&#xff1a;Cross Entropy Loss &#xff08;交叉熵损失函数&#xff09; 前言相关介绍Softmax函数代码实例 Cross Entropy Loss &#xff08;交叉熵损失函数&#xff09;Cross Entropy Loss与BCE loss区别代码实例 前言 由于本人水平有限&#xff0c;难免出现错漏&am…

安全开发实战(2)---域名反查IP

目录 安全开发专栏 前言 域名与ip的关系 域名反查ip的作用 1.2.1 One 1.2.2 Two 1.2.3 批量监测 ​总结 安全开发专栏 安全开发实战http://t.csdnimg.cn/25N7H 这步是比较关键的一步,一般进行cdn监测后,获取到真实ip地址后,或是域名时,然后进行域名反查IP地址,进行进…

【剪映专业版】10时间线工具:主轨磁吸、自动吸附、联动、预览轴、全局缩放预览

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 主轨&#xff1a;有封面标志的轨道才是主轨。 主轨磁吸&#xff1a;开启后&#xff0c;在主轨上移动素材&#xff0c;自动向前磁吸&#xff0c;在其他轨道上移动无此效果&#xff1b;关闭后&#xff0c;不自动向前磁吸&…

python教学入门:字典和集合

字典&#xff08;Dictionary&#xff09;&#xff1a; 定义&#xff1a; 字典是 Python 中的一种数据结构&#xff0c;用于存储键值对&#xff08;key-value pairs&#xff09;。字典使用花括号 {} 定义&#xff0c;键值对之间用冒号 : 分隔&#xff0c;每对键值对之间用逗号…

150个 HTML5 成体系的网站模版 量大慢选 持续更新中

目录 HTML5 网站模版 No.1HTML5 网站模版 No.2HTML5 网站模版 No.3HTML5 网站模版 No.4HTML5 网站模版 No.5 HTML5 网站模版 No.1 HTML5 网站模版 No.1 HTML5 网站模版 No.2 HTML5 网站模版 No.2 HTML5 网站模版 No.3 HTML5 成体系网站模版 No.3 HTML5 网站模版…