0

0

java实现多线程的几种方式

青灯夜游

青灯夜游

发布时间:2023-01-04 15:52:49

|

9973人浏览过

|

来源于php中文网

原创

实现多线程的方式:1、继承thread类,通过jdk提供的thread类,重写thread类的run方法即可;2、实现runnable接口,runnable是一个“@functionalinterface”函数式接口,也就意味了可以利用jdk8提供的lambda的方式来创建线程任务;3、使用内部类的方式;4、利用定时器;5、带返回值的线程实现方式;6、基于线程池实现多线程。

java实现多线程的几种方式

本教程操作环境:windows7系统、java8版、DELL G3电脑。

多线程的形式上实现方式主要有两种,一种是继承Thread类,一种是实现Runnable接口。本质上实现方式都是来实现线程任务,然后启动线程执行线程任务(这里的线程任务实际上就是run方法)。这里所说的6种,实际上都是在以上两种的基础上的一些变形。

下面分别就这6中实现方式一一介绍。

第一种方式:继承Thread类

立即学习Java免费学习笔记(深入)”;

万物皆对象,那么线程也是对象,对象就应该能够抽取其公共特性封装成为类,使用类可以实例化多个对象,那么实现线程的第一种方式就是继承Thread类。继承Thread类是最简单的一种实现线程的方式,通过JDK提供的Thread类,重写Thread类的run方法即可,那么当线程启动的时候,就会执行run方法体的内容。代码如下:

package com.kingh.thread.create;

/**
 * 继承Thread类的方式创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/13 19:19
 */
public class CreateThreadDemo1 extends Thread {

    public CreateThreadDemo1() {
        // 设置当前线程的名字
        this.setName("MyThread");
    }

