Java线程和操作系统线程的关系

2023-10-29

传统进程的缺点

  • fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用,即使使用现代的写时复制(copy-on-write)技术。
  • 各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。

多线程的优缺点
线程:其实可以先简单理解成cpu的一个执行流,指令序列。多支持多线程的程序(进程)可以取得真正的并行(parallelism),且由于共享进程的代码和全局数据,故线程间的通信是方便的。它的缺点也是由于线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。
轻量级进程LWP
既然称作轻量级进程,可见其本质仍然是进程,与普通进程相比,LWP与其它进程共享所有(或大部分)逻辑地址空间和系统资源,一个进程可以创建多个LWP,这样它们共享大部分资源;LWP有它自己的进程标识符,并和其他进程有着父子关系,父进程);这是和类Unix操作系统的系统调用vfork()生成的进程一样的。
LWP由内核管理并像普通进程一样被调度。Linux内核是支持LWP的典型例子。Linux内核在 2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程,通过参数决定子进程和父进程共享的资源种类和数量,这样就有了轻重之分。在内核中, clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork()、vfork()系统调用的最终实现。

在大多数系统中,LWP与普通进程的区别也在于它只有一个最小的执行上下文和调度程序所需的统计信息,而这也是它之所以被称为轻量级的原因。
因为LWP之间共享它们的大部分资源,所以它在某些应用程序就不适用了;这个时候就要使用多个普通的进程了

用户线程
用户线程指的是完全建立在用户空间的线程库,用户线程的建立,同步,销毁,调度完全在用户空间完成,不需要内核的帮助。因此这种线程的操作是极其快速的且低消耗的。

操作系统线程模型

线程实现在用户空间下

当线程在用户空间下实现时,操作系统对线程的存在一无所知,操作系统只能看到进程,而不能看到线程。所有的线程都是在用户空间实现。在操作系统看来,每一个进程只有一个线程。过去的操作系统大部分是这种实现方式,这种方式的好处之一就是即使操作系统不支持线程,也可以通过库函数来支持线程。

我们换一种通俗的方式来讲解这段话,首先就是在这在模型下,程序员需要自己实现线程的数据结构、创建销毁和调度维护。也就相当于需要实现一个自己的线程调度内核,而同时这些线程运行在操作系统的一个进程内,最后操作系统直接对进程进行调度。

image.png

这样做有一些优点,就是线程的调度只是在用户态,减少了操作系统从内核态到用户态的切换开销。
当然缺点也很明显:这种模式最致命的缺点也是由于操作系统不知道线程的存在,因此当一个进程中的某一个线程进行系统调用时,比如缺页中断而导致线程阻塞,此时操作系统会阻塞整个进程,即使这个进程中其它线程还在工作

线程实现在操作系统内核中

