精华内容
下载资源
问答
  • 进程线程

    万次阅读 多人点赞 2021-03-17 22:50:20
    进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间 动态性 进程与程序的区别在于,程序只是...

    进程与线程

    1 进程

    1.1 进程的概念

    进程就是正在运行的程序,它代表了程序所占用的内存区域

    1.2 进程的特点

    • 独立性
      进程是系统中独立存在的实体,它可以拥有自己独立的资源,每个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间
    • 动态性
      进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,程序加入了时间的概念以后,称为进程,具有自己的生命周期和各种不同的状态,这些概念都是程序所不具备的.
    • 并发性
      多个进程可以在单个处理器CPU上并发执行,多个进程之间不会互相影响.

    1.3 并行和并发

    并行与并发

    HA(High Availability)高可用:指在高并发的情景中,尽可能的保证程序的可用性,减少系统不能提供服务的时间

    2 线程

    2.1 线程的概念

    线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.
    一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程
    我们看到的进程的切换,切换的也是不同进程的主线程
    多线程扩展了多进程的概念,使的同一个进程可以同时并发处理多个任务

    2.2 进程与线程的关系

    一个操作系统中可以有多个进程,一个进程中可以包含一个线程(单线程程序),也可以包含多个线程(多线程程序)
    进程与线程的关系
    每个线程在共享同一个进程中的内存的同时,又有自己独立的内存空间.
    所以想使用线程技术,得先有进程,进程的创建是OS操作系统来创建的,一般都是C或者C++完成
    进程与线程的关系

    3 多线程的特性

    3.1 随机性

    线程的随机性指的是同一时刻,只有一个程序在执行
    我们宏观上觉得这些程序像是同时运行,但是实际上微观时间是因为CPU在高效的切换着,这使得各个程序从表面上看是同时进行的,也就是说,宏观层面上,所有的进程/线程看似同时运行,但是微观层面上,同一时刻,一个CPU只能处理一件事.切换的速度甚至是纳秒级别的,非常快
    线程切换

    3.2 CPU分时调度

    时间片,即CPU分配给各个线程的一个时间段,称作它的时间片,即该线程被允许运行的时间,如果在时间片用完时线程还在执行,那CPU将被剥夺并分配给另一个线程,将当前线程挂起,如果线程在时间片用完之前阻塞或结束,则CPU当即进行切换,从而避免CPU资源浪费,当再次切换到之前挂起的线程,恢复现场,继续执行。
    注意:我们无法控制OS选择执行哪些线程,OS底层有自己规则,如:

    1. FCFS(First Come First Service 先来先服务算法)
    2. SJS(Short Job Service短服务算法)

    CPU分片

    3.3 线程的状态

    由于线程状态比较复杂,我们由易到难,先学习线程的三种基础状态及其转换,简称”三态模型” :

    • 就绪(可运行)状态:线程已经准备好运行,只要获得CPU,就可立即执行
    • 执行(运行)状态:线程已经获得CPU,其程序正在运行的状态
    • 阻塞状态:正在运行的线程由于某些事件(I/O请求等)暂时无法执行的状态,即线程执行阻塞
      线程的3种状态

    就绪 → 执行:为就绪线程分配CPU即可变为执行状态"
    执行 → 就绪:正在执行的线程由于时间片用完被剥夺CPU暂停执行,就变为就绪状态
    执行 → 阻塞:由于发生某事件,使正在执行的线程受阻,无法执行,则由执行变为阻塞
    (例如线程正在访问临界资源,而资源正在被其他线程访问)
    反之,如果获得了之前需要的资源,则由阻塞变为就绪状态,等待分配CPU再次执行

    我们可以再添加两种状态:

    • 创建状态:线程的创建比较复杂,需要先申请PCB,然后为该线程运行分配必须的资源,并将该线程转为就绪状态插入到就绪队列中
    • 终止状态:等待OS进行善后处理,最后将PCB清零,并将PCB返回给系统
      线程的5种状态

    PCB(Process Control Block):为了保证参与并发执行的每个线程都能独立运行,OS配置了特有的数据结构PCB来描述线程的基本情况和活动过程,进而控制和管理线程

    3.4 线程状态与代码对照

    线程状态与代码对照
    线程生命周期,主要有五种状态:

    1. 新建状态(New) : 当线程对象创建后就进入了新建状态.如:Thread t = new MyThread();
    2. 就绪状态(Runnable):当调用线程对象的start()方法,线程即为进入就绪状态.
      处于就绪(可运行)状态的线程,只是说明线程已经做好准备,随时等待CPU调度执行,并不是执行了t.start()此线程立即就会执行
    3. 运行状态(Running):当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态
      就绪状态是进入运行状态的唯一入口,也就是线程想要进入运行状态状态执行,先得处于就绪状态
    4. 阻塞状态(Blocked):处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.
      根据阻塞状态产生的原因不同,阻塞状态又可以细分成三种:
      等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态
      同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态
      其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态
    5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期

    4 多线程代码创建方式1:继承Thread

    4.1 概述

    Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例
    启动线程的唯一方法就是通过Thread类的start()实例方法
    start()方法是一native方法,它将通知底层操作系统,.最终由操作系统启动一个新线程,操作系统将执行run()
    这种方式实现的多线程很简单,通过自己的类直接extends Thread,并重写run()方法,就可以自动启动新线程并执行自己定义的run()方法
    模拟开启多个线程,每个线程调用run()方法.

    4.2 常用方法

    构造方法

    Thread() 分配新的Thread对象
    Thread(String name) 分配新的Thread对象
    Thread(Runnable target) 分配新的Thread对象
    Thread(Runnable target,String name) 分配新的Thread对象

    普通方法

    static Thread currentThread( )
    返回对当前正在执行的线程对象的引用
    long getId()
    返回该线程的标识
    String getName()
    返回该线程的名称
    void run()
    如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法
    static void sleep(long millions)
    在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
    void start()
    使该线程开始执行:Java虚拟机调用该线程的run()

    4.3 测试多线程的创建方式1

    创建包: cn.tedu.thread
    创建类: Thread1.java

    package cn.tedu.thread;
    /**本类用来测试多线程的编程方式1 
     * extends Thread
     * */
    public class Thread1 {
    	public static void main(String[] args) {
    		//4.创建线程对象
    		MyThread t = new MyThread();/**对应的状态就是新建状态*/
    		/**如果只是调用两个线程的run(),那么会先执行完一个线程,再执行另一个线程,不会有多线程的效果*/
    		//t.run();//怎么去执行run()的业务?--真的可以用run()来执行我们的多线程任务吗?
    		/**run()与start()本质上的区别,run()在执行时只能是当做一个顺序执行的单线程普通方法执行,并没有多线程编程的效果*/
    		t.start();/**对应的状态就是就绪状态,想要使用多线程启动干活,必须调用start()才是真正的启动线程*/
    
    		/**6.只有调用satrt()才会使线程从新建状态变成可运行状态
    		 * 当我们调用start()启动线程时,底层虚拟机会自动调用run()的业务
    		 * */
    		//5.模拟多线程,需要至少启动2个线程,如果只是启动一个线程,是单线程程序
    		MyThread t2 = new MyThread();
    		//t2.run();
    		t2.start();
    
    		/**线程的随机性,t0 t1 t2的执行结果不可控,因为会由CPU在调度,结果有随机性*/
    		/**线程的随机性:CPU会自动调度可运行状态的线程们,但是哪个时间片执行哪个线程我们无法控制*/
    //		2=Thread-0
    //		3=Thread-0
    //		2=Thread-1
    //		1=Thread-2
    //		3=Thread-1
    		MyThread t3 = new MyThread("小灰灰");
    		t3.start();
    	}
    }
    
    //1.自定义多线程类
    /**1.方式1:extends Thread*/
    class MyThread extends Thread{
    	
    	/**最后:为了修改线程名称,不再使用系统分配的默认名称,需要提供含参构造*/
    	//右键-->Source-->倒数第二个-->DisSelect All-->选择无参构造或者传名字的构造
    	public MyThread() {
    		super();
    	}
    
    	public MyThread(String name) {
    		super(name);
    	}
    	
    	//2.1线程中的业务必须写在run()里
    	/**2.源码:745行*/
    	//    @Override
    	//    public void run() {
    	//        if (target != null) {
    	//            target.run();
    	//        }
    	//    }
    	/**3.如果不满意run()的内容,可以重写alt+/*/
    	//2.2 重写Thread父类中的run()
    	@Override
    	public void run() {
    		/**4.super表示父类对象的引用,也就是默认使用Thread类里的业务,不用*/
    		//super.run();
    		//3.写业务:输出10次当前正在执行的线程名称
    		for (int i = 0; i < 10; i++) {
    			/**5.getName()可以获取正在执行任务的线程名称,是从父类中继承的方法,可以直接使用*/
    			System.out.println(i+"="+getName());
    		}
    	}
    }
    

    5 多线程代码创建方式2:实现Runnable接口

    5.1 概述

    如果自己的类已经extends另一个类,就无法多继承,此时,可以实现一个Runnable接口

    5.2 常用方法

    void run()使用实现接口Runnable的对象创建线程时,启动该线程将导致在独立执行的线程中调用对象的run()方法

    5.3 练习2:测试多线程的创建方式2

    创建包: cn.tedu.thread
    创建类: Thread2.java

    package cn.tedu.thread;
    
    /**本类用于测试多线程编程方式2 implements Runnable*/
    public class Thread2 {
    	public static void main(String[] args) {
    		//4.创建线程对象
    		MyRunnable target = new MyRunnable();
    		//5.2 问题:怎么把接口的实现类和Thread类绑定
    		Thread thread1 = new Thread(target);
    		
    		//5.1如何启动线程?
    		thread1.start();
    		
    		//6.--以多线程编程的方式启动,需要创建多个线程对象并启动
    		//8.修改线程的名称--使用Thread类的含参构造
    		Thread thread2 = new Thread(target,"杰克");
    		Thread thread3 = new Thread(target,"露丝");
    		thread2.start();
    		thread3.start();
    		
    		//7.自己测试start()和run()的区别
    		//run()只是一个普通方法执行的效果,也就是单线程顺序执行的效果,没有多线程的线现象
    	}
    }
    
    //1.自定义多线程类,方式2 implements Runnable
    class MyRunnable implements Runnable{
    
    	//2.把业务放入run(),重写了Runnable接口里的
    	@Override
    	public void run() {
    		//3.写业务,打印10次线程名称
    		for(int i = 0; i< 10; i++){
    			//问题:Runnable接口中,没有提供多余的方法维度只有一个run()
    			//Thread.currentThread()获取当前正在执行业务的线程对象 getName()获取此线程对象的名称
    			System.out.println(i+"="+Thread.currentThread().getName());
    		}
    	}
    }
    

    5.4 两种实现方式的比较

    • 继承Thread类
      优点: 编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获得当前线程
      缺点: 自定义的线程类已继承了Thread类,所以后续无法再继承其他的类
    • 实现Runnable接口
      优点: 自定义的线程类只是实现了Runnable接口或Callable接口,后续还可以继承其他类,在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、还有数据分开(解耦),形成清晰的模型,较好地体现了面向对象的思想
      缺点: 编程稍微复杂,如想访问当前线程,则需使用Thread.currentThread()方法

    6 售票案例

    需求:设计4个售票窗口,总计售票100张。用多线程的程序设计并写出代码

    6.1 方案1:继承Thread

    创建包: cn.tedu.tickets
    创建类: TestThread.java

    package cn.tedu.tickets;
    /*需求:设计多线程编程模型,4个窗口共计售票100张*/
    /*本类通过继承Thread类的方式实现多线程售票案例*/
    public class TestThread {
        public static void main(String[] args) {
            //5.创建多个线程对象 Ctrl+D 复制当前行
            TicketThread t1 = new TicketThread();
            TicketThread t2 = new TicketThread();
            TicketThread t3 = new TicketThread();
            TicketThread t4 = new TicketThread();
            //6.以多线程的方式启动
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    //1.自定义线程售票业务类
    class TicketThread extends Thread{
        //3.定义变量,用来保存票数
        //int tickets = 100;//不可以,会卖400张票
        //7.解决4个线程卖了400张票的BUG
        static int tickets = 100;//静态资源属于类资源,被全局所有对象共享,只有一份
        //2.把业务写在重写run()里
        @Override
        public void run() {
            //4.通过循环结构来一直卖票
            while (true){
                try {
                    //8.如果数据能够经受住sleep的考验,才能说明数据没有了安全隐患--人为制造问题
                    //问题1:产生了重卖:同一张票卖给了多个人
                    //问题2:产生了超卖:超出了规定票数,甚至卖出了0和-1这样的票数
                    Thread.sleep(10);//让程序休眠10ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+"="+tickets--);
                //做判断,如果没有票了,就退出死循环
                if(tickets <= 0) break;//注意,死循环一定要设置出口
            }
        }
    }
    

    6.2 方案2:实现Runnable

    创建包: cn.tedu.tickets
    创建类: TestRunnable.java

    package cn.tedu.tickets;
    /*需求:设计多线程编程模型,4个窗口共计售票100张*/
    /*本类通过实现Runnable接口的方式实现多线程售票案例*/
    public class TestRunnable {
        public static void main(String[] args) {
            //5.创建目标业务对象
            TicketRunnable target = new TicketRunnable();
            //6.使用Thread类中的含参构造,将目标对象与线程对象做绑定
            Thread t1 = new Thread(target);
            Thread t2 = new Thread(target);
            Thread t3 = new Thread(target);
            Thread t4 = new Thread(target);
            //7.以多线程的方式启动线程
            t1.start();
            t2.start();
            t3.start();
            t4.start();
    
        }
    }
    //1.创建自定义多线程类
    class TicketRunnable implements Runnable{
        //3.创建成员变量,用来保存票数,注意必须是静态的
        static int tickets = 100;
        //2.添加接口中未实现的方法,把业务放在run()里
        @Override
        public void run() {
            while (true){
                try {
                    //让程序休眠后出现的两个问题:
                    //问题1.重卖:一张票卖给了多个人
                    //问题2.超卖:出现了票数为0甚至是负数的情况
                    Thread.sleep(10);//让程序休眠10ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //3.获取当前正在卖票的线程名称,以及卖票
                System.out.println(Thread.currentThread().getName()+"="+tickets--);
                //4.设置死循环的出口
                if (tickets <= 0) break;
            }
        }
    }
    

    6.3 问题

    1. 每次创建线程对象,都会生成一个tickets变量值是100,创建4次对象就生成了400张票了。不符合需求,怎么解决呢?能不能把tickets变量在每个对象间共享,就保证多少个对象都是卖这100张票。
      解决方案: 用静态修饰
    2. 产生超卖,0 张 、-1张、-2张。
    3. 产生重卖,同一张票卖给多人。
    4. 多线程安全问题是如何出现的?常见情况是由于线程的随机性+访问延迟。
    5. 以后如何判断程序有没有线程安全问题?
      在多线程程序中 + 有共享数据 + 多条语句操作共享数据
      解决方案:下一节 同步锁点这里
    展开全文
  • 一个进程能运行多少线程

    千次阅读 2019-04-11 15:22:41
    也知道线程的概念,它是程序执行的最小单元,是进程中的一个实体用来执行程序,一个进程中有多个线程。(一个cpu内核只能运行一个进程/线程,一台8核cpu的服务器只能同时运行8个进程/线程。只是每个进...

    转载:https://www.cnblogs.com/wozijisun/p/10370897.html

     

    对于Linux来说:

    我们都知道进程的概念,它是CPU分配资源的载体,是程序运行的实例;也知道线程的概念,它是程序执行的最小单元,是进程中的一个实体用来执行程序,一个进程中有多个线程。(一个cpu内核只能运行一个进程/线程,一台8核cpu的服务器只能同时运行8个进程/线程。只是每个进程/线程都会得到一个cpu运行时间,只要cpu速度快,给人的感觉就是服务器在运行成千上万个进程/线程

    既然如此,我们可以想象,一个系统中的进程数量肯定是有上限的,不然系统资源就会不够用了,同样的道理,一个进程中的线程资源也是有上限的。那进程和线程的上限到底是多大呢?

    Linux中最多可以有多少个进程?
    一.Linux中有一个命令可以帮助我们查看系统中的进程上限

    [pigff@izbp13yd4r85qvk53t04mbz ~]$ ulimit -u
    4096

    这属于软限制,是可以改变的。也就是说在我的机器上最多可以有4096个进程,但是我可以通过改变这个参数的值来修改对于进程数量的软限制,比如说用下面的命令将软限制改到5120。

     ulimit -u 5120

     二.我们用pid_t来表示一个进程的pid,因此能表示的进程的范围一定不会超过pid_t类型的大小

    [pigff@izbp13yd4r85qvk53t04mbz ~]$ cat /proc/sys/kernel/pid_max
    32768

    pid_t实际上就是一个short类型变量,当然这里能表示的范围只是进程id最多表示到这么多,这只是一个理论值,实际上,由于内存等系统资源的限制,根本不会同时有这么多的进程存在。

    一个进程中最多可以有多少个线程?
    在上一篇文章Linux中线程占用内存中,我们知道了创建一个线程会占用多少内存,这取决于分配给线程的调用栈大小,可以用ulimit -s命令来查看大小(一般常见的有10M或者是8M)。我们还知道,一个进程的虚拟内存是4G,在Linux32位平台下,内核分走了1G,留给用户用的只有3G,于是我们可以想到,创建一个线程占有了10M内存,总共有3G内存可以使用。于是可想而知,最多可以创建差不多300个左右的线程。

    因此,进程最多可以创建的线程数是根据分配给调用栈的大小,以及操作系统(32位和64位不同)共同决定的。
    --------------------- 
    原文:https://blog.csdn.net/lvyibin890/article/details/82255047 
     

    对于Windows来说:

    默认情况下,一个线程的栈要预留1M的内存空间 
    而一个进程中可用的内存空间只有2G,所以理论上一个进程中最多可以开2048个线程 
    但是内存当然不可能完全拿来作线程的栈,所以实际数目要比这个值要小。 
    你也可以通过连接时修改默认栈大小,将其改的比较小,这样就可以多开一些线程。 
    如将默认栈的大小改成512K,这样理论上最多就可以开4096个线程。 

    即使物理内存再大,一个进程中可以起的线程总要受到2GB这个内存空间的限制。 
    比方说你的机器装了64GB物理内存,但每个进程的内存空间还是4GB,其中用户态可用的还是2GB。


    如果是同一台机器内的话,能起多少线程也是受内存限制的。每个线程对象都要站用非页面内存,而非页面内存也是有限的,当非页面内存被耗尽时,也就无法创建线程了。 

    如果物理内存非常大,同一台机器内可以跑的线程数目的限制值会越来越大。  

    在Windows下写个程序,一个进程Fork出2000个左右线程就会异常退出了,为什么?

    这个问题的产生是因为windows32位系统,一个进程所能使用的最大虚拟内存为2G,而一个线程的默认线程栈StackSize为1024K(1M),这样当线程数量逼近2000时,2000*1024K=2G(大约),内存资源就相当于耗尽。

    如果你的服务器端程序设计成:来一个client连接请求则创建一个线程,那么就会存在2000个限制(在硬件内存和CPU个数一定的情况下)

    展开全文
  • 在平时工作中,经常会听到应用程序的进程线程的... 线程是指进程内的一个执行单元,也是进程内的调度实体. 线程是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位线程自己基本上不拥有系统资源...

    在平时工作中,经常会听到应用程序的进程和线程的概念,那么它们两个之间究竟有什么关系或不同呢?
    一、对比进程和线程

    1)两者概念
    . 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
    . 线程是指进程内的一个执行单元,也是进程内的可调度实体. 线程是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位线程自己基本上不拥有系统资源,只拥有一点
    在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
    2)两者关系
    . 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
    . 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
    3)两者区别
    进程和线程的主要差别在于它们是不同的操作系统资源管理方式:进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响;而线程只是一个进程中的不同执行路径。
    线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差
    一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
    进程和线程的区别:
    . 地址空间:线程是进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
    . 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
    . 线程是处理器调度的基本单位,但进程不是.
    . 进程和线程二者均可并发执行.
    . 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
    . 线程的划分尺度小于进程,使得多线程程序的并发性高。
    . 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    . 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个
    线程执行控制。
    . 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就
    是进程和线程的重要区别。
    4)优缺点
    线程和进程在使用上各有优缺点:
    . 线程执行开销小,但不利于资源的管理和保护;而进程正相反。
    . 线程适合于在SMP机器上(即对称多处理结构的简称,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构)运行,而进程则可以跨机器迁移。


    二、如何查看某个进程的线程数

    有些时候需要确定进程内部当前运行了多少线程,查询方法如下:
    1)通过pstree命令(根据pid)进行查询:
    [root@xqsj_web2 ~]# ps -ef|grep java //查找进程pid(比如这里查找java(tomcat)进程的pid)
    [root@xqsj_web2 ~]# pstree -p 19135
    java(19135)─┬─{java}(19136)
    ├─{java}(19137)
    .......
    └─{java}(13578)
    [root@xqsj_web2 ~]# pstree -p 19135|wc -l
    46 //由于第一行包括了2个线程,所以该进程下一共有47个线程!
    或者使用top命令查看(可以查看到线程情况)
    [root@xqsj_web2 ~]# top -Hp 19135 //下面结果中的Tasks 对应的47即是线程的个数
    top - 14:05:55 up 391 days, 20:59, 1 user, load average: 0.00, 0.00, 0.00
    Tasks: 47 total, 0 running, 47 sleeping, 0 stopped, 0 zombie
    Cpu(s): 0.2%us, 0.1%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
    Mem: 8058056k total, 7718656k used, 339400k free, 354216k buffers
    Swap: 0k total, 0k used, 0k free, 4678160k cached
    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    19135 root 20 0 5339m 632m 5476 S 0.0 8.0 0:00.00 java
    19136 root 20 0 5339m 632m 5476 S 0.0 8.0 0:00.84 java
    ......
    2)根据ps命令直接查询:
    [root@xqsj_web2 ~]# ps hH p 19135| wc -l
    47
    3)通过查看/proc/pid/status
    proc伪文件系统,它驻留在/proc目录,这是最简单的方法来查看任何活动进程的线程数。/proc目录以可读文本文件形式输出,提供现有进程和系统硬件
    相关的信息如CPU、中断、内存、磁盘等等。
    [root@xqsj_web2 ~]# cat /proc/19135/status
    Name: java
    State: S (sleeping)
    Tgid: 19135
    Pid: 19135
    PPid: 1
    TracerPid: 0
    ........
    Threads: 47 //这里显示的是进程创建的总线程数。输出表明该进程有47个线程。
    SigQ: 1/62793
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    .......
    voluntary_ctxt_switches: 1
    nonvoluntary_ctxt_switches: 1
    或者,也可以在/proc//task中简单的统计子目录的数量,如下所示:
    [root@xqsj_web2 ~]# ll /proc/19135/task
    总用量 0
    dr-xr-xr-x 6 root root 0 6月 14 17:57 11553
    ......
    [root@xqsj_web2 ~]# ll /proc/19135/task|wc -l
    48
    这是因为,对于一个进程中创建的每个线程,在/proc/<pid>/task中会创建一个相应的目录,命名为其线程ID。由此在/proc/<pid>/task中目录的总数表示在进程中线程的数目。


    脚本解释:比如某台服务器的CPU使用率飙升,通过top命令查看是gitlab程序占用的cpu比较大,"ps -ef|grep gitlab"发现有很多个gitlab程序,现在需要查询gitlab各个进程下的线程数情况。批量查询命令如下:
    # for pid in $(ps -ef|grep -v grep|grep gitlab|awk '{print $2}');do echo ${pid} > /root/a.txt ;cat /proc/${pid}/status|grep Threads > /root/b.txt;paste /root/a.txt /root/b.txt;done|sort -k3 -rn

    1)for pid in $(ps -ef|grep -v grep|grep gitlab|awk '{print $2}')
    定义${pid}变量为gitlab进程的pid号
    2)echo ${pid} > /root/a.txt
    将http进程的pid号都打印到/root/a.txt文件中
    3)cat /proc/${pid}/status|grep Threads > /root/b.txt
    将各个pid进程号下的线程信息打印到/root/b.txt文件中
    4)paste /root/a.txt /root/b.txt
    以列的形式展示a.txt和b/txt文件中的信息
    5)sort -k3 -rn
    -k3 表示以第三列进行排序
    -rn 表示降序

    =================来看个cup使用率告警问题处理案例==================

    收到告警,生产环境一台机器的cpu使用率超过了85%!立刻登录服务器,发现情况如下:
    [root@kevin ~]# uptime
    10:39:40 up 215 days, 13:02, 2 users, load average: 3.32, 3.40, 3.37
    [root@kevin ~]# top
    top - 10:52:51 up 215 days, 13:15, 3 users, load average: 3.32, 3.40, 3.37
    Tasks: 168 total, 1 running, 167 sleeping, 0 stopped, 0 zombie
    Cpu(s): 98.4%us, 0.2%sy, 0.0%ni, 1.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
    Mem: 8053692k total, 6542296k used, 1511396k free, 168560k buffers
    Swap: 16777212k total, 0k used, 16777212k free, 2565452k cached
    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    31969 app 20 0 4510m 1.9g 6220 S 393.5 25.1 2281:49 java
    .........
    [root@kevin ~]# ps -ef|grep 31969
    root 15826 15129 0 10:58 pts/0 00:00:00 grep 31969
    app 31969 31962 0 Jul02 ? 02:25:01 java -cp /data/lo-boxes/box_home/lo-starter.jar:/data/lo-boxes/box_home/lib/* -Dbox.name=B0002 -Dbox.home=/data/lo-boxes/B0002 -Dbox.jmx.port=57009 -XX:+CMSPermGenSweepingEnabled -XX:SoftRefLRUPolicyMSPerMB=1 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=1 -XX:+CMSClassUnloadingEnabled -XX:MaxTenuringThreshold=12 -XX:SurvivorRatio=8 -XX:ParallelGCThreads=3 -XX:+HeapDumpOnOutOfMemoryError -Dsun.reflect.inflationThreshold=2147483647 -XX:HeapDumpPath=/data/lo-boxes/B0002/boxlogs/logs/heapdump_31961.hprof -Xloggc:/data/lo-boxes/B0002/boxlogs/gclogs/gc.31961.log -XX:ErrorFile=/data/lo-boxes/B0002/boxlogs/hs_err_pid31961.log -Xms1024M -Xmx1024M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:NewSize=256M -XX:MaxNewSize=512M cn.openlo.starter.BoxStartupStandalone
    解决办法:
    1)最简单粗暴的方法:重启上面这个pid号为31969的进程所属的服务程序
    2)查出这个pid进程的cpu资源各自被哪个线程所占。通过"top -Hp pid"可以查看该进程下各个线程的cpu使用情况;如下:
    [root@kevin ~]# top -Hp 31969
    .......
    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    31969 app 20 0 3754m 1714m 16m S 390.5 29.1 0:00.00 java
    31970 app 20 0 3754m 2124m 16m S 382.2 20.5 0:02.74 java
    31971 app 20 0 3754m 1954m 16m S 360.0 19.5 0:00.49 java
    31972 app 20 0 3754m 1584m 16m S 300.9 23.1 0:00.48 java
    ......
    如上可知,31969的进程主要被上面四个线程耗了过多的CPU资源。
    通过top命令定位到cpu占用率较高的线程之后,继续使用jstack pid命令查看当前java进程的堆栈状态,这就用到jstack工具!
    jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。
    jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。
    jstack工具一般是在java/bin目录下的。如下设置java环境变量
    =============================================================================================
    [root@kevin ~]# ll /data/software/
    总用量 626244
    drwxr-xr-x 8 app app 4096 4月 11 2015 jdk1.7.0_80
    -rw-r--r-- 1 app app 153530841 6月 4 2016 jdk-7u80-linux-x64.tar.gz
    [root@kevin ~]# /data/software/jdk1.7.0_80/bin/java -version
    java version "1.7.0_80"
    Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
    Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)
    [root@kevin ~]# vim /etc/profile
    JAVA_HOME=/data/software/jdk1.7.0_80
    JAVA_BIN=/data/software/jdk1.7.0_80/bin
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/sbin/:/data/software/jdk1.7.0_80/bin/
    CLASSPATH=.:/lib/dt.jar:/lib/tools.jar
    export JAVA_HOME JAVA_BIN PATH CLASSPATH
    [root@kevin ~]# source /etc/profile
    [root@kevin ~]# mv /usr/bin/java /usr/bin/java.bak
    [root@kevin ~]# ln -s /data/software/jdk1.7.0_80/bin/java /usr/bin/java
    [root@kevin ~]# java -version
    java version "1.7.0_80"
    Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
    Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)
    [root@kevin ~]# jstack --help
    Usage:
    jstack [-l] <pid>
    (to connect to running process)
    jstack -F [-m] [-l] <pid>
    (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
    (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
    (to connect to a remote debug server)
    Options:
    -F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m to print both java and native frames (mixed mode)
    -l long listing. Prints additional information about locks
    -h or -help to print this help message
    =============================================================================================
    下面开始使用jstack对
    [root@kevin ~]# jstack 31969 或者"jstack 31969 > jstack-31969" 打印出堆栈信息到一个文件中,方便后续查看
    [root@kevin ~]# jstack 31970
    [root@kevin ~]# jstack 31971
    [root@kevin ~]# jstack 31972


    jstack命令生成的thread dump信息包含了JVM中所有存活的线程,为了分析指定线程,必须找出对应线程的调用栈,应该如何找?

    在top命令中,已经获取到了占用cpu资源较高的线程pid,将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可;隔段时间再执行一次stack命令获取thread dump,区分两份dump是否有差别,在nid=0x246c的线程调用栈中,发现该线程一直在执行JstackCase类第33行的calculate方法,得到这个信息,就可以检查对应的代码是否有问题。

    ***************当你发现自己的才华撑不起野心时,就请安静下来学习吧***************

    展开全文
  • 简单复习一下:一个”进程“代表中计算机中实际跑起来的一个程序,在现代操作系统的保护模式下,每个进程拥有自己独立的进程地址空间和上下文堆栈。但是就一个程序本身执行的操作来说,进程其实什么也不做(不执行...

    进程与线程的关系

    简单复习一下:一个”进程“代表中计算机中实际跑起来的一个程序,在现代操作系统的保护模式下,每个进程拥有自己独立的进程地址空间和上下文堆栈。但是就一个程序本身执行的操作来说,进程其实什么也不做(不执行任何进程代码),它只是提供一个大环境容器,在进程中实际的执行体是”线程“。因此一个进程至少得有一个线程,我们把这个线程称之为”主线程“,也就是说,一个进程至少要有一个主线程。

     

    进程中创建线程的限制

    默认情况下,一个线程的栈要预留1M的内存空间,而一个进程中可用的内存空间只有2G,所以理论上一个进程中最多可以开2048个线程。但是内存当然不可能完全拿来作线程的栈,所以实际数目要比这个值要小。

    #include "stdafx.h"
    #include <windows.h>
    #include <process.h>
    #include <assert.h>
    
    #define DEFAULT_STACK//注释该行,则开辟的线程默认栈大小为512KB
    
    volatile bool g_bExitThread = false;
    HANDLE g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    
    
    UINT WINAPI WorkThread(void* ptr)
    {
    	int nThreadID = *((int*)ptr);	
    	printf("%d线程启动\n", nThreadID);
    
    	SetEvent(g_hEvent);
    	while(!g_bExitThread)
    	{
    		Sleep(1);//权宜之计,让线程不要提前退出
    	}
    
    	return 0;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	for(int i=0; i<5000; i++)
    	{
    
    #ifdef DEFAULT_STACK
    		HANDLE _handle = (HANDLE)_beginthreadex(NULL, 0, WorkThread, &i, 0, NULL);
    #else
    		HANDLE _handle = (HANDLE)_beginthreadex(NULL, 512*1024, WorkThread, &i, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
    #endif		
    		
    		if (_handle == 0)
    		{
    			if(GetLastError() == 8)
    			{
    				printf("开启线程失败,存储空间不足!\n");
    			}
    			else
    			{
    				printf("开启线程失败,错误号%d\n", GetLastError());
    			}
    			break;
    		}
    		WaitForSingleObject(g_hEvent, INFINITE);
    	}
    	getchar();
    	return 0;
    }

    使用默认的线程栈大小(1M),从输出结果来看,一个进程最大能开辟的线程数为:1451

    将上面代码的“#define DEFAULT_STACK”注释掉,使用线程的栈大小改为512KB。从输出结果来看,一个进程最大能开辟的线程数为:2284

     

    如何突破2000个限制

    可以通过连接时修改默认栈大小,将其改的比较小,这样就可以多开一些线程。 如将默认栈的大小改成512K,这样理论上最多就可以开4096个线程。但即使物理内存再大,一个进程中可以起的线程总要受到2GB这个内存空间的限制。比方说你的机器装了64GB物理内存,但每个进程的内存空间还是4GB,其中用户态可用的还是2GB。

    如果是同一台机器内的话,能起多少线程也是受内存限制的。每个线程对象都要占用非页面内存,而非页面内存也是有限的,当非页面内存被耗尽时,也就无法创建线程了。如果物理内存非常大,同一台机器内可以跑的线程数目的限制值会越来越大。


     

    展开全文
  • 被问懵了:一个进程最多可以创建多少个线程

    千次阅读 多人点赞 2021-07-15 09:19:09
    大致意思就是,他看了一个面经,说虚拟内存是 2G 大小,然后他看了我的图解系统 PDF 里说虚拟内存是 4G,然后他就懵逼了。 其实他看这个面经很有问题,没有说明是什么操作系统,以及是多少位操作系统。 因为不同的...
  • 转自:http://blog.chinaunix.net/uid-12461657-id-3182841.html线程共享的环境包括:进程代码段、进程的...进程拥有这许多共性的同时,还拥有自己的个性。有了这些个性,线程才能实现并发性。这些个性包括:1.线程ID
  • 进程-线程-多线程 ...程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程进程状态:进程有三个状态,就绪、运行、阻塞。 511遇见易语言多线程大漠多线程
  • 线程共享的环境包括:进程代码段、... 进程拥有这许多共性的同时,还拥有自己的个性。有了这些个性,线程才能实现并发性。这些个性包括:  1.线程ID  每个线程都有自己的线程ID,这ID在本进程中是唯一的。进
  • 一个进程最多能包含多少线程

    万次阅读 2012-10-20 16:22:34
    实验蛮简单的,但是,我不由想到了,一个进程最多能包含多少个线程。 在网上查了查,貌似也没找到多少这方面的资料。大部分都是关于服务器多线程链接sever的,关于本机执行exe的进程数倒是没有什么涉及。 我觉得...
  • 一个进程下的线程共享哪些资源

    千次阅读 2015-10-19 21:04:36
    同一进程中的线程究竟共享哪些资源  ... 线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的... 进程拥有这许多共性的同时,还拥有自己的个性。有了
  • 线程进程

    千次阅读 2016-08-07 12:45:41
    线程进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不少的资源,但它与同属一个进程的其它线程共享进程拥有的全部资源...
  • 进程线程的区别:一个简单又困难的问题

    千次阅读 多人点赞 2020-04-20 02:45:39
    文章目录进程线程的区别:一个简单又困难的问题进程执是什么线程是什么进程线程的区别 进程线程的区别:一个简单又困难的问题 如果简单得记得,进程是操作系统分配资源的最小单位,线程是CPU调度的最小单位。...
  • 线程进程

    千次阅读 2017-09-02 18:51:36
    线程自己基本上不拥有系统资源,只拥有一点在运行中必不少的资源(如程序计数器,一组寄存器和栈),但是它与同属一个进程的其他的线程共享进程拥有的全部资源.2.关系一个线程可以创建和撤销另一个线
  • 一个进程中各线程的堆和栈的关系

    千次阅读 2014-03-13 13:56:12
    在很多现代操作系统中,一个进程的(虚)地址空间大小为4G,分为系统空间和用户空间两部分,系统空间为所有进程共享,而...堆(heap)的分配与栈有所不同,一般是一个进程一个C运行时堆,这个堆为本进程中所有线程
  • 线程进程

    千次阅读 2019-11-06 19:36:09
    线程进程 ...那么此时进程就是这条需要被修的路(任务),线程就是这一个施工队【单进程线程】。 如果在修这条路的时候,来了两个施工队,各修一半,此时就是多线程【多个施工队】,单进程【一...
  • 线程进程、多线程、多进程 和 多任务 小结

    千次阅读 多人点赞 2019-04-20 11:59:56
    5 线程进程的关系 6 线程进程的区别 7 进程的优缺点 7.1 进程的优点 7.2 进程的缺点 8 线程的优缺点 8.1 线程的优点 8.2 线程的缺点 9 多线程的优缺点 9.1 多线程的优点 9.2 多线程的缺点 10多进程的...
  • 线程VS进程

    千次阅读 2021-03-10 16:44:19
    一般的定义:进程是指在操作系统中正在运行的一个应用程序,线程是指进程内独立执行某个任务的一个单元。 怎么理解呢? 比如说QQ是是一个进程,如果你在和A朋友语音聊天的同时和B朋友打字聊天,同时还在QQ群下载图片...
  • 进程线程、死锁

    万次阅读 2018-04-18 11:05:29
    进程   进程:系统进行资源分配和调度的一个独立...线程与同属一个进程的其他线程共享进程拥有的全部资源。线程有 3 个基本状态:就绪、执行、阻塞。 进程线程的区别 进程是资源的分配和调度的一个独...
  • 一个进程拥有的数据和变量属于它自己。 线程(thread):是进程内相对独立的执行单元,所以也被称为轻量进程(lightweight processes);是操作系统进行任务调度的基本单元。它与父进程的其它线程共享该进程所...
  • 进程线程的深入理解

    万次阅读 多人点赞 2019-04-25 00:14:40
    进程线程的深入理解精确概括进程线程的区别 既然要谈区别,那么首先需要理解什么是进程线程。 之前,我读到一篇材料,发现有一个很好的类比,可以把它们解释地清晰易懂。 1.计算机的核心是CPU,它承担了所有的...
  • 进程线程一个简单解释

    千次阅读 2013-05-07 22:16:16
    进程(process)和线程(thread)是操作系统的基本概念,但是它们比较抽象,不容易掌握。 最近,我读到一篇材料,发现有一个很好的类比,可以把它们解释地清晰易懂。 1. 计算机的核心是CPU,它承担了...
  • 一个进程包括由操作系统分配的内存空间,包含一个或多个线程一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。 线程  又称其为轻量级进程...
  • 线程fork进程

    千次阅读 2013-10-15 22:03:10
    一个线程进程的某个线程fork出一个子进程,那么子进程完整复制调用fork的线程,且子进程不会拥有进程那么多的线程,这里出现一个潜在的死锁条件,就是子进程继承了父进程的互斥锁但是并不知道该互斥锁的状态,若...
  • 默认情况下,一个线程的栈要预留1M的内存空间,而一个进程中可用的内存空间只有2G,所以理论上一个进程中最多可以开2048个线程,但是内存当然不可能完全拿来作线程的栈,所以实际数目要比这个值要小。 #...
  • 进程线程

    千次阅读 2018-05-26 10:05:13
    线程自己基本上不拥有系统资源,只拥有一点在运行中必不少的资源(如程序计数器,一组寄存器和栈),但是它与同属一个进程的其他的线程共享进程拥有的全部资源.2.关系一个线程可以创建和撤销另一个线程...
  • 线程自己基本上不拥有系统资源,只拥有一点在运行中必不少的资源(如程序计数器,一组寄存器和栈),但是它与同属一个进程的其他的线程共享进程拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多...
  • 进程线程的概念、区别及进程线程间通信

    千次阅读 多人点赞 2020-08-02 16:24:25
    每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 184,420
精华内容 73,768
关键字:

一个进程只可拥有一个线程