Thread类的基本操作(JAVA多线程)

线程是操作系统中的概念,操作系统内核实现了线程这样的机制,并提供了一些API供外部使用。

JAVA中 Thread类 将系统提供的API又近一步进行了抽象和封装,所以如果想要使用多线程就离不开 Thread 这个类。

线程的创建(Thread类)

在JAVA中 创建线程可以有多种方法,这里简单介绍几种。

方法一:我们自己编写一个类使这个类继承自Thread类,然后重写里面的 run() 方法。

    class MeThread extends Thread {//必须要实现这个方法,此方法是新线程执行的入口方法(告诉线程应该做什么)@Overridepublic void run() {System.out.println("这是新线程执行的任务");}}public static void main(String[] args) {//创建一个线程对象MeThread t = new MeThread();//调用系统API启动线程t.start();}

当我们运行程序之后就会执行 run() 方法中的打印操作。

new 一个Thread类只是创建出了一个线程,并不会调用系统API创建线程,只有当调用了start() 方法之后才会调用系统API在系统中创建出线程并启动。(此时不理解可以,因为在介绍isAlive()方法时会验证)

这个重写的 run() 方法可以理解为一个任务,这个方法会在线程启动时自动被调用执行当线程执行完这个方法中的内容时该线程就会被销毁并且无法再次使用start()方法唤醒。

此时我们为了可以更好的呈现多线程并发编程的效果对上述代码进行了一些细微修改。

    class MeThread extends Thread {//必须要重写这个方法,此方法是新线程执行的入口(告诉线程应该做什么)@Overridepublic void run() {//此处让这个新线程每隔0.1s执行一次打印操作while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("这是创建的新线程执行的任务");}}}public static void main(String[] args) {MeThread t = new MeThread();//调用系统API启动线程t.start();//让主线程每隔0.1s执行一次打印操作while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("主线程");}}

5420a28a4bc04b979e145e656e12fa9c.png

此时可以看到两个while循环是“同时执行的” ,每个线程都是一个独立的执行流。

代码执行之后可以看到程序在无规律的进行打印,其主要原因是系统对线程的调度是随机的。 


方法二:我们自己编写一个类使实现 Runnable接口 然后重写里面的 run() 方法。

  • Thread类 实现了 Runnable接口;
  • Thread类 中的 run() 方法也是重写的 Runnable接口中的;
  • 因为在 Thread类 中提供了一个这样的构造方法:57fd3e2dbd40417aa31283ca59e7d382.png

    class MeRunnable implements Runnable{//必须要重写这个方法,此方法是新线程执行的入口@Overridepublic void run() {System.out.println("这是一个新线程执行的任务");}}public static void main(String[] args) {MeRunnable runnable = new MeRunnable();Thread t = new Thread(runnable);//调用系统API启动线程t.start();}

使用 Runnable 接口和直接继承 Thread 的区别就是:可以帮我们降低代码的耦合性也就是“解耦合”

Runnable 它表示一个可以执行的任务 而它并不关心这个任务是啥,在哪里执行;这个任务也不一定和线程强相关,因为这个代码可能使用单线程、多线程还是不使用线程或者是用其他方法(例:线程池,协程……)执行都没有任何区别。

而此时使用 Runnable 就可以将这个任务单独的提取出来,这样就可以随时改变这个任务是使用什么方法进行执行(例如:后面如果不想用线程了就可以直接在main方法中进行调用)例:

    class MeRunnable implements Runnable{@Overridepublic void run() {System.out.println("这是一个任务");}}public static void main(String[] args) {MeRunnable runnable = new MeRunnable();//此时不想使用线程执行这个任务runnable.run();}

方法三:使用匿名内部类。

    public static void main(String[] args) {Thread t = new Thread(){@Overridepublic void run() {System.out.println("这是创建的新线程执行的任务");}};//调用系统API启动线程t.start();}

因为在 Thread类 中提供了一个这样的构造方法:57fd3e2dbd40417aa31283ca59e7d382.png

所以我们可以写成过这样: 

    public static void main(String[] args) {Thread t = new Thread(new Runnable(){@Overridepublic void run() {System.out.println("这是一个新线程执行的任务");}});//调用系统API启动线程t.start();}

 方法四:因为Runnable接口是一个函数式接口,所以可以利用 Lambda表达式 来创建线程。

    public static void main(String[] args) {Thread t = new Thread(() -> System.out.println("这是创建的新线程执行的任务"));//调用系统API启动线程t.start();}

除了观看控制台的输出结果来观察多线程之外,还可以使用JDK中带有的工具 jconsole 来更形象的观测:具体方法可以跳转这里icon-default.png?t=N7T8http://t.csdnimg.cn/b8Wca

Thread类的一些常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即为线程组,这
个目前我们了解即可

Thread类中的一些常见属性:

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

getId():

获取当前线程的 id 

id 是线程的唯一身份标识,这个 id 是 JAVA 为这个线程分配的,并不是系统 API 分配的 ID ,更不是 PCB 的 ID 。

    public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {//打印异常e.printStackTrace();}System.out.println("这是创建的新线程执行的任务");}});Thread t1 = new Thread(() -> {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("这是创建的新线程执行的任务");}});t.start();t1.start();System.out.println(t.getId());//获取并打印当前线程的IDSystem.out.println(t1.getId());//获取并打印当前线程的ID}

b804bcb5dcb64387821b90b2bdf7d29e.png

isDaemon()

判断当前线程是否为后台线程。

线程默认都是前台线程。

  • 前台线程:只要该进程中还有前台线程未执行完,那么该进程就不会结束(前台线程会影响进程的结束与否);
  • 后台线程:只要该进程中的前台线程都执行完毕,那么此时无论是否有未执行完的后台线程进程都会结束(后台线程不会影响进程的结束与否)。
    public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {//打印异常e.printStackTrace();}System.out.println("这是创建的新线程执行的任务");}});t.start();System.out.println(t.isDaemon());//获取并打印当前线程是否为后台线程}

0529f0a3cabe4a7cb3c1601bc1ed7ca9.png

此时将上述代码改成后台线程。

    public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {//打印异常e.printStackTrace();}System.out.println("这是创建的新线程执行的任务");}});//将当前线程设置为后台线程t.setDaemon(true);t.start();System.out.println(t.isDaemon());}

8bd1e4d9722f48f899f64e0cb9d291f1.png

此时因为主线程(main线程)飞快的执行完了所以没有任何打印。

isAlive()

分别在线程启动前后打印判断线程是否存活(注意:线程对象存活时线程并不一定会存活)

    public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {//打印异常e.printStackTrace();}System.out.println("新线程执行完毕");});System.out.println("线程启动前");System.out.println("线程是否存活"+t.isAlive());t.start();System.out.println("线程已启动");System.out.println("线程是否存活"+t.isAlive());Thread.sleep(2000);System.out.println("线程是否存活"+t.isAlive());}

c5b74a4e3d544c9299f1eab678344a41.png

根据结论可以得知当我们创建线程对象之后并不会调用系统API创建线程,只有当调用了start() 方法之后才会调用系统API在系统中创建出线程并启动。

打断线程

在JAVA中打断线程的方法是比较唯一的:本质上都是让run()方法尽快执行结束;而在C++中是有办法可以在线程执行过程中直接销毁该线程,但是这样做有个坏处比如这个线程在写文章时突然中断了那就会令这篇文章有头无尾,而在JAVA中就可以允许在此处进行一些收尾工作。

而现实中令run()方法迟迟无法结束的原因一般都是应为循环,所以只要 结束循环就可以让线程尽快执行完run()方法,从而达到打断线程的效果。

此处介绍两种方法:

第一种方法:

可以手动创建出一个标志位,用来控制 run() 方法中循环的终止条件。

    //创建一个成员变量用来控制循环的终止条件,默认值为 false;private static boolean isQuit;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while(!isQuit) {System.out.println("新线程正在工作");try {Thread.sleep(100);} catch (InterruptedException e) {//打印异常e.printStackTrace();                }}System.out.println("新线程执行完毕");});t.start();Thread.sleep(500);isQuit = true;System.out.println("打断新线程");}

cf6542b5d12c4b8abd3f50a8b87e8445.png

但是有以下两个问题:

  1. 需要手动创建标志位;
  2. 如果循环正处在sleep状态程序将不能进行及时的响应。

第二种方法:

在JAVA中默认就有一个标志位,我们可以利用JAVA中默认的标志位来进行快速结束run()方法的操作。

好处是这样我们就不用再单独创建一个变量,不用再思考变量捕获的问题了。

  • interrupt() :该方法可以将线程中默认的标志位设置为true
  • isInterrupt():判断对象关联的线程的标志位是否被设置,调用后不清除标志位。还可以使sleep()方法抛出InterruptedException异常,来强行中断sleep()方法。

利用这两个方法就可以实现线程的打断

    public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {//Thread.currentThread()该方法是用来得到该线程的实例也就是t(哪个线程调用该方法就返回哪个线程的实例)//因为此时t还没有被创建所以不能写为t.isInterrupted()while(!Thread.currentThread().isInterrupted()) {System.out.println("新线程正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {//打印异常e.printStackTrace();}}System.out.println("新线程执行完毕");});t.start();Thread.sleep(5000);//设置标志位为truet.interrupt();System.out.println("打断新线程");}

0a7d1cbb2cb043519d03f502c3c38b12.png

结果可以看出来此时标志位确实被设置了,sleep()方法也抛出了异常,可是循环并没有被终止。

原因是sleep()方法在抛出异常之后会自动将标志位清除,而此引起的结果就和没有设置标志位是相同的。

而JAVA如此设计的原因其实就是扩大程序员的可操作空间,可以再sleep()方法抛出异常之后进行一些收尾工作。

    public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {//Thread.currentThread()该方法是用来得到该线程的实例也就是t(哪个线程调用该方法就返回哪个线程的实例)//因为此时t还没有被创建所以不能写为t.isInterrupted()while(!Thread.currentThread().isInterrupted()) {System.out.println("新线程正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {//此处可以写一些收尾工作的代码break;}}System.out.println("新线程执行完毕");});t.start();Thread.sleep(500);//设置标志位为truet.interrupt();System.out.println("打断新线程");}

37fd158250634d449c15b256f84593f7.png

线程等待

再多线程的代码中由于线程的调度是随机的,所以也就会导致每个线程的结束时间也是无法预测的,而这种情况下就会使得在有些场景下代码出现BUG。

而线程等待就是让一个线程来等待另一个线程执行结束,本质上就是来控制线程结束的顺序。

join()

实现线程等待的效果,让一个线程阻塞等待另一个线程执行结束之后再执行。

  • 等的线程:在哪个线程中调用 join 方法,哪个线程就阻塞等待;
  • 被等的线程:调用的哪个线程对象的 join 方法,哪个线程就是被等的线程,当这个线程执行完毕等的线程才会执行。

我们创建一个线程 t 让这个线程每隔一秒打印一次数据,让主线程等待该线程。

Thread t = new Thread(()->{for (int i = 0; i < 4; i++) {System.out.println("t线程执行中");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
});
t.start();
System.out.println("等待开始");
t.join();
System.out.println("等待结束");

注意:如果 t 线程已经执行结束,此时再调用 join 就会直接返回执行,不会发生阻塞等待。

join(等待时间)

上面的 join() 方法是一种“死等”的方法,只要被等待的线程不结束,那么就会一直等待下去。

但是一般情况下我们并不会死等而是等待超过了一定时间之后就不会再继续等待了,因为没有意义。

  • join(long millis)         最多等待 millis 毫秒
  • join(long millis, int nanos)       和上面的方法一样 就是时间精度更高,精确到了纳秒

休眠线程

  • sleep(long millis)  让线程休眠 millis 毫秒;
  • sleep(long millis,int nanos)  和上面的功能一样,就是精度更高。

但是下面的这一种其实意义不大,因为 sleep() 本身就存在一定的误差,并不是你写 sleep(1000) 就真的刚好等精确 1000 ms ,它还有一个调度的开销。系统会按照 1000 这个时间来休眠线程,当时间到了之后,系统会唤醒该线程(阻塞 -> 就绪),而且并不是线程进入就绪状态就能立即进入CPU执行。

//获取系统当前时间戳
long a = System.currentTimeMillis();
Thread.sleep(1000);
//获取系统当前时间戳
long b = System.currentTimeMillis();
System.out.println("时间:"+(b - a)+" ms");

而且每次运行之后结果都是不同的。

线程的状态

JAVA中线程的所有状态都存储在一个枚举类型中,Thread.State :

for (Thread.State str:Thread.State.values()) {System.out.println(str);
}

可以通过上述代码来打印所有的线程状态

通过 getState() 方法可以获取线程的状态

  • NEW:Thread对象已经有了,但还没有调用 start() 方法;
Thread t = new Thread(()->{});
System.out.println(t.getState());

  • RUNNABLE:就绪状态(线程已经在CPU上执行了或者排队准备执行)
Thread t = new Thread(()->{while(true) {}
});
t.start();
System.out.println(t.getState());

  • BLOCKED:阻塞,由于锁竞争导致的阻塞
Object lock1 = new Object();
Object lock2 = new Object();Thread t1 = new Thread(()->{synchronized(lock1) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized(lock2) {}}
});
Thread t2 = new Thread(()->{synchronized(lock2) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized(lock1) {}}
});
t1.start();
t2.start();
//让主线程等待 2 秒
Thread.sleep(2000);
//此时t1和t2两个线程会因为互相争对方的锁,而导致死锁
System.out.println(t1.getState());
System.out.println(t2.getState());

  • WAITING:阻塞由 wait 这种不固定时间的方式引起的阻塞
Object lock1 = new Object();Thread t1 = new Thread(()->{synchronized(lock1) {try {//调用wait方法让线程阻塞lock1.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}
});t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

  • TIMED_WAITING:由 sleep 这种固定时间限制的方式引起的阻塞
Thread t1 = new Thread(()->{try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}
});t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

  • TERMINATED:Thread对象还在,可是线程已经没了
Thread t1 = new Thread(()->{});t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

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

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

相关文章

中国多主数据库:压强投入,期待破茧

拿破仑曾说&#xff1a;“战争的艺术就是在某一点上集中最大优势兵力”&#xff0c;强调了力量集中的重要性。 如今&#xff0c;国际形势风云变幻&#xff0c;西方世界对中国的围剿不再仅仅体现在军事和地缘政治上&#xff0c;而更多表现在经济与科技上。在科技领域&#xff0…

Akshare获取同花顺行业

使用akshare可以很方便的获取同花顺行业列表&#xff0c;与每个行业对应的个股信息&#xff0c;流程如下&#xff1a; 使用ak.stock_board_industry_summary_ths()获取行业列表循环行业列表&#xff0c;使用ak.stock_board_industry_cons_ths()获取行业对应的个股信息 官方文…

MySQL数据库入门到大牛_02_MySQL环境搭建、演示使用、图形化管理工具、一二章练习

文章目录 1. MySQL的卸载步骤1&#xff1a;停止MySQL服务步骤2&#xff1a;软件的卸载步骤3&#xff1a;残余文件的清理步骤4&#xff1a;清理注册表&#xff08;选做&#xff09;步骤5&#xff1a;删除环境变量配置 2. MySQL的下载、安装、配置2.1 MySQL的4大版本2.2 软件的下…

Python基础(第五期): python数据容器(序列) 列表 集合 元素 字符串 字典 序列遍历操作

python基础专栏 python基础&#xff08;第五期&#xff09; 文章目录 python基础&#xff08;第五期&#xff09;数据容器一、列表1、列表的定义2、列表的下标索引 3、列表的(添加)方法3.1 列表的查询方法3.2 修改特定下标索引的值3.3 列表指定位置插入元素3.3 列表指定元素的追…

【Linux】-文件操作(重定向、缓冲区以及Linux下一切皆文件的详解)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

电脑监控软件丨2023全网最详细解析

电脑监控软件是一个比较敏感的话题&#xff0c;因为很多员工会觉得电脑监控侵犯了自己的隐私&#xff0c;电脑上企业会觉得安装软件只不过是为了保护自己的核心利益。 对于此&#xff0c;我们要辩证的看待。 今天我们从企业的角度出发&#xff0c;谈谈电脑监控软件的话题。 必…

【python VS vba】(5) 在python中使用xlwt操作Excel(待完善ing)

目录 1 什么是xlwt 2 导入xlwt 3 相关语法 3.1 创建新的workbook 3.2 创建新的sheet 3.3 保存workbook 4 python里表格的形式 4.1 矩阵 4.2 EXCEL的数据形式 完全等于矩阵的数字结构 4.3 python里矩阵 5 具体代码 5.1 代码 5.2 结果 5.3 要注意的问题 5.3.1 不能…

NOIP2023模拟12联测33 D. 滈葕

NOIP2023模拟12联测33 D. 滈葕 文章目录 NOIP2023模拟12联测33 D. 滈葕题目大意思路code 题目大意 思路 放一段题解的材料 ABO 血型系统是血型系统的一种&#xff0c;把血液分为 A,B,AB,O 四种血型。血液由红细胞和血清等组成&#xff0c;红细胞表面 有凝集原&#xff0c;血清…

LangChain+LLM实战---Midjourney(v5.1) Prompt深度剖析

原文&#xff1a;Anatomy of Midjourney Promps: In-Depth Study for effective Prompting Strategies — V5.1 examples 作者&#xff1a;Michael King 你是否曾经发现自己盯着Midjourney的空白画布&#xff0c;手指悬停在键盘上&#xff0c;让我问自己&#xff1a;“我应该…

kafka问题汇总

报错1&#xff1a; 解决方式 1、停止docker服务   输入如下命令停止docker服务 systemctl stop docker 或者service docker stop1   停止成功的话&#xff0c;再输入docker ps 就会提示出下边的话&#xff1a; Cannot connect to the Docker daemon. Is the docker daem…

方案分享:F5机器人防御助企业应对复杂攻击

企业是Bot攻击者的目标&#xff0c;网络犯罪分子会不断调整他们的攻击&#xff0c;来攻破愈发成熟的Bot防护&#xff0c;这使企业安全团队时刻处于紧张状态。如果不能有效地管理Bot&#xff0c;应用性能、客户体验和业务都会被影响&#xff0c;但在尝试阻止这些攻击时&#xff…

k8s service

文章目录 Service 基础概念Service 类型&#xff1a;Service 的工作流程&#xff1a;东西流量&#xff0c;南北流量NodePortLoadBalancer Service 基础概念 在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;Service 是一个抽象的概念&#xff0c;表示一个应用程序的逻…

计算机毕设 基于大数据的服务器数据分析与可视化系统 -python 可视化 大数据

文章目录 0 前言1 课题背景2 实现效果3 数据收集分析过程**总体框架图****kafka 创建日志主题****flume 收集日志写到 kafka****python 读取 kafka 实时处理****数据分析可视化** 4 Flask框架5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&a…

fio数据整理之二

fio数据简单抓取 上文我们完成了一些fio output数据的简单抓取&#xff0c;本文将针对抓取的数据做进一步的处理&#xff0c;输出到表格之中&#xff0c;方便我们查看&#xff0c;统计结果。 本文先使用最简单的方法创建csv档案 我们现有个基本认知&#xff0c;在csv档案中&am…

Corel VideoStudio 会声会影2024剪辑中间的视频怎么删 剪辑中音乐太长怎么办

我很喜欢视频剪辑软件Corel VideoStudio 会声会影2024&#xff0c;因为它使用起来很有趣。它很容易使用&#xff0c;但仍然给你很多功能和力量。视频剪辑软件Corel VideoStudio 会声会影2023让我与世界分享我的想法&#xff01;“这个产品的功能非常多&#xff0c;我几乎没有触…

【MySQL数据库】 四

本文主要介绍了mysql数据库的几种常见的约束. 一.数据库约束 我们希望存储的数据是靠谱的,mysql提供一些机制来辅助我们自动的依赖程序对数据进行检查 . 这类查数据的机制,就是约束 一旦约束好了,后续在进行增 删 改的时候,mysql就会自动的对修改的数据做出检查,如果不符合…

openGauss学习笔记-116 openGauss 数据库管理-设置数据库审计-审计概述

文章目录 openGauss学习笔记-116 openGauss 数据库管理-设置数据库审计-审计概述116.1 背景信息116.2 操作步骤 openGauss学习笔记-116 openGauss 数据库管理-设置数据库审计-审计概述 116.1 背景信息 数据库安全对数据库系统来说至关重要。openGauss将用户对数据库的所有操作…

服务器搭建:从零开始创建自己的Spring Boot应用【含登录、注册功能】

当然&#xff0c;你可以先按照IDEA搭建SSM框架【配置类、新手向】完成基础框架的搭建 步骤 1&#xff1a;设计并实现服务器端的用户数据库 在这个示例中&#xff0c;我们将使用MySQL数据库。首先&#xff0c;你需要安装MySQL并创建一个数据库以存储用户信息。以下是一些基本步…

后端工程化 | SpringBoot 知识点

文章目录 [SpringBoot] 后端工程化1 需求2 开发流程3 RequestController 类&#xff08;操作类&#xff09;3.1 简单参数&#xff08;形参名和请求参数名一致&#xff09;3.2 简单参数&#xff08;形参名和请求参数名不一致&#xff09;3.3 复杂实体参数3.4 数组参数3.5 集合参…

COOHOM通过采用亚马逊云科“专库专用”的方式,为云原生的构建提供稳定的数据支撑

全球化浪潮下&#xff0c;面对全球化业务发展带来的新需求与新挑战&#xff0c;越来越多的企业开启了云原生构建旅程&#xff0c;以推动业务系统快速迭代&#xff0c;为国际业务的拓展打下坚实的基础。COOHOM是杭州群核信息技术有限公司旗下的国际化品牌。为全球企业和个人提供…