内核线程就是直接由操作系统内核(Kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核就叫做多线程内核(Multi-Threads Kernel)。

通俗的讲就是,程序员直接使用操作系统中已经实现的线程,而线程的创建、销毁、调度和维护,都是靠操作系统(准确的说是内核)来实现,程序员只需要使用系统调用,而不需要自己设计线程的调度算法和线程对CPU资源的抢占使用

目前的Linux已经基于NPTL实现了更符合POSIX标准的线程,之前的LinuxThreads早已经被替代。Pthread库目前也是基于NPTL实现,已经是一种符合POSIX标准的线程模型了,Linux摆脱了原来LWP的阴影。

使用用户线程加轻量级进程混合实现

在这种混合实现下,即存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。在这种混合模式中,用户线程与轻量级进程的数量比是不定的,即为N:M的关系:

Linux线程的发展

一直以来, linux内核并没有线程的概念. 每一个执行实体都是一个task_struct结构, 通常称之为进程. Linux内核在 2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程。在内核中, clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork()、vfork()系统调用的最终实现。后来为了引入多线程,Linux2.0~2.4实现的是俗称LinuxThreads的多线程方式,到了2.6,基本上都是NPTL的方式了

模型一 :LinuxThreads

linux 2.6以前, pthread线程库对应的实现是一个名叫linuxthreads的lib.这种实现本质上是一种LWP的实现方式,即通过轻量级进程来模拟线程,内核并不知道有线程这个概念,在内核看来,都是进程。

Linux采用的“一对一”的线程模型,即一个LWP对应一个线程。这个模型最大的好处是线程调度由内核完成了,而其他线程操作(同步、取消)等都是核外的线程库函数完成的。

linux上的线程就是基于轻量级进程, 由用户态的pthread库实现的.使用pthread以后, 在用户看来, 每一个task_struct就对应一个线程, 而一组线程以及它们所共同引用的一组资源就是一个进程.但是, 一组线程并不仅仅是引用同一组资源就够了, 它们还必须被视为一个整体.

对此, POSIX(portable operation system interface)标准提出了如下要求:

1, 查看进程列表的时候, 相关的一组task_struct应当被展现为列表中的一个节点;
2, 发送给这个"进程"的信号(对应kill系统调用), 将被对应的这一组task_struct所共享, 并且被其中的任意一个"线程"处理;
3, 发送给某个"线程"的信号(对应pthread_kill), 将只被对应的一个task_struct接收, 并且由它自己来处理;
4, 当"进程"被停止或继续时(对应SIGSTOP/SIGCONT信号), 对应的这一组task_struct状态将改变;
5, 当"进程"收到一个致命信号(比如由于段错误收到SIGSEGV信号), 对应的这一组task_struct将全部退出;
6, 等等(以上可能不够全);

在LinuxThreads中,专门为每一个进程构造了一个管理线程,负责处理线程相关的管理工作。当进程第一次调用pthread_create()创建一个线程的时候就会创建并启动管理线程。然后管理线程再来创建用户请求的线程。也就是说,用户在调用pthread_create后,先是创建了管理线程,再由管理线程创建了用户的线程。

linuxthreads利用前面提到的轻量级进程来实现线程, 但是对于POSIX提出的那些要求,linuxthreads除了第5点以外, 都没有实现(实际上是无能为力):

1, 如果运行了A程序, A程序创建了10个线程, 那么在shell下执行ps命令时将看到11个A进程, 而不是1个(注意, 也不是10个, 下面会解释);

2, 不管是kill还是pthread_kill, 信号只能被一个对应的线程所接收;

3, SIGSTOP/SIGCONT信号只对一个线程起作用;

还好linuxthreads实现了第5点, 我认为这一点是最重要的. 如果某个线程"挂"了, 整个进程还在若无其事地运行着, 可能会出现很多的不一致状态. 进程将不是一个整体, 而线程也不能称为线程. 或许这也是为什么linuxthreads虽然与POSIX的要求差距甚远, 却能够存在, 并且还被使用了好几年的原因吧~

但是, linuxthreads为了实现这个"第5点", 还是付出了很多代价, 并且创造了linuxthreads本身的一大性能瓶颈.

为什么A程序创建了10个线程, 但是ps时却会出现11个A进程了. 因为linuxthreads自动创建了一个管理线程. 上面提到的"第5点"就是靠管理线程来实现的.

当程序开始运行时, 并没有管理线程存在(因为尽管程序已经链接了pthread库, 但是未必会使用多线程).

程序第一次调用pthread_create时, linuxthreads发现管理线程不存在, 于是创建这个管理线程. 这个管理线程是进程中的第一个线程(主线程)的儿子.
然后在pthread_create中, 会通过pipe向管理线程发送一个命令, 告诉它创建线程. 即是说, 除主线程外, 所有的线程都是由管理线程来创建的, 管理线程是它们的父亲.
于是, 当任何一个子线程退出时, 管理线程将收到SIGUSER1信号(这是在通过clone创建子线程时指定的). 管理线程在对应的sig_handler中会判断子线程是否正常退出, 如果不是, 则杀死所有线程, 然后自杀.

那么, 主线程怎么办呢? 主线程是管理线程的父亲, 其退出时并不会给管理线程发信号. 于是, 在管理线程的主循环中通过getppid检查父进程的ID号, 如果ID号是1, 说明父亲已经退出, 并把自己托管给了init进程(1号进程). 这时候, 管理线程也会杀掉所有子线程, 然后自杀.

可见, 线程的创建与销毁都是通过管理线程来完成的, 于是管理线程就成了linuxthreads的一个性能瓶颈.
创建与销毁需要一次进程间通信, 一次上下文切换之后才能被管理线程执行, 并且多个请求会被管理线程串行地执行.

这种通过LWP的方式来模拟线程的实现看起来还是比较巧妙的,但也存在一些比较严重的问题:

1)线程ID和进程ID的问题
按照POSIX的定义,同一进程的所有的线程应该共享同一个进程和父进程ID,而Linux的这种LWP方式显然不能满足这一点。

