进程和线程详解

一、进程和线程的概述

1、要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在

2、什么是进程?

      通过任务管理器我们就看到了进程的存在。 而通过观察,我们发现只有运行的程序才会出现进程。
      进程:就是正在运行的程序
      进程:是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

3、多进程有什么意义呢?

      单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。

      举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。 也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个程序。 可以提高CPU的使用率。

      问题: 一边玩游戏,一边听音乐是同时进行的吗?

                  不是。因为单核CPU在某一个时间点上只能做一件事情。 而我们在玩游戏,和在听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。

4、什么是线程?

      在同一个进程(正在运行的程序)内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。
      线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
      单线程:如果程序只有一条执行路径。

      多线程:如果程序有多条执行路径。 一个进程 = 一个正在运行的程序 = 1个线程+1个线程+1个线程+... = 多个线程 = 多个任务

 

5、多线程有什么意义呢?

      多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率(提高能使用cpu的概率)。 程序的执行其实都是在抢CPU的资源,或者叫CPU的执行权。 多个进程是在抢这个资源,而其中的某一个进程如果执行路径(线程)比较多,那么就会有更高的几率抢到CPU的执行权。 我们是不敢保证哪一个线程能够在哪个时刻抢到cpu的执行权的,所以线程的执行具有随机性

小结:
  多进程的意义:提高使用cpu的效率。(多用cpu)
  多线程的意义:提高能使用cpu的概率。(有多少机会用cpu,因为cpu不是你想用就能用啊)

多线程程序的引入图解

二、Java程序的运行原理及JVM的启动是多线程的吗?

  A:Java程序的运行原理
    Java通过java命令会启动java虚拟机。启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
     该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
  B:JVM的启动是多线程的吗?
    垃圾回收线程也要先启动,否则很容易会出现内存溢出
     JVM的启动是多线程的,因为它最低有两个线程启动了,主线程垃圾回收线程

示例代码如下:

1 package cn.itcast_01;
 2 /*
 3  *    进程:
 4  *        正在运行的程序,是系统进行资源分配和调用的独立单位。
 5  *        每一个进程都有它自己的内存空间和系统资源。
 6  *    线程:
 7  *        是进程中的单个顺序控制流,是一条执行路径。
 8  *        是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
 9  *
10  *        一个进程如果只有一条执行路径,则称为单线程程序。
11  *        一个进程如果有多条执行路径,则称为多线程程序。
12  *
13  *  举例:
14  *      扫雷程序,迅雷下载
15  *  
16  *  大家注意两个词汇的区别:并行和并发。
17  *        并行:前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
18  *        并发:后者是物理上同时发生,指在某一个时间点同时运行多个程序。
19  *
20  *      
21  *
22  * Java程序的运行原理:
23  *        通过java命令会启动 java虚拟机。启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。
24  *        该进程会自动启动一个 “主线程”,然后主线程去调用某个类的 main方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
25  * 
26  * 思考题:
27  *         jvm虚拟机的启动是单线程的还是多线程的?
28  *             多线程的。
29  *             原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。
30  *             现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。
31  */
32 public class MyThreadDemo {
33     public static void main(String[] args) {
34         System.out.println("hello");
35         new Object(); // 造对象
36         new Object(); // 造对象
37         new Object(); // 造对象
38         new Object(); // 造对象
39         //...造很多很多对象后,如果垃圾回收线程不启动的话,内存就会溢出!
40         System.out.println("world");
41     }
42 }

  

3、多线程的实现方案(掌握)

A:自定义类继承Thread类

1:自定义类MyThread继承Thread类

2:MyThread类里面重写run()方法

3:在测测试类MyThreadTest中创建MyThread类的对象

4:启动线程

 

 

代码演示:

package com.thread;

public class ThreadDemo extends Thread {
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"执行了"+i+"次");
        }
    }
}

  

class Test{
    public static void main(String[] args) {
        ThreadDemo thread = new ThreadDemo();
        thread.start();
    }
}

  

运行结果:

Thread-0执行了0次
Thread-0执行了1次
Thread-0执行了2次
Thread-0执行了3次
Thread-0执行了4次
Thread-0执行了5次
Thread-0执行了6次
Thread-0执行了7次
Thread-0执行了8次
Thread-0执行了9次

  

B:自定义类实现Runnable接口

1:自定义类MyRunnable实现Runnable接口

2:MyRunnable类里面重写run()方法

3:在测测试类MyRunnableTest中创建MyRunnable类的对象

4;在测测试类MyRunnableTest中再创建Thread类的对象,并把3步骤的对象作为构造参数进行传递

5:启动线程

 代码演示:

package com.thread;

public class Runable1 implements Runnable{
    
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"执行了"+i+"次");
        }
    }
}

  

class Runnable1Demo {
    public Runnable1Demo() {
    }

    public static void main(String[] args) {
        Runable1 runable1 = new Runable1();
        Thread thread = new Thread(runable1);
        Thread thread1 = new Thread(runable1);

        thread.start();
        thread1.start();
    }
}

  运行结果:

Thread-0执行了0次
Thread-1执行了0次
Thread-0执行了1次
Thread-1执行了1次
Thread-0执行了2次
Thread-1执行了2次
Thread-0执行了3次
Thread-1执行了3次
Thread-0执行了4次
Thread-1执行了4次

  

