Java基础学习(16)多线程

news2024/12/26 0:03:12

Java基础学习多线程

  • 一、多线程
    • 1.1 什么是多线程
    • 1.2 多线程的两个概念
      • 1.2.1 并发
    • 1.3 多线程的实现方式
    • 1.4 多线程的成员方法
    • 1.5 线程的生命周期
  • 二、线程安全
    • 1.6 同步方法
    • 1.7 锁lock
    • 1.8 死锁
    • 1.8 生产者和消费者 (等待唤醒机制)
    • 1.9 等待唤醒机制(阻塞队列方式实现)
    • 1.10 线程状态
  • 二、线程池
    • 2.1 线程池的概述
    • 2.2 自定义线程池
    • 2.3 线程池到底多大才合适

一、多线程

1.1 什么是多线程

线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位.
应用软件中互相独立,可以同时运行的功能

进程:
进程是程序的基本执行实体

多线程的应用场景:

  1. 软件中的耗时操作
  2. 拷贝、迁移大文件
  3. 加载大量的资源文件
  4. 所有的聊天软件
  5. 所有的后台服务器
    ……
    同时这些多项任务,提升运行效率

1.2 多线程的两个概念

1.2.1 并发

并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
在这里插入图片描述

1.3 多线程的实现方式

  • 继承Thread类的方式进行实现

多线程的第一种启动方式:

  1. 自己定义一个类继承Thread
  2. 重写run方法
  3. 创建子类的对象,并启动线程

重写的Run方法:

package MyThreads;
//重写run方法(Thread)
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "HelloWorld!!");
        }
    }
}

启动的线程:

package MyThreads;
//新建Thread
public class Dom1 {
    public static void main(String[] args) {
    //创建线程对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        //给线程添加名字
        t1.setName("线程1");
        t2.setName("线程2");

        //启动两个线程
        t1.start();
        t2.start();
    }
}

  • 实现Runnable接口的方式进行实现

多线程的第二种启动方式:

  1. 自己定义一个类实现Runnable接口
  2. 重写里面的run方法
  3. 创建自己的类的对象
  4. 创建一个Thread类的对象,并开启线程

执行代码:

package MyThreads;
//创建线程二
//利用runnable接口进行创建
/*
* 多线程的第二种启动方式:
* 1.自己定义一个类实现Runnable接口
* 2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
*
* */

public class Dom2 {
    public static void main(String[] args) {
        //创建线程需要执行的任务
        MyThread2 mr = new MyThread2();

        //创建线程执行mr任务
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        //给线程添加名字
        t1.setName("线程一");
        t2.setName("线程二");

        //开启线程
        t1.start();
        t2.start();
    }
}

接口代码:

package MyThreads;
//创建线程二
//利用runnable接口进行创建
/*
* 多线程的第二种启动方式:
* 1.自己定义一个类实现Runnable接口
* 2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
*
* */

public class Dom2 {
    public static void main(String[] args) {
        //创建线程需要执行的任务
        MyThread2 mr = new MyThread2();

        //创建线程执行mr任务
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        //给线程添加名字
        t1.setName("线程一");
        t2.setName("线程二");

        //开启线程
        t1.start();
        t2.start();
    }
}

  • 利用Callable接口和Future接口方式实现

多线程的第三种实现方式:
特点:可以获取到多线程运行的结果

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

对象:

package MyThreads;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

//线程创建3
/*
* 多线程的第三种实现方式:
特点:可以获取到多线程运行的结果
1。创建一个类MyCallable实现Callable接口
* 2。重写ca11   (是有返回值的,表示多线程运行的结果 )
3。 创建MyCallable的对象 (表示多线程要执行的任务)
* 4。创建FutureTask的对象 (作用管理多线程运行的结果)
* 5。创建Thread类的对象,并启动 (表示线程)
* */
public class Dom3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建MyCallable的对象 (表示多线程要执行的任务)
        MyCallable mr = new MyCallable();
        //创建FutureTask的对象 (作用管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<>(mr);
        //创建Thread类的对象,并启动 (表示线程)
        Thread t1 = new Thread(ft);
        //启动线程
        t1.start();
        //获取返回值
        Integer result = ft.get();
        System.out.println(result);
    }
}

接口:

package MyThreads;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            sum = sum + i;
        }
        return sum;
    }
}

总结:
按照不同的需求去完成我们的线程创建
在这里插入图片描述

1.4 多线程的成员方法