2)信号处理问题

异步信号是以进程为单位分发的,而Linux的线程本质上每个都是一个进程,且没有进程组的概念,所以某些缺省信号难以做到对所有线程有效,例如SIGSTOP和SIGCONT,就无法将整个进程挂起,而只能将某个线程挂起。

3)线程总数问题
LinuxThreads将每个进程的线程最大数目定义为1024,但实际上这个数值还受到整个系统的总进程数限制,这又是由于线程其实是核心进程。

4)管理线程问题

管理线程容易成为瓶颈,这是这种结构的通病;同时,管理线程又负责用户线程的清理工作,因此,尽管管理线程已经屏蔽了大部分的信号,但一旦管理线程死亡,用户线程就不得不手工清理了,而且用户线程并不知道管理线程的状态,之后的线程创建等请求将无人处理。

5)同步问题
LinuxThreads中的线程同步很大程度上是建立在信号基础上的,这种通过内核复杂的信号处理机制的同步方式,效率一直是个问题。

模型二:NPTL

到了linux 2.6, glibc中有了一种新的pthread线程库--NPTL(Native POSIX Threading Library).

本质上来说,NPTL还是一个LWP的实现机制,但相对原有LinuxThreads来说,做了很多的改进。下面我们看一下NPTL如何解决原有LinuxThreads实现机制的缺陷

NPTL实现了前面提到的POSIX的全部5点要求. 但是, 实际上, 与其说是NPTL实现了, 不如说是linux内核实现了.

在linux 2.6中, 内核有了线程组的概念, task_struct结构中增加了一个tgid(thread group id)字段.

如果这个task是一个"主线程", 则它的tgid等于pid, 否则tgid等于进程的pid(即主线程的pid).

在clone系统调用中, 传递CLONE_THREAD参数就可以把新进程的tgid设置为父进程的tgid(否则新进程的tgid会设为其自身的pid).

类似的XXid在task_struct中还有两 个:task->signal->pgid保存进程组的打头进程的pid、task->signal->session保存会话 打头进程的pid。通过这两个id来关联进程组和会话。

有了tgid, 内核或相关的shell程序就知道某个tast_struct是代表一个进程还是代表一个线程, 也就知道在什么时候该展现它们, 什么时候不该展现(比如在ps的时候, 线程就不要展现了).
而getpid(获取进程ID)系统调用返回的也是tast_struct中的tgid, 而tast_struct中的pid则由gettid系统调用来返回.

在执行ps命令的时候不展现子线程,也是有一些问题的。比如程序a.out运行时,创建 了一个线程。假设主线程的pid是10001、子线程是10002(它们的tgid都是10001)。这时如果你kill 10002,是可以把10001和10002这两个线程一起杀死的,尽管执行ps命令的时候根本看不到10002这个进程。如果你不知道linux线程背 后的故事,肯定会觉得遇到灵异事件了

为了应付"发送给进程的信号"和"发送给线程的信号", task_struct里面维护了两套signal_pending, 一套是线程组共享的, 一套是线程独有的.

通过kill发送的信号被放在线程组共享的signal_pending中, 可以由任意一个线程来处理; 通过pthread_kill发送的信号(pthread_kill是pthread库的接口, 对应的系统调用中tkill)被放在线程独有的signal_pending中, 只能由本线程来处理.