    @Override
    public void run() {
        // 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意这里,要调用start方法才能启动线程,不能调用run方法
        new CreateThreadDemo1().start();

        // 演示主线程继续向下执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}

运行结果如下

当前运行的线程名为: main
当前运行的线程名为: MyThread
当前运行的线程名为: main
当前运行的线程名为: MyThread
当前运行的线程名为: MyThread
当前运行的线程名为: main

这里要注意,在启动线程的时候,并不是调用线程类的run方法,而是调用了线程类的start方法。那么我们能不能调用run方法呢?答案是肯定的,因为run方法是一个public声明的方法,因此我们是可以调用的,但是如果我们调用了run方法,那么这个方法将会作为一个普通的方法被调用,并不会开启线程。这里实际上是采用了设计模式中的模板方法模式,Thread类作为模板,而run方法是在变化的,因此放到子类来实现。

1. 创建多个线程

上面的例子中除了我们创建的一个线程以外其实还有一个主线程也在执行。那么除了这两个线程以外还有没有其他的线程在执行了呢,其实是有的,比如我们看不到的垃圾回收线程,也在默默的执行。这里我们并不去考虑有多少个线程在执行,上面我们自己创建了一个线程,那么能不能多创建几个一起执行呢,答案是肯定的。一个Thread类就是一个线程对象,那么多创建几个Thread类,并调用其start方法就可以启动多个线程了。代码如下

package com.kingh.thread.create;

/**
 * 创建多个线程同时执行
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 9:46
 */
public class CreateMultiThreadDemo2 extends Thread {

    public CreateMultiThreadDemo2(String name) {
        // 设置当前线程的名字
        this.setName(name);
    }

    @Override
    public void run() {
        // 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意这里,要调用start方法才能启动线程,不能调用run方法
        new CreateMultiThreadDemo2("MyThread-01").start();

        // 创建多个线程实例,同时执行
        new CreateMultiThreadDemo2("MyThread-02").start();

        // 演示主线程继续向下执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}

运行结果如下

当前运行的线程名为: main
当前运行的线程名为: MyThread-02
当前运行的线程名为: MyThread-01
当前运行的线程名为: main
当前运行的线程名为: MyThread-01
当前运行的线程名为: MyThread-02
当前运行的线程名为: main

2. 指定线程名称

可以看到,通过创建多个Thread类,并且调用其start方法,启动了多个线程。每个线程都有自己的名字,在上述代码中,分别给创建的线程指定了MyThread-01和MyThread-02这个名字,然后构造方法中通过调用父类的setName方法给线程名字赋值。如果不指定线程名字,系统会默认指定线程名,命名规则是Thread-N的形式。但是为了排查问题方便,建议在创建线程的时候指定一个合理的线程名字。下面的代码是不使用线程名的样子

package com.kingh.thread.create;

/**
 * 创建多个线程同时执行,使用系统默认线程名
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 9:46
 */
public class CreateMultiThreadDemo3 extends Thread {

    @Override
    public void run() {
        // 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意这里,要调用start方法才能启动线程,不能调用run方法
        new CreateMultiThreadDemo3().start();

        // 创建多个线程实例,同时执行
        new CreateMultiThreadDemo3().start();

        // 演示主线程继续向下执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}

运行的结果如下:

当前运行的线程名为: main
当前运行的线程名为: Thread-1
当前运行的线程名为: Thread-0
当前运行的线程名为: main
当前运行的线程名为: Thread-1
当前运行的线程名为: Thread-0

第二种方式:实现Runnable接口

实现Runnable接口也是一种常见的创建线程的方式,使用接口的方式可以让我们的程序降低耦合度。Runnable接口中仅仅定义了一个方法,就是run。我们来看一下Runnable接口的代码。

package java.lang;

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

其实Runnable就是一个线程任务,线程任务和线程的控制分离,这也就是上面所说的解耦。我们要实现一个线程,可以借助Thread类,Thread类要执行的任务就可以由实现了Runnable接口的类来处理。 这就是Runnable的精髓之所在!

Runnable 是一个@FunctionalInterface 函数式接口,也就意味了可以利用JDK8提供的lambda的方式来创建线程任务,后面的代码中会给读者演示具体如何使用。

使用Runnable实现上面的例子步骤如下:

  • 定义一个类实现Runnable接口,作为线程任务类
  • 重写run方法,并实现方法体,方法体的代码就是线程所执行的代码
  • 定义一个可以运行的类,并在main方法中创建线程任务类
  • 创建Thread类,并将线程任务类做为Thread类的构造方法传入
  • 启动线程

1. 创建线程任务

线程任务就是线程要做的事情,这里我们让这个线程每隔1s中打印自己的名字

package com.kingh.thread.create;

/**
 * 线程任务
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo4_Task implements Runnable {

    @Override
    public void run() {
		// 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}

2. 创建可运行类

在这里创建线程,并把任务交给线程处理,然后启动线程。

package com.kingh.thread.create;

/**
 * 创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo4_Main {

    public static void main(String[] args) throws Exception {
        // 实例化线程任务类
        CreateThreadDemo4_Task task = new CreateThreadDemo4_Task();

        // 创建线程对象,并将线程任务类作为构造方法参数传入
        new Thread(task).start();

        // 主线程的任务,为了演示多个线程一起执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}

线程任务和线程的控制分离,那么一个线程任务可以提交给多个线程来执行。这是很有用的,比如车站的售票窗口,每个窗口可以看做是一个线程,他们每个窗口做的事情都是一样的,也就是售票。这样我们程序在模拟现实的时候就可以定义一个售票任务,让多个窗口同时执行这一个任务。那么如果要改动任务执行计划,只要修改线程任务类,所有的线程就都会按照修改后的来执行。相比较继承Thread类的方式来创建线程的方式,实现Runnable接口是更为常用的。

3. lambda方式创建线程任务

这里就是为了简化内部类的编写,简化了大量的模板代码,显得更加简洁。如果读者看不明白,可以读完内部类方式之后,回过来再看这段代码。

package com.kingh.thread.create;

/**
 * 创建线程with lambda
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo5_Lambda {

    public static void main(String[] args) throws Exception {
        // 使用lambda的形式实例化线程任务类
        Runnable task = () -> {
            while (true) {
                // 输出线程的名字
                printThreadInfo();
            }
        };

        // 创建线程对象,并将线程任务类作为构造方法参数传入
        new Thread(task).start();

        // 主线程的任务,为了演示多个线程一起执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

第三种方式:使用内部类的方式

这并不是一种新的实现线程的方式,只是另外的一种写法。比如有些情况我们的线程就想执行一次,以后就用不到了。那么像上面两种方式(继承Thread类和实现Runnable接口)都还要再定义一个类,显得比较麻烦,我们就可以通过匿名内部类的方式来实现。使用内部类实现依然有两种,分别是继承Thread类和实现Runnable接口。代码如下:

package com.kingh.thread.create;

/**
 * 匿名内部类的方式创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo6_Anonymous {

    public static void main(String[] args) {
        // 基于子类的方式
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }.start();

        // 基于接口的实现
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }).start();
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

可以想象一下,我能不能既基于接口,又基于子类呢?像下面的代码会执行出什么样子呢?

华友协同办公自动化OA系统
华友协同办公自动化OA系统

华友协同办公管理系统(华友OA),基于微软最新的.net 2.0平台和SQL Server数据库,集成强大的Ajax技术,采用多层分布式架构,实现统一办公平台,功能强大、价格便宜,是适用于企事业单位的通用型网络协同办公系统。 系统秉承协同办公的思想,集成即时通讯、日记管理、通知管理、邮件管理、新闻、考勤管理、短信管理、个人文件柜、日程安排、工作计划、工作日清、通讯录、公文流转、论坛、在线调查、

下载
package com.kingh.thread.create;

/**
 * 匿名内部类的方式创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo7_Anonymous {

    public static void main(String[] args) {
        // 基于子类和接口的方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    printInfo("interface");
                }
            }
        }) {
            @Override
            public void run() {
                while (true) {
                    printInfo("sub class");
                }
            }
        }.start();
    }

    /**
     * 输出当前线程的信息
     */
    private static void printInfo(String text) {
        System.out.println(text);
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

运行结果如下:

sub class
sub class

我们可以看到,其实是基于子类的执行了,为什么呢,其实很简单,我们先来看一下为什么不基于子类的时候Runnable的run方法可以执行。这个要从Thread的源码看起,下面是我截取的代码片段。

public Thread(Runnable target)
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */

        /* If there is a security manager, ask the security manager
               what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
    g.checkAccess();

    /*
         * Do we have the required permissions?
         */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
        acc != null ? acc : AccessController.getContext();
    this.target = target; // 注意这里
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
}

其实上面的众多代码就是为了表现 this.target = target 那么target是什么呢,是Thread类的成员变量。那么在什么地方用到了target呢?下面是run方法的内容。

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

我们可以看到,如果通过上面的构造方法传入target,那么就会执行target中的run方法。可能有朋友就会问了,我们同时继承Thread类和实现Runnable接口,target不为空,那么为何不执行target的run呢。不要忘记了,我们在子类中已经重写了Thread类的run方法,因此run方法已经不在是我们看到的这样了。那当然也就不回执行target的run方法。

lambda 方式改造

刚才使用匿名内部类,会发现代码还是比较冗余的,lambda可以大大简化代码的编写。用lambda来改写上面的基于接口的形式的代码,如下

// 使用lambda的形式
new Thread(() -> {
    while (true) {
        printThreadInfo();
    }
}).start();


// 对比不使用lambda的形式
new Thread(new Runnable() {
    @Override
    public void run() {
        while (true) {
            printThreadInfo();
        }
    }
}).start();

第四种方式:定时器

定时器可以说是一种基于线程的一个工具类,可以定时的来执行某个任务。在应用中经常需要定期执行一些操作,比如要在凌晨的时候汇总一些数据,比如要每隔10分钟抓取一次某个网站上的数据等等,总之计时器无处不在。

在Java中实现定时任务有很多种方式,JDK提供了Timer类来帮助开发者创建定时任务,另外也有很多的第三方框架提供了对定时任务的支持,比如Spring的schedule以及著名的quartz等等。因为Spring和quartz实现都比较重,依赖其他的包,上手稍微有些难度,不在本篇博客的讨论范围之内,这里就看一下JDK所给我们提供的API来实现定时任务。

1. 指定时间点执行

package com.kingh.thread.create;

import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 定时任务
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo9_Timer {

    private static final SimpleDateFormat format =
            new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    public static void main(String[] args) throws Exception {

        // 创建定时器
        Timer timer = new Timer();

        // 提交计划任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时任务执行了...");
            }
        }, format.parse("2017-10-11 22:00:00"));
    }
}

2.间隔时间重复执行

package com.kingh.thread.create;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 定时任务
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo10_Timer {

    public static void main(String[] args){

        // 创建定时器
        Timer timer = new Timer();

        // 提交计划任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时任务执行了...");
            }
        },
                new Date(), 1000);
    }
}