方法名称说明
String getName()返回此线程的名称
void setName(string name)设置线程的名字(构造方法也可以设置名字)
static Thread currentThread()获取当前线程的对象
static void sleep(long time)让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级
final void setDaemon(boolean on)设置为守护线程
public static void yield()出让线程/礼让线程
public static void join()插入线程/插队线程

细节:(前面四个成员方法)

  • 细节1:
    如果我们没有设置名字,JVM会自动给我们设置Thread-x的名字,x从0开始。
    如果我们要设置名字。可以通过set设置,也可以用继承的Thread的对象,重写Thread的构造方法,利用有参构造
  • 细节2;
    当启动JVM时,就会创建多个线程,我们以前所写的代码都在main线程里面进行的
    细节3:
    sleep:哪条线程执行到该代码就会在这条线程上停留

测试代码:

package MyThreadThod;
//成员方法演示
/*
* 1. 设置名字,返回名字
* 2. 获取当前的线程名字
* 3. 让线程睡眠(单位毫秒)
* */
public class Dom1 {
    public static void main(String[] args) {
        //创建线程对象
        MyThread t1 = new MyThread("lisi");
        MyThread t2 = new MyThread("zhangsan");

        //开启线程
        t1.start();
        t2.start();


    }
}

继承Thread代码:

package MyThreadThod;

public class MyThread extends Thread{
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //睡眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(getName()+"@"+i);
        }
    }
}

成员方法优先级
范围: 1~10
优先级越高,就代表的执行他的概率就越高,但是不是代表优先级越高,就先执行该代码,执行的顺序是随机的,但是优先级越大,随机的概率就越大

默认的优先级: 5

package MyThreadThod;
//成员方法,优先级
public class Dom2 {
    public static void main(String[] args) {
        //创建多线程执行的任务
        MyRunnable mr = new MyRunnable();

        //创建Thread对象,执行任务
        Thread t1 = new Thread(mr);

        //设置优先级
        t1.setPriority(10);
        //启动
        t1.start();
    }
}

守护线程:

这是表示“备胎守护线”,当所守护的对象不存在的时候就没有存在的必要了,就会提前结束线程

package MyThreadThod;
//成员方法:守护线程
public class Dom3 {
    public static void main(String[] args) {
        //创建Thread对象
        MyThread t1 = new MyThread();
        MyThread2 t2 = new MyThread2();

        //将第二个设置为守护线程
        t2.setDaemon(true);

        t1.setName("女神");
        t2.setName("二货");

        t2.start();
        t1.start();

    }
}

出让线程:

让执行的线程相对平均
Thread.yieId();

插入线程:

让该线程执行完后再执行该代码

package MyThreadThod;

import MyThreads.MyThread1;

//插入代码
public class Dom4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new MyThread1();
        t1.setName("洋芋");
        t1.start();

        t1.join();

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

1.5 线程的生命周期

在这里插入图片描述

二、线程安全

在这里插入图片描述
买票引发的安全问题:

  1. 相同的票出现了多次
  2. 出现了超出范围的票

解决方法:
在这里插入图片描述

  • 利用锁
    特点1: 锁默认打开,有一个线程进去了,锁自动关闭
    特点2:里面的代码全部执行完毕,线程出来,锁自动打开

代码实现

package MySafeThread.safe1;

public class MySafe  extends Thread{
    static int ticket = 0;
    //锁对象必须是唯一的
    static Object obj = new Object();
    @Override
    public void run() {
        //创建锁
        //作用:如果有一个进入到了线程当中就会,就不会让其他的线程进入,直到
        //当该线程完成过后,重写再抢夺CPU
        while (true){   
            //创建锁
            synchronized (obj){
                if (ticket<100){
                    //每个10毫秒在进行买票
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    ticket++;
                    System.out.println(getName() + "正在卖"+ticket+"张票了!!!!!!!!!");
                }else {
                    break;
                }
            }
        }
    }
}

锁对象必须得是唯一的

锁不唯一就表示打开这扇门的钥匙有多个,不能让该代码执行完后,才能让其他的线程抢夺,这样会导致多执行,重复的效果

一般情况下用当前代码的字节码文件.class来表示唯一对象

1.6 同步方法

就是把synchronized关键字加到方法上

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

特点1: 同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定

里面在不同状态下调用的对象也不一样:

非静态: this
静态:当前类的字节码文件对象