当线程停止/继续, 或者是收到一个致命信号时, 内核会将处理动作施加到整个线程组中

Java线程

Java线程在操作系统上本质

Java线程在JDK1.2之前,是基于称为“绿色线程”(Green Threads)的用户线程实现的,而在JDK1.2中,线程模型替换为基于操作系统原生线程模型来实现。因此,在目前的JDK版本中,操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的,这点在不同的平台上没有办法达成一致,虚拟机规范中也并未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对Java程序的编码和运行过程来说,这些差异都是透明的。
也就说JDK1.2之前,程序员们为JVM开发了自己的一个线程调度内核,而到操作系统层面就是用户空间内的线程实现。而到了JDK1.2及以后,JVM选择了更加稳健且方便使用的操作系统原生的线程模型,通过系统调用,将程序的线程交给了操作系统内核进行调度。现在的Java中线程的本质,其实就是操作系统中的线程**

image.png

 

特别注意:这些线程的状态时JVM中的线程状态!不是操作系统中的线程状态。

操作系统中的进程(线程)状态**(区分和JVM中的线程状态)

image.png

这里需要着重解释一点,在现在的操作系统中,因为线程依旧被视为轻量级进程,所以操作系统中线程的状态实际上和进程状态是一致的模型。

操作系统中线程和Java线程状态的关系:

从实际意义上来讲,操作系统中的线程除去new和terminated状态,一个线程真实存在的状态,只有:

  • ready:表示线程已经被创建,正在等待系统调度分配CPU使用权。
  • running:表示线程获得了CPU使用权,正在进行运算
  • waiting:表示线程等待(或者说挂起),让出CPU资源给其他线程使用
    为什么除去new和terminated状态?是因为这两种状态实际上并不存在于线程运行中,所以也没什么实际讨论的意义

对于Java中的线程状态
无论是Timed Waiting ,Waiting还是Blocked,对应的都是操作系统线程的waiting(等待)状态。而Runnable状态,则对应了操作系统中的ready和running状态。
状态转换

image.png

 

Java语言定义了5种线程状态,在任意一个时间点,一个线程只能有且只有其中的一种状态,这5种状态分别如下:

1)新建(New):创建后尚未启动的线程处于这种状态。

2)运行(Runable):Runable包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。

3)无限期等待(Waiting):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其它线程显示地唤醒。以下方法会让线程陷入无限期的等待状态:

  • 没有设置Timeout参数的Object.wait()方法
  • 没有设置Timeout参数的Thread.join()方法
  • LockSupport.park()方法
  1. 限期等待(Timed Waiting):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其它线程显示地唤醒,在一定时间之后它们会由系统自动唤醒。以下方法会让线程进入限期等待状态:
  Thread.sleep()方法
  设置了Timeout参数的Object.wait()方法
  设置了Timeout参数的Thread.join()方法
  LockSupport.parkNanos()方法
  LockSupport.parkUntil()方法

而对不同的操作系统,由于本身设计思路不一样,对于线程的设计也存在种种差异,所以JVM在设计上,就已经声明:

虚拟机中的线程状态,不反应任何操作系统线程状态

java主线程结束和子线程结束之间的关系

Main线程是个非守护线程,不能设置成守护线程。

这是因为,main线程是由java虚拟机在启动的时候创建的。main方法开始执行的时候,主线程已经创建好并在运行了。对于运行中的线程,调用Thread.setDaemon()会抛出异常Exception in thread "main" java.lang.IllegalThreadStateException。测试代码如下:

public class MainTest
{
    public static void main(String[] args)
    {
        System.out.println(" parent thread begin ");
        Thread.currentThread().setDaemon(true);
    }
}

Main线程结束,其他线程一样可以正常运行。

主线程,只是个普通的非守护线程,用来启动应用程序,不能设置成守护线程;除此之外,它跟其他非守护线程没有什么不同。主线程执行结束,其他线程一样可以正常执行。代码如下:

public class ParentTest
{
 
