您现在的位置: 首页> 观点 > 正文
快报:详解java多线程锁
发布时间:2023-03-22 08:25:46 来源:腾讯云

java多线程锁

多线程程序是并发编程的核心,而Java多线程锁则是保证线程安全的重要手段。但是,不同类型的锁适用于不同的场景,而正确地选择锁对于程序的性能和正确性至关重要。在本文中,我们将深入探讨Java多线程锁的工作原理和最佳实践。

多线程模型

Java的多线程模型是基于线程的抢占式调度机制,它允许多个线程同时执行,并且使用共享内存来实现线程间通信。


【资料图】

可以看出,java可以创建多个线程并行执行,读写的内存都是同一个进程虚拟内存的数据,就可能会导致出现问题:

public class Main {    static int a = 1;    public static void main(String[] args) throws Exception {        Runnable r = () -> {            for (int i = 0; i < 100000; i++) {                //读取a的值                a = a + 1;            }        };        Thread t1 = new Thread(r);        t1.start();        Thread t2 = new Thread(r);        t2.start();        t1.join();        t2.join();        System.out.println(a);    }}

输出:

可以看出,在2个线程同时运行的情况下,a的值并不是完整的20万,而是小于20万,原因就是在运行的过程中,多线程出现了数据竞争和内存一致性的问题:

a的值为1t1线程获取到a的值为1t2线程获取到a的值为1t1将值更新为2t2将值更新为2...由于t1和t2是同时运行的,所以就出现了获取到相同的值,更新又更新到了相同的值的情况

同样的例子还有很多,正是因为多线程下内存共享的问题,那么该如何解决这个问题呢?

java内存模型

首先我们需要简单了解一下java的内存模型结构:

什么是本地内存?

JMM(Java Memory Model) java内存模型,是在特定的操作协议下,对特定的内存或者高速缓存进行读写访问的过程抽象描述,不同架构下的物理机拥有不一样的内存模型,Java虚拟机是一个实现了跨平台的虚拟系统,因此它也有自己的内存模型

在进程概念中,并没有本地内存的概念,只有进程内存,它是一个虚拟的概念.

在线程运行时,需要先把线程所需的内存数据提取到cpu cache中,然后压入寄存器进行cpu运算:

可以看出,在jMM内存模型中,除了进程的虚拟内存区域之外,额外存在一个本地内存的虚拟区域概念,这个内存只对线程本身可见,属于线程运行时所需要用到的高速缓存 (其实所有语言的进程都会有这个内存概念)

当然,JMM不仅仅是只有这一个概念,它还包括了java在编译优化代码时候的重排序,内存屏障等概念,这个暂时不讲.

顺序一致性

在上面的例子我们可以看到,a的值不正确的原因是2个线程同时读取,或者同时写入值,导致了结果不正确,那么,如果变量的读写都保证一个顺序,是不是就不会出现这个问题呢?我们先假设a+=1这个命令只需要执行一次,而不是先获取a,再赋值a

在顺序一致性模型中,所有变量在同一时间被一个线程获取,其他线程需要等待,线程实现了按照顺序的串行执行,这样就使得了数据正确但是,这样多线程的优势就没了,所有线程都是串行化执行,不能并发执行,同时这里面还有一个重排序的问题

在上面,我们有一个假设,那就是:假设a+=1这个命令只需要执行一次,而不是先获取a,再复制a,2次操作,如果是2次操作,会发生什么问题呢?

可以看出,当分成2次操作的时候,其实产生了一个临时变量t,在获取a=1,时,存储了这个1值,然后再将1+1写入给了a由于是分成了2步操作,在线程执行的时候,先后顺序可能是不一致的,就又会导致变量更新出错的问题,

所以,在多线程环境下,是无法保证顺序一致性的这个语义的

重排序

在上面的多线程顺序一致性例子中,我们知道了多线程情况下,如果获取+写入的不再同一个位置执行,就会出现与预期结果不符的问题

在单线程情况下,cpu,编译器为了提高并行度的情况下,也会对操作指令做出更改,例如:

int a = 0;        int b = 0;        a = 1;        b = a;        a = b+1;        b = a+1;        a = b+1;        b = a+1;

重排序下,可能会出现

a = 1;        a = b+1;        a = b+1;        b = a;        b = a+1;        b = a+1;

这个时候,代码和预期结果是不一样的,理论上是不允许出现的

编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性(上面的a和b语句互相依赖),编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序

int a = 0;        int b = 0;        a++;        b++;        a++;        b++;        a++;        b++;

在这个例子中,需要循环的给a和b操作数据,cpu为了优化,则可能会优化成:

int a = 0;        int b = 0;        a++;        a++;        a++;        b++;        b++;        b++;

这个时候,执行顺序其实是已经变了,但是这个执行顺序并不会对程序的结果造成影响,这个叫做as-if-serial语义

as-if-serial语义只能确保单线程不会出现问题,如果是在多线程上,就算是没有遵守了没有互相依赖的重排序,也可能会导致改变程序的执行结果

public class Main {    static int flag = 0;    static int a = 0;    public static void main(String[] args) throws Exception {        Runnable r1 = () -> {            flag = 1;            a = 1;        };        Runnable r2 = () -> {            if (flag == 1) {                int c = a * a;            }        };        Thread t1 = new Thread(r1);        Thread t2 = new Thread(r2);        t1.start();        t2.start();        t1.join();        t2.join();    }}

在这个例子中,t1线程中的flag和a并没有依赖关系,所以可能会重排序,t2的flag和c也没有依赖关系,所以可能会出现:

在多线程的情况下,虽然对没有依赖关系的语句进行了重排序,但是实际上已经改变了执行的结果

as-if-serial语义

as-if-serial语义的意思是:不管怎么重排序(编译器和处理器为了提高并行度),(单线程) 程序的执行结果不能被改变。编译器、runtime和处理器都必须遵守as-if-serial语义。

内存屏障

为了保证内存的可见性,java编译器会在生成指令的适当位置插入内存屏障来禁止特定类型的重排序,,JMM把内存屏障指令分为4类:

这个表如果不好理解,可以粗俗的理解为: Load (读取内存必须是读取最新的),Store(存储必须刷新到内存)StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他3个屏障的效果。现代的多处 理器大多支持该屏障(其他类型的屏障不一定被所有处理器支持)。执行该屏障开销会很昂 贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(Buffer Fully Flush)。

happens-before

在JMM中,提出了happens-before的概念用于实现as-if-serial语义,

happens-before 指定了2个操作之间的执行顺序,如果 a happen-before B,那么A的执行顺序必须排在B之前

这个原则就是:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化,怎么排序都行

注意,是单线程程序,和 正确同步的多线程程序,多线程需要正确同步.

线程同步

在多线程编程中,正确同步指的是在多个线程之间共享的数据和资源被正确地访问和更新,从而避免了竞态条件、死锁和其他的并发问题。这种同步是通过使用同步机制(如锁、信号量、条件变量等)和原子操作(如原子加、原子比较交换等)来实现的。

一个正确同步的多线程程序是指程序中的多个线程能够正确地共享数据和资源,而不会出现竞态条件、死锁等问题,并且程序能够正确地执行并达到预期的结果。在这种情况下,编译器和处理器的优化和排序不会影响程序的正确性和执行结果。

重点在于以下几点1:共享变量的可见性,如果修改了变量,在其他线程能马上获取到最新的值,也就是线程本地内存不要缓存旧值2:共享变量的线程安全性,多个线程访问同一个变量时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个变量的行为都可以获得正确的结果,那么这个变量就是线程安全的。3:原子操作,如果对一个变量的操作是原子性的(不会出现先获取,再加值),就不会出现错误的结果4:同步机制,如果多线程在同一时间只会有一个线程在操作变量,就不会出现线程共享问题

CAS

CAS的全称为Compare-And-Swap,直译就是对比交换。是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值。经过调查发现,其实现方式是基于硬件平台的汇编指令,就是说CAS是靠硬件实现的,JVM只是封装了汇编调用,那些AtomicInteger类便是使用了这些封装后的接口。 简单解释:CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下在旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。

CAS锁可以保证变量的原子操作

public class Main {    static AtomicInteger a = new AtomicInteger(1);    public static void main(String[] args) throws Exception {        Runnable r = () -> {            for (int i = 0; i < 100000; i++) {                int oldValue, newValue;                do {                    //读取a的值                    oldValue = a.get();                    newValue = oldValue + 1;                } while (!a.compareAndSet(oldValue, newValue));            }        };        Thread t1 = new Thread(r);        t1.start();        Thread t2 = new Thread(r);        t2.start();        t1.join();        t2.join();        System.out.println(a.get());    }}

将int改为AtomicInteger类型,在每次+1时通过CAS方式+1,判断如果旧值不对,则循环更新,直到更新成功

可以看出,CAS的更新需要判断成功和失败,如果目的就是更新,那失败之后就还得重复的去尝试更新,也就是自旋.

CAS ABA问题

ABA问题是指在进行CAS操作时,如果一个变量的值先被修改为B,再被修改回A,此时CAS操作会认为该变量的值没有被修改过,从而可能导致出错。解决这些问题的方式主要是使用版本号控制,即在每次修改变量时,都增加一个版本号,这样可以解决ABA问题。另外,为了避免循环时间长问题,可以设置一个尝试次数的上限,如果超过这个上限仍然没有成功,就放弃操作。

synchronized关键字

synchronized关键字用于实现线程同步,它可以用于方法或代码块上。

作为方法修饰符使用synchronized关键字时,它可以确保在同一时间内只有一个线程可以进入被修饰的方法,其他线程必须等待该方法执行完成后才能进入。这样就避免了多个线程同时访问共享资源时可能引发的数据竞争和并发问题。

作为代码块使用synchronized关键字时,它可以确保在同一时间内只有一个线程可以执行该代码块中的代码,其他线程必须等待当前线程执行完该代码块后才能执行。这样可以在并发环境下保证共享资源的安全访问。

public class Main {    static  int a = 1;    public static void main(String[] args) throws Exception {        Runnable r = () -> {            for (int i = 0; i < 100000; i++) {                //读取a的值                synchronized (Main.class){                    a++;                }            }        };        Thread t1 = new Thread(r);        Thread t2 = new Thread(r);        t1.start();        t2.start();        t1.join();        t2.join();        System.out.println(a);    }}

通过synchronized关键字,可以使得一个代码块的代码每次只会有一个在运行,也就是实现了该代码块的顺序同步问题,所以不会出现问题

图示:

对于普通同步方法,锁是当前实例对象。对于静态同步方法,锁是当前类的Class对象。对于同步方法块,锁是Synchonized括号里配置的对象。

synchronized内存语义

在增加synchronized关键字后,JMM到底做了什么呢?首先,在增加synchronized关键字后,

执行的代码块将会先尝试获取锁,没有获得的话将阻塞等待获得锁的代码块开始读取主内存的变量数据,写入到本地内存执行代码,将更新后的变量写入到本地内存释放锁,将本地内存写入到主内存,并向阻塞等待锁的线程发消息等待锁的代码块开始获得锁,读取最新的内存开始执行...

happens-before关系

synchronized的happens-before关系取决于谁先获取的锁,大致为

获取锁的代码块 happens-before 代码块本身代码块本身 happens-before 释放锁释放锁 happens-before 等待获取锁的其他线程代码块....

可以看出,有synchronized关键字的代码块,将严格执行happens-before关系,先获得锁代码块A一定是happens-before代码块B这样就使得了程序运行正确

synchronized的实现原理

synchronized用的锁存在于java的对象头里,根据具体锁的对象进行获取/释放锁

当线程尝试获得锁之后,将更新java的对象头新增锁的标识,表示这个锁已经被这个线程获取,其他线程将阻塞

为了减少获得锁和释放锁带来的性能消耗,在Java SE 1.6之后引入了 偏向锁轻量级锁,锁一共有4种状态

无锁偏向锁轻量级锁重量级锁

偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得,为了使得获得锁的代价更低,所以引入了偏向锁:

当一个线程访问同步块获得锁后,会在对象头和栈帧中记录偏向锁的线程id以后只要是该进程获得和释放锁都不在需要进行CAS操作,而是只要判断是这个线程id就可以

如果判断失败,则需要判断 偏向锁标识是否为1,如果是则通过CAS尝试将线程id修改为当前id,否则通过CAS竞争获得锁(修改线程id+偏向锁标识1)

轻量级锁

当另一个线程获取偏向锁失败后,说明已经有线程占用了锁,那么这个线程就会尝试自旋获取锁,此时则升级为了轻量级锁

重量级锁

轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成 功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁.

重量级锁会使得线程阻塞,等待线程被唤醒释放

锁的优缺点对比

volatile关键字

volatile关键字将会使得一个变量的单次读写都是实时对其他线程同步的

volatile特性

volatile只是对单次读和写做出了同步,只能保证单次的读/写 是原子性的

回到一开始的例子

public class Main {    static volatile int a = 1;    public static void main(String[] args) throws Exception {        Runnable r = () -> {            for (int i = 0; i < 100000; i++) {                //读取a的值                a = a + 1;            }        };        Thread t1 = new Thread(r);        t1.start();        Thread t2 = new Thread(r);        t2.start();        t1.join();        t2.join();        System.out.println(a);    }}

在这个例子中,并不能保证a的值就是20001,而是依然小于,那是因为 a=a+1是2个步骤:

线程1读取a的值,因为加了volatile关键字,所以会立即读取到主内存的最新值,比如是1 线程2读取a的值,因为加了volatile关键字,所以会立即读取到主内存的最新值,比如是1 线程1将a的值+1,然后将a=2的值立即写入到主内存线程2依然会将值+1,然后将a=2的值立即写入到主内存

这样依然会造成a的值不正确的情况总而言之,volatile自身具有以下特性

可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写 入。 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不 具有原子性。

volatile内存语义

线程A写一个volatile变量,实质上是线程A向接下来将要读这个volatile变量的某个线程 发出了(其对共享变量所做修改的)消息。线程B读一个volatile变量,实质上是线程B接收了之前某个线程发出的(在写这个volatile 变量之前对共享变量所做修改的)消息。线程A写一个volatile变量,随后线程B读这个volatile变量,这个过程实质上是线程A通过 主内存向线程B发送消息。

volatile原理

在上面我们了解到了,多线程的变量共享问题主要问题在于多了一个线程本地内存,volatile关键字会额外给CPU指令增加上LOCK前缀

在cpu执行时,LOCK会使得操作的变量立即回写回内存,这样可以保证更新变量后,内存永远存储一个最新的变量值在cpu回写之后,会使得cpu cache的变量缓存立即失效,这样可以保证其他线程读取的变量不会是缓存,而是是最新的变量值.

为了实现volatile内存语义,JMM将限制重排序:

当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保 volatile写之前的操作不会被编译器重排序到volatile写之后。当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保 volatile读之后的操作不会被编译器重排序到volatile读之前。当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。

编译器在生成字节码时,会在指令序列中插入内存屏障来 禁止特定类型的处理器重排序。对于编译器来说,发现一个最优布置来最小化插入屏障的总 数几乎不可能。为此,JMM采取保守策略。下面是基于保守策略的JMM内存屏障插入策略。

在每个volatile写操作的前面插入一个StoreStore屏障。 在每个volatile写操作的后面插入一个StoreLoad屏障。 在每个volatile读操作的后面插入一个LoadLoad屏障。 在每个volatile读操作的后面插入一个LoadStore屏障。例如:

懒汉单例模式

在java程序中,懒汉单例的实现如下:

package org.example;public class UnsafeLazyInstance {    private static UnsafeLazyInstance instance;    public static UnsafeLazyInstance getInstance() {        if (instance == null) {            instance = new UnsafeLazyInstance();        }        return instance;    }}

这个单例并非是线程安全的,主要有2个问题1:new 一个对象,这个步骤并不是原子性的,创建对象的步骤为:

分配对象内存空间设置instance指向对象的内存空间初始化对象

2:getInstance这个方法是非同步方法,在多线程同时调用时,可能会2个线程都进入到instance==null,然后创建2个对象

基于volatile和synchronized的双重解决方案

package org.example;public class SafeLazyInstance {    private static volatile SafeLazyInstance instance;    public static SafeLazyInstance getInstance() {        if (instance == null) {            synchronized (SafeLazyInstance.class){                if (instance == null){                    instance = new SafeLazyInstance();                }            }        }        return instance;    }}

为什么synchronize放在了if里面?因为如果声明到方法中的话,每次调用getInstance都会加锁,但是实际上不需要加锁,因为大多数情况都是只需要返回instance对象,而且instance除了初始化,其他时候都不会被修改

为什么synchronize需要双重检查?因为方法并没有实现同步,可能会出现多次调用进入==null的情况

基于类初始化的解决方案

JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在 执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。 基于这个特性,可以实现另一种线程安全的延迟初始化方案(这个方案被称之为 Initialization On Demand Holder idiom)。

package org.example;public class SafeLazyInstance {    private static class InstanceHolder {//额外加载一个InstanceHolder类        public static final SafeLazyInstance instance = new SafeLazyInstance();//final常量,确保只会赋值一次    }    public static SafeLazyInstance getInstance() {        return InstanceHolder.instance;    }}

java锁

Lock接口

而Java SE 5之后,并发包中新增 了Lock接口(以及相关实现类)用来实现锁功能,它提供了与synchronized关键字类似的同步功 能,只是在使用时需要显式地获取和释放锁。虽然它缺少了(通过synchronized块或者方法所提 供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以 及超时获取锁等多种synchronized关键字所不具备的同步特性。

public class Main {    static volatile int a = 1;    public static void main(String[] args) throws Exception {        Lock lock = new ReentrantLock();        Runnable r = () -> {            for (int i = 0; i < 100000; i++) {                lock.lock();                try{                    //读取a的值                    a = a + 1;                }finally {                    lock.unlock();                }            }        };        Thread t1 = new Thread(r);        t1.start();        Thread t2 = new Thread(r);        t2.start();        t1.join();        t2.join();        System.out.println(a);    }}

通过锁也可以实现synchronized关键字的功能,但是2者之间还是有一些区别的

Lock接口提供的synchronized关键字不具备的主要特性

当然,java除了ReentrantLock之外,还有Mutex,读写锁等,读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读 线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写 锁,使得并发性相比一般的排他锁有了很大提升。

这种不另外说明

标签:

快报:详解java多线程锁

多线程程序是并发编程的核心,而Java多线程锁则是保证线程安全的重要手段。但是,不同类型的锁适用于不...

每日热议!召开周例会的通知

1、通知称谓:兹定于*年*月*日(具体时间)在***(地方)举行**公司周例会,会议将讨论***,表决***(事情),请

【全球播资讯】慈溪市气象台发布大雾黄色预警【Ⅲ级/较重】

慈溪市气象台发布大雾黄色预警【Ⅲ级 较重】

即时焦点:续约徐正源,成都冲亚冠,谢晖亲承被禁2个窗口大连人保级有难度

徐正源得到了俱乐部和球迷的认可,能够续约也是理所当然的。亚冠联赛,今年球队在引援上可谓是下足了功...

全球报道:根据张桂梅校长事迹改编,袁泉大山里的女校被搁置?官微辟谣

最近有消息说,袁泉主演的《大山里的女校》项目搁置了。官微看不下去了,直接发文回应:“非常感谢大家...

百事通!国光电器:累计回购约1595万股 占比3.41%

国光电器(SZ002045,收盘价:15 39元)3月21日晚间发布公告称,截至2023年3月21日,公司通过股份回购专...

天天头条:飞利浦剃须刀头拆解图解_飞利浦剃须刀头拆解图

1、按下剃须刀正面的按钮,打开剃须刀头,简单观察其内部结构。2、转动中间的旋钮,轻轻拉出整个钻头,...

焦点热门:公交及时回复17路站牌、站点环境及车型问题|市公交总公司新增定制公交 畅通企业出行路……快来看最新一期《绿动铜都》

公交及时回复17路站牌、站点环境及车型问题|市公交总公司新增定制公交畅通企业出行路……快来看最新一...

观热点:陕西省太白县发布大雾黄色预警

陕西省太白县发布大雾黄色预警

当前通讯!燕麦科技:3月20日融资净买入109.47万元,连续3日累计净买入178.15万元

3月20日,燕麦科技(688312)融资买入214 96万元,融资偿还105 5万元,融资净买入109 47万元,融资余...

热头条丨进口增值税缴款书在哪里打印_进口增值税

1、一样的。2、只不过,进口增值税由海关代为征收,没有增值税专用发票抵扣联,取得海关进口增值税完税...

全球热资讯!金刚网防盗窗怎么安装_金刚网防盗窗

1、金刚网防盗窗顾名思义,就是相对比于传统意义上的防盗窗产品而言具有更为出色防护作用以及价值的产品...

观天下!斗罗251:千仞雪成神后,做的第一件事是什么?唐三一句话回应她

​​斗罗大陆第251集已经更新,在这一集中,千仞雪终于成为天使之神,再次降临星斗大森林,要找唐三算总...

环球速讯:万华化学:2022年归母净利润为162.3亿元,同比下降34.1%

万华化学于2023年3月21日披露年报,公司2022年实现营业总收入1655 65亿元,同比增长13 8%;实现归母净...

全球快播:华懋科技:2022年净利润同比增长12.68% 拟10派1.87元

南方财经3月20日电,华懋科技发布2022年年度报告,2022年实现营业收入16 37亿元,同比增长35 75%;归...

全球短讯!黄金概念板块走强 机构称金价有望保持上行趋势

截至发稿,四川黄金、宝莫股份涨停,中润资源、湖南黄金、赤峰黄金等跟涨。

头条焦点:原神温迪武器推荐四星_原神温蒂武器

1、温迪四星武器推荐选择:祭礼弓(增加对怪物控制时间)或者西风猎弓(提高充能效率,加快施法技能和大...

每日速递:360可能导致云服务器出现这些问题

解决方案:找替代方案,比如火绒,但是火绒的病毒库不如360,各有利弊吧,自己选适合自己的

全球新消息丨一周涨价40%,鸡蛋成为阿根廷价格涨幅最大商品

App3月20日消息,当地时间3月19日,阿根廷国家统计与普查研究所发布一份统计报告指出,近一周来,阿根廷...

当前简讯:安徽顺驰网络科技有限公司

1、安徽顺驰网络科技有限公司成立于珠城。2、是皖北地区互联网+传媒+推广的一家新形态的企业。3、也是蚌...

当前速递!怼怼三国最厉害的阵容_怼怼三国最佳阵容

1、「霍尔姆运输舰队肯佩斯」「霍尔姆部署舰队鲁伊思」「伦塔特圣城巴林教父」「伦塔特圣城」「伦塔特圣...

世界热议:搭载1.3T发动机,2024款奔驰GLB官图发布,竞争宝马X1

日前,Auto情报处从相关渠道获得了,2024款奔驰GLB的官图和相关信息,新车主要对一些细节之处做出调整,...

当前关注:新科技“加速跑”为高质量发展插上“翅膀”

央视网消息:除了春耕的田间地头,在经济发展、生态保护、文化创新等领域,科技的助力也为高质量发展插...

今日快看!帽子的英文怎么写_帽子的英文

1、常用cap和hatcap英音:[kæp]美音:[kæp]无边便帽;制服帽;(表示职业,等级等)帽子hat英

当前焦点!3.18全国爱肝日 | 长沙市第一医院开展义诊送健康活动

2023年3月18日是第23个全国爱肝日,为了提高广大群众对肝炎的认识,主动体检了解肝脏健康状况,全面遏制...

【当前热闻】小沈龙脱口秀段子完整台词_小沈龙脱口秀全集台词

世界速看:商标续展啥意思_商标续展是什么意思

【天天速看料】调皮孩子拉走椅子致童星摔至尿失禁,要靠尿不湿度过

全球最新:弥猴桃的功效与作用是什么(弥猴桃的功效)

天天快看点丨林内龙过脉森林步道_关于林内龙过脉森林步道简述

当前播报:做胃镜的危害_做胃镜的危害

焦点速读:强迫戴阴环的大学生女友_强迫戴阴环的大学生

世界讯息:海口一日游最佳攻略_海口有什么好玩的景点

动态:适合早上摆摊卖的早餐_早上地摊小吃什么最火

看热讯:银之杰(300085.SZ):董事冯军完成减持161.77万股

世界快资讯:恒生指数收涨1.64% 本周累涨1.03%

世界今热点:三河、燕郊开始全面大清洗!

今日热讯:今日现货白银价格最新查询(3月16日)

聚焦:开英菲尼迪的都很有钱吗?一般什么人开英菲尼迪QX50

天天要闻:然今遣汝者翻译_然今卒困于此 此天之亡我 非战之罪也 是什么意思

当前观点:人工智能7大应用领域_人工智能在生活中有哪些应用

环球快报:乌克兰大妈手撕无人机,泽连斯基老婆讲抗俄神剧,美政客在一旁笑

天天看热讯:文灿股份:与力劲科技子公司签订设备采购框架协议

天天要闻:盗墓笔记:七星鲁王宫

每日速讯:土耳其震区强降雨已致15人死亡

当前资讯!2020年国家公务员准考证打印_2019年国家公务员考试准考证打印入口

世界看点:许昌市襄城县麦岭镇:助力抗旱浇麦保丰收

环球新动态:电脑回收站的文件清空了怎么还原_电脑回收站的文件清空了怎么恢复

天天快播:提车不到一个月就因“幽灵刹车”追尾,长安深蓝引车主安全担忧

【世界快播报】护宝生死恋

世界看热讯:230315 MAMAMOO+单曲1[ACT 1, SCENE 1] Chico malo玟星 概念预告

焦点信息:林师傅在首尔下载地址_林师傅在首尔下载

焦点快报!原尚股份:2022年年归母净利润同比增22.19%,拟每10股派2.8元

天天观焦点:痘痘变成瘊子简介

要闻:美国通胀符合预期 国际金价收跌丨工行连线

每日头条!苏南硕放国际机场怎么去无锡太湖国际博览中心

每日头条!ST三圣:公司石膏矿开采工作正在有序推进中

速讯:口怎么组词的近义词

精选!3月14日基金净值:易方达产业升级混合A最新净值0.8283,跌1.32%

速看:曝梅西父亲向沙特球队开价6亿欧具体详细内容是什么

今日看点:4岁男孩磕断两颗门牙 同一位医生的两份诊断书引争议

世界通讯!雅迪电动车刷新行业服务时效纪录

世界观察:人工智能新材料研发公司幻量科技完成数千万元种子轮融资 红杉中国种子基金领投

【时快讯】红米游戏手机散热怎么样

天天热议:伦敦金属交易所LME:铜库存减少750吨

热议:“香港女神”陈加玲:因太漂亮被逼拍三级片,隐退后今52岁成富婆

世界观天下!华素片是治什么病的_华素片是治什么的

当前速读:成都电动车逆行会被抓拍处罚吗?

天天看点:英首相在历史遗迹建私人泳池,耗电巨大当地电网必须升级

环球热点评!异动快报:葵花药业(002737)2023年03月13日14时17分触及涨停板

焦点关注:法国食品价格飞涨 近七成法国人因缺乏预算改变饮食习惯

热议:桑树_说一说桑树的简介

天天快讯:耙耙柑里面流水能吃吗?

每日简讯:宫颈癌疫苗九价年龄范围_宫颈癌疫苗九价年龄

每日焦点!植树节,这些古树名木你认识吗?

x 广告
x 广告

Copyright ©  2015-2022 纵横自然网版权所有  备案号:浙ICP备2022016517号-12   联系邮箱:51 46 76 11 3 @qq.com