Stirngbuilder 与 Stringbuffer 的区别:

  • Stringbuffer在多线程下相对于Stirngbuilder较为安全
  • 所以在单线程的情况下一般采用 Stringbuilder

主要修改的代码

package MySafeThread.safe2;

import java.util.Timer;

public class MyRunnable2 implements Runnable{
    int ticket = 0;
    @Override
    public void run() {
        //1. 循环
        //2. 同步方法
        //3.判断是否完成,未完成,执行下列代码
        //4.判断是否完成,已完成,执行下列代码
        while (true){
            if (method()) break;
        }
    }

    //创建同步方法:这里是非静态的调用时为:this
    private synchronized boolean method() {
        if (ticket == 100) {
            return true;
        } else {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            ticket++;
            System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "票了!!!!!");
        }

        return false;
    }
}

1.7 锁lock

定义:

虽然我们可以理解同步代码块和同步方法的锁对象问题但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock中提供了获得锁和释放锁的方法

  • void lock(): 获得锁
  • void unlock(): 释放锁

手动上锁、手动释放锁

Lock是接口不能直接实例化

这里采用它的实现染ReentrantLock来实例化

ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例

1.8 死锁

两个锁互相嵌套导致,两个锁都在等待着对方放下锁,但是他们释放锁的情况是执行该所下面的代码才能释放,这样就导致程序卡死出不去

死锁实列:

在这里插入代码片

所以我们要在写代码的时候,千万不要进行锁的嵌套写法,一不小心就会掉进坑里面

package MySafeThread.BugLock;

public class BUGLock extends Thread {
    static Object obj1 = new Object();
    static Object obj2 = new Object();
    @Override
    public void run() {
        if ("A".equals(getName())){
            synchronized (obj1){
                System.out.println("拿到了线程A,准备拿线程B");
                synchronized (obj2){
                    System.out.println("拿到了线程B,结束");
                }
            }
        } else if ("B".equals(getName())) {
            if ("B".equals(getName())){
                synchronized (obj2){
                    System.out.println("拿到了线程B,现在拿线程A");
                    synchronized (obj1){
                        System.out.println("拿到了线程A,现在拿线程B");
                    }
                }
            }
        }
    }
}

1.8 生产者和消费者 (等待唤醒机制)

生产者消费者模式是一个十分经典的多线程协作的模式

形象化理解:

在这里插入图片描述

方法名称说明
void wait()当前线程等待,直到被其他线程唤醒
void notify()随机唤醒单个线程
void notifyAll()唤醒所有线程

启动窗口:

package MyLock.WaitAndNotify;

import java.io.FileOutputStream;

public class waitNotiyDom {
    public static void main(String[] args) {
        //创建厨师和吃货的对象
        Cook cook = new Cook();
        FoodAddict addict = new FoodAddict();

        //设置名字
        cook.setName("厨师");
        addict.setName("吃货");

        cook.start();
        addict.start();
    }
}

厨师窗口:

package MyLock.WaitAndNotify;