    public static void main(String[] args)
    {
        System.out.println("parent thread begin ");
        
        ChildThread t1 = new ChildThread("thread1");
        ChildThread t2 = new ChildThread("thread2");
        t1.start();
        t2.start();
 
        System.out.println("parent thread over ");
    }
}
 
class ChildThread extends Thread
{
    private String name = null;
 
    public ChildThread(String name)
    {
        this.name = name;
    }
 
    @Override
    public void run()
    {
        System.out.println(this.name + "--child thead begin");
 
        try
        {
            Thread.sleep(500);
        }
        catch (InterruptedException e)
        {
            System.out.println(e);
        }
 
        System.out.println(this.name + "--child thead over");
    }
}

这样其实是很合理的,按照操作系统的理论,进程是资源分配的基本单位,线程是CPU调度的基本单位。对于CPU来说,其实并不存在java的主线程和子线程之分,都只是个普通的线程。进程的资源是线程共享的,只要进程还在,线程就可以正常执行,换句话说线程是强依赖于进程的。也就是说,线程其实并不存在互相依赖的关系,一个线程的死亡从理论上来说,不会对其他线程有什么影响。

Main线程结束,其他线程也可以立刻结束,当且仅当这些子线程都是守护线程。

java虚拟机(相当于进程)退出的时机是:虚拟机中所有存活的线程都是守护线程。只要还有存活的非守护线程虚拟机就不会退出,而是等待非守护线程执行完毕;反之,如果虚拟机中的线程都是守护线程,那么不管这些线程的死活java虚拟机都会退出。测试代码如下:

public class ParentTest
{
 
    public static void main(String[] args)
    {
        System.out.println("parent thread begin ");
        
        ChildThread t1 = new ChildThread("thread1");
        ChildThread t2 = new ChildThread("thread2");
        t1.setDaemon(true);
        t2.setDaemon(true);
        
        t1.start();
        t2.start();
 
        System.out.println("parent thread over ");
    }
}
class ChildThread extends Thread
{
    private String name = null;
    public ChildThread(String name)
    {
        this.name = name;
    }
    @Override
    public void run()
    {
        System.out.println(this.name + "--child thead begin");
        try
        {
            Thread.sleep(500);
        }
        catch (InterruptedException e)
        {
            System.out.println(e);
        }
        System.out.println(this.name + "--child thead over");
    }
}
 
 执行结果如下:
parent thread begin
parent thread over
thread1--child thead begin
thread2--child thead begin

在这种情况下,的确主线程退出,子线程就立刻结束了,但是这是属于JVM的底层实现机制,并不是说主线程和子线程之间存在依赖关系。

linux中用top、ps命令查看进程中的线程

在ps命令中,“-T”选项可以开启线程查看。下面的命令列出了由进程号为<pid>的进程创建的所有线程。

1.$ ps -T -p <pid>

1.$ ps -T -p <pid>

top命令可以实时显示各个线程情况。要在top输出中开启线程查看,请调用top命令的“-H”选项,该选项会列出所有Linux线程。在top运行时,你也可以通过按“H”键将线程查看模式切换为开或关。
1.$ top -H

要让top输出某个特定进程<pid>并检查该进程内运行的线程状况:

2.$ top -H -p <pid>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java线程和操作系统线程的关系 的相关文章

  • 按键时关闭 ModalWindow

    我希望能够在用户按下某个键 在我的例子中是 ESC 时关闭 ModalWindow 我有一个用于按键的 Javascript 侦听器 它调用取消按钮 ID 的单击事件 jQuery modalWindowInfo closeButtonId
  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • Junit:如何测试从属性文件读取属性的方法

    嗨 我有课ReadProperty其中有一个方法ReadPropertyFile返回类型的Myclass从属性文件读取参数值并返回Myclass目的 我需要帮助来测试ReadPropertyFile方法与JUnit 如果可能的话使用模拟文件
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • Spring AspectJ 在双代理接口时失败:无法生成类的 CGLIB 子类

    我正在使用Spring的
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • 从最终实体获取根证书和中间证书

    作为密码学的菜鸟 我每天都会偶然发现一些简单的事情 今天只是那些日子之一 我想用 bouncy castle 库验证 java 中的 smime 消息 我想我几乎已经弄清楚了 但此时的问题是 PKIXparameters 对象的构建 假设我
  • 将流转换为 IntStream

    我有一种感觉 我在这里错过了一些东西 我发现自己做了以下事情 private static int getHighestValue Map
  • Eclipse Maven Spring 项目 - 错误

    I need help with an error which make me crazy I started to study Java EE and I am going through tutorial on youtube Ever
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • 当 OnFocusChangeListener 应用于包装的 EditText 时,TextInputLayout 没有动画

    不能比标题说得更清楚了 我有一个由文本输入布局包裹的 EditText 我试图在 EditText 失去焦点时触发一个事件 但是 一旦应用了事件侦听器 TextInputLayout 就不再对文本进行动画处理 它只是位于 editText
  • tomcat 中受密码保护的应用程序

    我正在使用 JSP Servlet 开发一个Web应用程序 并且我使用了Tomcat 7 0 33 as a web container 所以我的要求是tomcat中的每个应用程序都会password像受保护的manager applica
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • Eclipse 启动时崩溃;退出代码=13

    I am trying to work with Eclipse Helios on my x64 machine Im pretty sure now that this problem could occur with any ecli
  • Opencv Java 灰度

    我编写了以下程序 尝试从彩色转换为灰度 Mat newImage Imgcodecs imread q1 jpg Mat image new Mat new Size newImage cols newImage rows CvType C
  • Spring Rest 和 Jsonp

    我正在尝试让我的 Spring Rest 控制器返回jsonp但我没有快乐 如果我想返回 json 但我有返回的要求 完全相同的代码可以正常工作jsonp我添加了一个转换器 我在网上找到了用于执行 jsonp 转换的源代码 我正在使用 Sp