注意事项:
    1:Thread类的方法:
        public final String getName() 获取线程对象的名称(一般放在需要被线程执行的代run()方法里面)
        public final void setName(String name) 设置线程对象的名称
        对象名.setName("林青霞");
                    
    2:在不是Thread类的子类中,如何获取线程对象的名称呢?
        public static Thread currentThread() 返回当前正在执行的线程对象(静态方法)
        Thread.currentThread().getName()
            
    3:该自定义的类为什么要重写run()方法?
        自定义类中不是所有的代码都需要被线程执行。
        而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()方法,用来包含那些需要被线程执行的代码。    
        注意:这里的  被线程执行 = 开一个新线程执行    
            
    4:由于自定义类实现了接口,所以就不能在自定义类中直接使用Thread类的getName()方法了,但是可以间接的使用。
        Thread.currentThread().getName()

   5:为什么要实现Runnable接口?
     -Java不支持多继承
-不打算重写Thread类的其他方法
小问题:
    1:为什么要重写run()方法?
        :run()方法里面封装的是被线程执行的代码。    
    2:启动线程对象用的是哪个方法?
        :start()方法
    3:run()方法和start()方法的区别?
        :run()方法直接调用仅仅是普通方法。
           start()方法是先启动线程,再由jvm去调用run()方法。
    4:有了方式1,为什么还来一个方式2呢?
        :若自定义类MyThread类已经有一个父类了,那么它就不可以再去继承Thread类了。(java不支持多继承)
           若自定义类MyRunnable类已经实现了一个接口了,那么它还可以再去实现Runnable接口。(java支持多实现)
           即可以避免由于Java单继承带来的局限性。
            
           在测试类MyThreadTest中,要想开多个线程,就要先new多个自定义类MyThread的对象,每一个自定义类MyThread的对象的成员变量都相同,这样需要在栈中开辟很多内存;
           在测试类MyRunnableTest中,要想开多个线程,只需要new一个自定义类MyRunnable的对象,再new多个Thread类的对象即可,这样就大大节约了内存。
           即适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码和数据有效分离(即耦合性降低),较好的体现了Java面向对象的设计思想

4、线程的调度模型和如何获取和设置线程优先级

      假如我们的计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。 那么Java是如何对线程进行调用的呢?

线程有两种调度模型。

A:线程的调度模型

        a:分时调度模型 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。

        b:抢占式调度模型 (Java采用的是该调度方式) 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片的概率相对高一些。

B:如何获取和设置线程优先级

         线程默认的优先级是:5。

         线程优先级的范围是:1-10。

 

如何获取线程对象的优先级?

         public final int getPriority()

         返回线程对象的优先级 int i = 对象名.getPriority();

如何设置线程对象的优先级?

         public final void setPriority(int newPriority) 更改线程的优先级 对象名.setPriority(10); IllegalArgumentException:非法参数异常 抛出的异常表明向方法传递了一个不合法或不正确的参数。

 5、线程的控制(即线程常见的方法)

A:线程休眠

     public static void sleep(long millis)    单位是毫秒(该方法会抛出异常)
     Thread.sleep(1000);
package com.thread;

public class Sleep implements Runnable{
   
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            if (i==5){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"执行了"+i+"次");
        }
   }
}

  

class SleepDemo {
    public SleepDemo() {
    }

    public static void main(String[] args) {
        Sleep sleep = new Sleep();
        Thread thread = new Thread(sleep);
        thread.start();

    }
}

  



B:线程加入 public final void join() 等待该线程终止(为了使某线程先执行完毕)(该方法会抛出异常) 对象名.join(); // 该方法必须在启动线程后调用
package com.join;

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(this.getName()+"执行了"+i+"次");
        }
    }
}


class JoinDemo {
   
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();

        try {
            myThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i=0;i<5;i++){
            System.out.println("主线程执行了"+i+"次");
        }

        System.out.println("主线程执行结束");
    }
}

  

  运行结果:

Thread-0执行了0次
Thread-0执行了1次
Thread-0执行了2次
Thread-0执行了3次
Thread-0执行了4次
Thread-0执行了5次
Thread-0执行了6次
Thread-0执行了7次
Thread-0执行了8次
Thread-0执行了9次
主线程执行了0次
主线程执行了1次
主线程执行了2次
主线程执行了3次
主线程执行了4次
主线程执行结束

  


C:线程礼让
public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 能够在一定的程度上,让多个线程的执行更和谐,但是不能靠它保证一个线程一次。 Thread.yield(); D:后台线程(守护线程/用户线程) public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程 对象名.setDaemon(true); // 设置守护线程 当正在运行的线程都是守护线程时,Java虚拟机退出。该方法必须在启动线程前调用。 E:中断(终止)线程(掌握) public final void stop() 让线程停止,过时了,但是还可以使用。(为什么会过时呢?因为该方法太暴力了,具有固有的不安全性,直接把线程停止,该线程之后的代码都不能执行了) 对象名.stop(); public void interrupt() 中断线程。 把线程的状态终止,并抛出一个InterruptedException异常。 对象名.interrupt(); 注意事项: 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws。

6、线程的状态和生命周期

A:线程的创建 创建线程对象,无资格无权。

B:线程的就绪 有资格无权

C:线程的运行 有资格有权

D:线程的阻塞 无资格无权

E:线程的死亡 无资格无权

 





posted @ 2019-05-06 16:25  泰斗贤若如  阅读(824)  评论(0编辑  收藏  举报