public class Cook extends Thread{
    @Override
    public void run() {
        //循环
        while (true){
            //创建同步代码块
            synchronized (Desk.lock){
                //判断,没有执行到尾部
                if (Desk.MaxFood == 0){
                    break;
                }else {
                    //执行到末尾
                    //首先判断状态值是否为0,是否有面条
                    if (Desk.Food == 1){
                        //是1,就执行等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
                        //是0,就做食物,释放所有等待代码
                        System.out.println("厨师正在做食物");
                        System.out.println("厨师已做完,请吃");
                        //将状态码变为1
                        Desk.Food =1;
                        //释放所以等待代码
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}

吃货窗口:

package MyLock.WaitAndNotify;

public class FoodAddict extends Thread{
    @Override
    public void run() {
        while (true){
            synchronized (Desk.lock){
                //判断是否还能吃下去
                if (Desk.MaxFood == 0){
                    //不能吃下去了
                    System.out.println("吃货吃不下了,要g了");
                    break;
                }else{
                    //还能继续吃
                    //判断桌子上面是否有吃的
                    if (Desk.Food == 0){
                        //没有吃的就执行等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
                        //有吃的
                        //将吃的碗数减1
                        Desk.MaxFood--;
                        System.out.println("吃货正在吃面条,还可以吃"+Desk.MaxFood+"碗");
                        //已经吃完,释放所有的代码,叫厨师吃饭了
                        Desk.lock.notifyAll();
                        //食物清空
                        Desk.Food =0;
                    }
                }
            }
        }
    }
}

桌子窗口:

package MyLock.WaitAndNotify;
//表示桌子,来放置面条的状态,达到谁去执行的效果
public class Desk{
    //创建面的状态 , 0:没有面条 1:有面条
    public static int Food = 0;
    //创建吃货吃的最多碗
    public static int MaxFood = 10;

    //创建锁
    public static Object lock = new Object();

}

1.9 等待唤醒机制(阻塞队列方式实现)

在这里插入图片描述
实现类:
ArrayBlockingQueue底层是数组,有界限
LinkedBlockingQueue底层是链表,无界但不是真正的无界最大为int的最大值。

厨师类

package MyLock.ArrayBlockingQueues;

import java.util.concurrent.ArrayBlockingQueue;

public class queueCooker extends Thread{
    ArrayBlockingQueue<String> queue;

    public queueCooker(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true){
            //不需要再写锁了,因为put当中写了
            try {
                queue.put("面条");
                System.out.println("厨师做了面");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

吃货类

package MyLock.ArrayBlockingQueues;

import java.util.concurrent.ArrayBlockingQueue;

public class queueAddict extends Thread{
    ArrayBlockingQueue<String> queue;

    public queueAddict(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true){
            try {
                String food = queue.take();
                System.out.println("吃货在吃"+food);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

执行类:

package MyLock.ArrayBlockingQueues;

import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;

//阻塞队列方式实现等待唤醒任务
public class ArrayBlockingQueueDom {
    public static void main(String[] args) {
        //创建阻塞队列方式(唯一)
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);

        //创建两个变量,赋名字,并且把阻塞队列传入
        queueAddict addict = new queueAddict(queue);
        queueCooker cooker = new queueCooker(queue);

        addict.setName("吃货");
        cooker.setName("厨师");

        //开启线程
        addict.start();
        cooker.start();
    }
}

1.10 线程状态

六种红色的状态
在这里插入图片描述
新建状态(NEW ) --> 创建线程对象

就绪状态(RUNNABLE ) --> start方法

阻塞状态( BLOCKED ) --> 无法获得锁对象

等待状态 (WAITING ) --> wait方法

计时等待 (TIMED WAITING ) --> sleep方法

结束状态(TERMINATED ) --> 全部代码运行完毕

二、线程池

2.1 线程池的概述

  • 以前写多线程的弊端
  1. 用到线程的时候就创建
  2. 用完之后线程消失

会造成资源的浪费

所以现在出现了线程池来存储线程

线程池的核心原理:

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

创建线程池的方法:
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象

方法名称说明
public static ExecutorService newCachedThreadPool()创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool(int nThreads)创建有上限的线程池
package MythreadPool.dom1;

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

//创建线程池,初识线程池
public class Dom1 {
    public static void main(String[] args) {
        //创建无上限的线程池
        ExecutorService pool1 = Executors.newCachedThreadPool();
   //创建有上限的线程池(3个)
//        ExecutorService pool2 = Executors.newFixedThreadPool(3);

        //提交任务
        pool1.submit(new MyRunnable1());
        pool1.submit(new MyRunnable1());
        pool1.submit(new MyRunnable1());
        pool1.submit(new MyRunnable1());
    }
}

package MythreadPool.dom1;

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

2.2 自定义线程池

任务拒绝策略说明
ThreadPoolExecutor.AbortPolicy 默认策略: 丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardoldestPolicy抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy调用任务的run()方法绕过线程池直接执行

理解:
自定义线程池:

核心元素一: 正式员工数量 --------------> 核心线程数量(不能小于0)

核心元素二: 餐厅最大员工数 ---------------> 线程池中最大线程的数量(最大数量 >= 核心线程数量)

核心元素三: 临时员工空闲多长时间被辞退(值) --------> 空闲时间 (值) (不能小于0)

核心元素四: 临时员工空闲多长时间被辞退(单位) -------> 空闲时间(单位) (用TimeUnit指定)

核心元素五: 排队的客户 --------> 阻塞队列 (不能为null)

核心元素六: 从哪里招人 ---------> 创建线程的方式(不能为null)

核心元素七: 当排队人数过多,超出顾客请下次再来(拒绝服务) ------> 要执行的任务过多时的解决方案(不能为null)

package MythreadPool.dom2;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

//自定义线程池
public class dom2 {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3 , //核心线程数量(表示的正式员工)
                6, //最大线程数量(总员工数量)
                60,//空闲线程最大的存活时间
                TimeUnit.SECONDS, //表示当前最大存活的时间单位
                new ArrayBlockingQueue<>(3),//排队的客户
                Executors.defaultThreadFactory(),//创建线程工厂
                new ThreadPoolExecutor.AbortPolicy()//拒绝方法

        );
    }
}

总结:

  • 创建一个空的池子
  • 有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程

不断的提交任务,会有以下三个临界点:

  1. 当核心线程满时,再提交任务就会排队
  2. 当核心线程满,队伍满时,会创建临时线程
  3. 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略

2.3 线程池到底多大才合适

CPU 密集型运算 --------> 最大并行数 +I

l/0 密集型运算 ----------> 最大并行数 * ”期望 CPU利用率 * 总时间(CPU计算时间+等待时间) / CPU计算时间

例如: 4核8线程

在这里插入图片描述

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

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

相关文章

IntelliJ IDEA汉化

IntelliJ IDEA汉化 描述解决办法 描述 在开发过程中&#xff0c;我们想让界面现实为汉语&#xff0c;那么我们就需要对IDEA工具进行汉化&#xff0c;目前版本的IDEA汉化都非常简单&#xff0c;请看下述实现步骤。 解决办法 下述汉化方法&#xff0c;全家桶软件都通用。 打开…

attention机制

油管attention机制解释 油管的attention机制视频。 基础形态 如下图所示&#xff0c;假设现在有4个向量&#xff0c; v 1 v_1 v1​到 v 4 v_4 v4​。我们以 v 3 v_3 v3​为视角&#xff0c;看它是怎么得到 y 3 y_3 y3​的。首先用 v 3 v_3 v3​和全部4个向量做点乘&#xff…

不依赖硬件,可以无限扩展的闹钟组件

在实际的开发项目中&#xff0c;很多时候我们需要定时的做一些事情&#xff0c;举例&#xff1a; ①路上的路灯&#xff0c;每天晚上6:00准时打开&#xff0c;每天早上6:00准时关闭&#xff1b;②定时闹钟&#xff0c;起床上班。这些行为其实都是定时任务–闹钟。 大部分单片机…

【MySql】数据库设计过程

目录 概念数据库设计&#xff1a; 逻辑数据库设计&#xff1a; 物理数据库设计&#xff1a; ->需求分析&#xff08;收集需求和理解需求,“源”&#xff09; ->概念数据库设计&#xff08;建立概念模型:"E-R图/IDEF1X"&#xff09; ->逻辑数据库设计&…

idle_in_transaction_session_timeout idle_session_timeout

这两个参数都是用来控制PostgreSQL数据库中会话的超时时间的。 idle_in_transaction_session_timeout idle_in_transaction_session_timeout参数用于控制在事务中处于空闲状态的会话的超时时间。如果一个会话在事务中处于空闲状态超过了指定的时间&#xff0c;则该会话将被终…

Rocky Linux 9.2 正式版发布 - RHEL 下游免费发行版

Rocky Linux 由 CentOS 项目的创始人 Gregory Kurtzer 领导。 请访问原文链接&#xff1a;https://sysin.org/blog/rocky-linux-9/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 以社区方式驱动的企业 Linux Rocky Linux 是…

Android Studio 基础 之 使用 okhttp 长连接,流式获取数据的方法简单整理了

Android Studio 基础 之 使用 okhttp 长连接&#xff0c;流式获取数据的方法简单整理了 目录 Android Studio 基础 之 使用 okhttp 长连接&#xff0c;流式获取数据的方法简单整理了 一、简单介绍 二、实现原理 三、注意事项 四、效果预览 五、实现关键 六、关键代码 七…

2023年服务器数据保护的详细指南

​  无论您经营的是小型个人博客还是日常流量很高的大型企业电子商务网站&#xff0c;安全性都是每个人都关心的问题。虽然粗略的第三方有时间和资源来利用他们在您的系统中发现的任何弱点&#xff0c;但您也可以采取其他措施来提高服务器的安全性并阻止它们。以下是您可以采…

论文笔记:tri-plane 【持续更新】

文章目录 概述Tri-Plane 表达验证 tri-plane 表达 3D GAN 框架CNN生成器backbone以及渲染超分Dual discriminationModeling pose-correlated attributes 实验数据 参考文献 概述 论文名称&#xff1a; Efficient Geometry-aware 3D Generative Adversarial Networks Project pa…

Class 04 - 日期时间格式

Class 04 - 日期时间格式 R语言中的日期和时间格式tidyverse 和 lubridate 功能包简介tidyverse 简介lubridate 简介 加载 tidyverse 和 lubridate 功能包处理日期和时间日期时间的格式获取当前的日期 today()获取当前的时间 now() 字符串格式转换为日期格式ymd()mdy()dmy()字符…

AlmaLinux 9.2 正式版发布 - RHEL 兼容免费发行版

AlmaLinux 9.2 正式版发布 - RHEL 兼容免费发行版 由社区提供的免费 Linux 操作系统&#xff0c;RHEL 兼容发行版。 请访问原文链接&#xff1a;https://sysin.org/blog/almalinux-9/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sys…

一个胖乎乎的3D卡片(有点像捏扁的圆柱体)

先上效果图&#xff08;图片是随机的&#xff0c;可能你们看到的和这个不一样。但效果是相同的&#xff09;&#xff1a; 再上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Titl…

飞浆AI studio人工智能课程学习(1)-大模型时代优质Prompt

文章目录 大模型时代&优质PromptAl生成技术价值概览开始构建你的优质prompt 近十年深度学习模型主要更迭为什么大模型能够有如此强大的表现力大模型与Prompt例1&#xff1a; 画一幅画&#xff0c;呆萌的小猫躺在大泡泡中例2&#xff1a;请生成一张统计图&#xff0c;内容为…

apk自动签名工具

序言 因为360加固&#xff0c;自动签名需要开通VIP&#xff0c;每次加固完了都得手动签名。所以写了个工具。实现通过配置文件配置&#xff0c;拖拽APK自动签名。 支持&#xff1a;V1 V2 V3 V4 签名。通过分析清单文件&#xff0c;自动选择版本。 效果 使用 1.下载jar包 au…

实验室检验系统源码,集检验业务、质量控制、报告、统计分析、两癌等模块于一体

云 LIS 系统针对区域化 LIS 而设计&#xff0c;依托底层云架构&#xff0c;将传统的 LIS 功能模块进行“云化”。 该系统是集检验业务、科室管理、质量控制、报告、统计分析、两癌等模块于一体的数据检验信息平台。通过计算机联网&#xff0c;实现各类仪器数据结果的实时自动接…

新库上线 | CnOpenData舆情云数据

舆情云数据 一、数据简介 网络舆情监测数据是决策者进行数据分析和决策处置的基础。舆情云数据覆盖81000 网站、5600 论坛、1000 平面媒体、2500万 微信账号、3亿 微博账号、300 网络视频、17000 境外媒体、1400万 自媒体账号、2500 新闻客户端、170 电视台 &#xff0c;数据来…

图神经网络:(大型图的有关处理)在Pumbed数据集上动手实现图神经网络

文章说明&#xff1a; 1)参考资料&#xff1a;PYG官方文档。超链。 2)博主水平不高&#xff0c;如有错误还望批评指正。 3)我在百度网盘上传了这篇文章的jupyter notebook和有关文献。超链。提取码8848。 文章目录 Pumed数据集文献阅读继续实验 Pumed数据集 导库 from torch_…

【day2】单片机

目录 【1】GPIO 1.定义 2.应用 I - Input - 输入采集 O - Output - 输出控制 ​编辑 3.GPIO结构框图 4.功能描述 输入功能 输出功能 5.相关寄存器 【2】点亮一盏LED灯 1.实验步骤 2.编程实现 3.编译下载 4.复位上电 练习&#xff1a;实现LED灯闪烁 练习…

Linux - 第15节 - 网络基础(应用层)

1.再谈 "协议" 1.1.协议的概念 协议&#xff0c;网络协议的简称&#xff0c;网络协议是通信计算机双方必须共同遵从的一组约定&#xff0c;比如怎么建立连接、怎么互相识别等。 为了使数据在网络上能够从源到达目的&#xff0c;网络通信的参与方必须遵循相同的规则&…

收集数据集以训练自定义模型的 5 种方法

来源&#xff1a;投稿 作者&#xff1a;王同学 编辑&#xff1a;学姐 在过去的十年中&#xff0c;深度学习技术在计算机视觉领域中的应用逐年增加。其中当属「行人检测」和「车辆检测」最为火爆&#xff0c;其原因之一就是「预训练模型」的「可复用性」。 由于深度学习技术在这…