随机推荐

  • ThinkPHP实现凯撒密码加密和解密

    ThinkPHP实现凯撒密码加密和解密 在密码学中 恺撒密码 英语 Caesar cipher 或称恺撒加密 恺撒变换 变换加密 是一种最简单且最广为人知的加密技术 它是一种替换加密的技术 明文中的所有字母都在字母表上向后 或向前 按照一个
  • CSS 类选择器的特殊使用-多类名

    title CSS 类选择器的特殊使用 多类名 CSS 类选择器的特殊使用 多类名概念 一个标签有多个名字 多类名的使用方式 示例代码
  • 在NW.js里面使用node-printer

    0 写在前面的一些提示 不要使用太高版本的 NW js 以及 Node js 因为 node printer 会有兼容性问题 目前笔者测试到 node printer 最高只能在 0 43 x 版本以及低于这个版本的 NW js 里面正常运
  • 代码走查与代码审查区别

    代码走查 程序员和测试员组成审查小组 通过逻辑运行程序 第一步 小组成员提前阅读设计规格书 程序文本等相关文档 第二步 利用测试用例 使程序逻辑运行 记录程序的踪迹 发现 讨论 解决问题 代码审查 程序员和测试员组成审查小组 第一步 小组成
  • robot通过一个元素定位另一个元素

    xpath定位 xpath span text 文章评为精华 div div span text 去完成
  • 一些keil编程错误总结

    一 error c267 delay 10us requires ANSI style prototype问题 解决方法 1 未进行 模块化程序设计时 delay 10us该子函数要在主函数前 2 在进行 模块化程序设计时 这是由于 c文件
  • 区块链在非金融领域有哪些值得期待的应用

    引言 自己关注区块链好多年了 感觉大部分人都在炒币 或者所谓的搞金融 但是现在各国的审查都越来越严格 针对金融领域的审查尤其严格 在国内 基本上是禁止区块链应用有任何的发币行为的 我个人也是觉得金融领域的监管太多 反而不容易落地 这篇文章来
  • Redis中的String类型与List类型个人理解

    String类型 String类型是Redis的最基础类型 最大能存储512MB 其中存储值可以是最简单的字符串 复杂的 xml json的字符串 二进制图像或者音频的字符串 以及是数字的字符串 String数据类型与结构 基本操作 set
  • R语言的plyr包简介

    转载地址 http www jianshu com p bfddfe29aa39 R语言的plyr包简介 字数3040 阅读2875 评论3 喜欢7 R语言中的类SQL操作 plyr包可以进行类似于数据透视表的操作 将数据分割成更小的数据
  • Eclipse启动tomcat出现invalid ports的解决办法

    在Eclipse出现以上错误的 可以继续往下看 在server中 点击Tomcat 进入设置 把三个端口都填写了不留空 根据情况来填写相应的端口号 防止占用 保存即可
  • 交易流水指标统计——pandas

    1 根据交易流水 计算每个交易交易对象的如下指标 总收入笔数 总收入月数 最大月收入笔数 最大月收入笔数所在日期 年月日格式 import pandas as pd path r C Users xxx Desktop 科技数据 xlsx
  • Angular入门学习笔记

    Angualr入门扫盲必备 声明 这篇是我学习angualr的笔记 可以转载 但必须注明来源作者 kone 并附上本文链接 A 环境 工具 1 先确保安装了nodejs和npm Nodejs npm 包管理器 cnpm 淘宝的镜像 2 安裝
  • 低代码让开发变得不再复杂

    传统IT自研软件开发 通过编程去写代码 还有数据库 API 第三方基础架构等 从而造成开发周期长 难度大 技术人员不易于开发维护 因此价格及时间成本也是相对较高 后期出现了可以直接应用的成品软件 以及现在火热的低代码开发方式 针对这三种方式
  • 给Sqlite数据库设置密码

    免费版Sqlie是不提供设置密码功能的 经过查阅资料最终找到了解决方案 方案一 从sqlite源码入手 据说sqlite源码已经提供了加密的接口 只是免费版没有实现 可以参考这位仁兄的博客以了解详情 http www cnblogs com
  • 架构师是做什么的?

    哈佛大学有一个非常著名的关于目标对人生影响的跟踪调查 调查的对象是一群智力 学历 环境等条件都差不多的大学毕业生 结果是这样的 第一类人 27 的人 没有目标 第二类人 60 的人 目标模糊 第三类人 10 的人 有清晰但比较短期的目标 第
  • Mysql错误1452 - Cannot add or update a child row: a foreign key constraint fails 原因及解决方法

    报错的原因大概分为三种 原因一 添加的外键列与另一个表的唯一索引列 一般是主键 的数据类型不同 原因二 要添加外键的表类型与另一个表的存储引擎是不是都为innodb引擎 查看表引擎 法一 show create table 表名 法二 sh
  • ag-grid在Vue项目中的基本使用

    ag grid官网 1 安装 npm install ag grid community ag grid vue save dev 2 在main js中引入ag grid的样式文件 引入ag grid的样式文件 import node m
  • bing搜索引擎入口_资源搜索,除了百度,还有哪些好用的搜索引擎?

    作者 杨银洁 来源 ETPPT 遇到问题怎么办 百度一下 你out了 除了百度还有很多搜索引擎 而且还有很多国外网站可以检索哦 是否还在苦恼高清图找不到 PPT素材找不到 关注ETPPT这些就不是问题 今天我们就介绍一下有哪些可以轻松帮助你
  • 云原生之使用Docker部署BookBrowser电子书浏览器

    云原生之使用Docker部署BookBrowser电子书浏览器 一 BookBrowser简介 1 BookBrowser简介 2 BookBrowser特点 二 检查docker环境 1 检查docker状态 2 检查docker版本 3
  • Java线程和操作系统线程的关系

    传统进程的缺点 fork一个子进程的消耗是很大的 fork是一个昂贵的系统调用 即使使用现代的写时复制 copy on write 技术 各个进程拥有自己独立的地址空间 进程间的协作需要复杂的IPC技术 如消息传递和共享内存等 多线程的优缺