Java 基础(程序,进程,线程)

程序(program)

是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

进程(process)

是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。--生命周期,如: 运行中的QQ,运行中的MP3播放器

程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

线程(thread)

进程可进一步细化为线程,是一个程序内部的一条执行路径。

  1. 若一个进程同一时间并行执行多个线程,就是支持多线程的
  2. 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
  3. 一个进程中的多个线程共享相同的内存单元/内存地址空间 → 它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

jvm 虚拟机


"方法区" 和 "堆" 是线程共享的。"虚拟机栈" 和 "程序计数器" 是每个线程独立拥有的。

单核CPU和多核CPU的理解

  1. 单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以把他“挂起”(晾着他,等他想通了,准备好了钱,再去收费)。但是因为CPU时间单元特别短,因此感觉不出来。
  2. 如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

并行与并发

  1. 并行: 多个CPU同时执行多个任务。比如:多个人同时做不同的事。
  2. 并发: 一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

使用多线程的优点

背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短,为何仍需多线程呢?
多线程程序的优点:

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

何时需要多线程

  1. 程序需要同时执行两个或多个任务。
  2. 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
  3. 需要一些后台运行的程序时。

线程的创建和启动

  1. Java 语言的 JVM 允许程序运行多个线程,它通过 java.lang.Thread 类来体现。
  2. Thread类的特性

每个线程都是通过某个特定 Thread 对象的 run() 方法来完成操作的,经常把 run() 方法的主体称为线程体
通过该 Thread 对象的 start() 方法来启动这个线程,而非直接调用 run()

线程的分类

Java 中的线程分为两类:一种是守护线程,一种是用户线程。

  1. 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
  2. 守护线程是用来服务用户线程的,通过在start()方法前调用
    thread.setDaemon(true) 可以把一个用户线程变成一个守护线程。
  3. Java 垃圾回收就是一个典型的守护线程。
  4. 若JVM中都是守护线程,当前 JVM 将退出。
  5. 形象理解:兔死狗烹,鸟尽弓藏
package com.klvchen.java;

/**
 * @author klvchen
 * @create 2021-04-06-14:38
 *
 * 多线程的创建,方式一: 继承 Thread 类
 * 1. 创建一个继承于 Thread 类的子类
 * 2. 重写 Thread 类的 run()
 * 3. 创建 Thread 类的子类的对象
 * 4. 通过此对象调用 start()
 */
//1. 创建一个继承于Thread类的子类
class MyThread extends Thread{
    //2. 重写Thread类的run()
    @Override
    public void run(){
        for (int i = 0; i < 100; i++){
            if ( i % 2 == 0 ){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest {
    public static void main(String[] args){
        //3.创建Thread类的子类对象
        MyThread t1 = new MyThread();

        //4.通过此对象调用 start(): ①启动当前线程 ②调用当前线程的run()
        //不能通过直接调用 run() 的方式启动线程。
        //不可以还让已经 start() 的线程去执行。会报 IllegalThreadstateException
        t1.start();

        //如下操作仍然是在main线程中执行的。
        for (int i = 0; i < 100; i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i + "***********main()************" );
            }
        }
    }
}

 线程练习

练习3个窗口同时售票100张

使用继承 Thread 方式

package com.klvchen.java;

/**
 * @author klvchen
 * @create 2021-04-08-13:58
 *
 * 例子:创建三个窗口卖票,总票为 100 张
 * 存在线程安全问题,待解决
 */
class Window extends Thread{
    private static int ticket = 100;

    @Override
    public void run(){
        while (true){
            if (ticket > 0){
                System.out.println(getName() + ": 卖票, 票号为: " + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }

}

public class WindowTest {
    public static void main(String[] args){
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
实现Runnable接口的方式

package com.klvchen.java;

/**
 * @author klvchen
 * @create 2021-04-08-14:37
 *
 * 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
 * 存在线程的安全问题,待解决。
 */

class Windows1 implements Runnable{

    private int ticket = 100;

    @Override
    public void run() {
        while (true){
            if (ticket > 0){
                System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}

public class WindowTest1 {
    public static void main(String[] args){
        Windows1 w = new Windows1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

使用实现Runnable方式

1.创建一个实现了Runnable接口的类
2.实现类去实现Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()

package com.klvchen.java;

/**
 * @author klvchen
 * @create 2021-04-08-14:13
 */

//1.创建一个实现了Runnable接口的类
class MThread implements Runnable{

    //2.实现类去实现 Runnable 中的抽象方法:run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args){
        //3.创建实现类的对象
        MThread mThread = new MThread();
        //4.将此对象作为参数传递到 Thread 类的构造器中,创建 Thread 类的对象
        Thread t1 = new Thread(mThread);
        t1.setName("线程1");
        //5.通过 Thread 类的对象调用 start(): 1.启动线程 2.调用当前线程的run()-->调用了Runnable类型的target的run()
        t1.start();

        //再启动一个线程,遍历100以内的偶数
        Thread t2 = new Thread(mThread);
        t2.setName("线程2");
        t2.start();

    }
}

比较创建线程的两种方式

开发中:优先选择:实现Runnable接口的方式原因:
1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况。

相同点: 两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

posted @ 2021-11-29 14:19  民宿  阅读(290)  评论(0)    收藏  举报