关于Spring的定时任务,可以参考 《Spring计划任务》

第五种方式:带返回值的线程实现方式

我们发现上面提到的不管是继承Thread类还是实现Runnable接口,发现有两个问题,第一个是无法抛出更多的异常,第二个是线程执行完毕之后并无法获得线程的返回值。那么下面的这种实现方式就可以完成我们的需求。这种方式的实现就是我们后面要详细介绍的Future模式,只是在jdk5的时候,官方给我们提供了可用的API,我们可以直接使用。但是使用这种方式创建线程比上面两种方式要复杂一些,步骤如下。

  • 创建一个类实现Callable接口,实现call方法。这个接口类似于Runnable接口,但比Runnable接口更加强大,增加了异常和返回值。

  • 创建一个FutureTask,指定Callable对象,做为线程任务。

  • 创建线程,指定线程任务。

  • 启动线程

代码如下:

package com.kingh.thread.create;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 带返回值的方式
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo11_Callable {

    public static void main(String[] args) throws Exception {

        // 创建线程任务
        Callable<Integer> call = () -> {
            System.out.println("线程任务开始执行了....");
            Thread.sleep(2000);
            return 1;
        };

        // 将任务封装为FutureTask
        FutureTask<Integer> task = new FutureTask<>(call);

        // 开启线程,执行线程任务
        new Thread(task).start();

        // ====================
        // 这里是在线程启动之后,线程结果返回之前
        System.out.println("这里可以为所欲为....");
        // ====================

        // 为所欲为完毕之后,拿到线程的执行结果
        Integer result = task.get();
        System.out.println("主线程中拿到异步任务执行的结果为:" + result);
    }
}

执行结果如下:

这里可以为所欲为....
线程任务开始执行了....
主线程中拿到异步任务执行的结果为:1

Callable中可以通过范型参数来指定线程的返回值类型。通过FutureTask的get方法拿到线程的返回值。

第六种方式:基于线程池的方式

我们知道,线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。当然了,线程池也不需要我们来实现,jdk的官方也给我们提供了API。

代码如下:

package com.kingh.thread.create;

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

/**
 * 线程池
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo12_ThreadPool {

    public static void main(String[] args) throws Exception {

        // 创建固定大小的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        while (true) {
            // 提交多个线程任务,并执行
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    printThreadInfo();
                }
            });
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

执行结果如下:

当前运行的线程名为: pool-1-thread-1
当前运行的线程名为: pool-1-thread-2
当前运行的线程名为: pool-1-thread-4
当前运行的线程名为: pool-1-thread-3
当前运行的线程名为: pool-1-thread-7
当前运行的线程名为: pool-1-thread-8
当前运行的线程名为: pool-1-thread-9
当前运行的线程名为: pool-1-thread-6
当前运行的线程名为: pool-1-thread-5
当前运行的线程名为: pool-1-thread-10

线程池的内容还有非常多,这里不再详细地讲解。

更多编程相关知识,请访问:编程教学!!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

154

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

88

2026.01.26

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

214

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

192

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

61

2026.01.05

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1846

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

614

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2351

2025.12.29

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

1

2026.03.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.2万人学习

C# 教程
C# 教程

共94课时 | 10.8万人学习

Java 教程
Java 教程

共578课时 | 77.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号