java多线程基础入门系列(1)

java多线程基础(1)

“近期重新学了java多线程的知识,之前学过没怎么用,慢慢的也就忘得差不过了。这次就把多线程的基础知识在此记录一下~”

基础概念

并发与并行

  • 并发:一个CPU在同一时间段有多个程序在执行

  • 并行:同一时刻有多个程序在执行,在多核(多个CPU)的计算机中可以实现

    因为CPU运行的速度非常的快,所以这里的同一时间段和同一时刻都是针对于微观时间而言,我们人的感受上区别不出来这两个有什么区别。例如在单核的计算机中,我们仍然可以在同一时刻打开多个程序运行,但是其实是因为CPU快速的在多个程序中切换执行,让我们误以为是在“同时”执行,但是实际上单核CPU中每个时刻只能有一代码块在运行。

线程与进程

  • 进程:指的是在计算机中已经运行的程序,也可以将之看成是内存中一块独立的程序运行空间。

    例如我们打开一个客户端软件(有些软件同时开启多个进程),在IDEA中编译运行一个main方法等。这些都会开启一个进程,并在内存中划出一个独立的空间,让进程独立运行。因此一般情况下,进程中的数据是不会共享的。

    进程是操作系统运行程序的一个基本单位,我们说系统在运行一个程序即是指一个进程从创建,运行,到被销毁的过程。

  • 线程:进程是系统中程序运行的基本单位,而线程又是进程中程序执行的一个单元,用来负责进程中程序的执行,因此一个进程至少有一个线程。当一个进程中有多个线程在运行时,这个程序也就可以称之为多线程程序。

    例如,在IDEA中编译运行一个main方法,在开启一个进程的同时,该进程里面也会开启一个线程,即main线程。当使用java中多线程技术时即可在该进程中开启多个线程。

    可以将线程看成是CPU执行程序的一条路径(线)。

因此,可以将内存看成是进程的容器,而进程又是线程的容器。

PS:在windows10系统中,任务管理器里面即可查看当前电脑打开的进程

线程调度

由于多线程程序在CPU中可以并发运行,但是CPU使用权在每个时刻只能被一个线程所占有,如果是按照一个程序执行完再执行另外一个程序的话,就会造成卡顿的现象。为了合理的安排各个线程轮流占用CPU的使用,就有了线程调度机制。

线程调度机制就是为多个线程分配CPU的使用权。

调度方式

  1. 分时调度

    所有的线程轮流的获取CPU的使用权,平均分配每个线程占有CPU的使用时间。

  2. 抢占式调度

    设置线程的优先级,让优先级高的线程优先占有CPU使用权,如果优先级相同的话就会随机选择一个线程执行,在java就是使用抢占式调度的方式。

创建多线程

java使用Thread类实例来创建一个线程,所有的线程对象都线程的Thread类的实例或是其子类的实例。Thread类继承Runnable接口,并实现该接口的唯一一个抽象方法run(),该方法里面的代码就是每个线程中执行的代码。就像刚开始执行程序时,会创建一个main线程,该线程执行的代码就是main方法里面的代码。而当我们在程序中另外创建一个线程时,其执行的就是run方法里面的代码了。

下面就来讲java中创建线程的几种方式

继承Thread类创建

  1. 自定义线程类

    public class MyThread extends Thread {
          
          
        public MyThread() {
          
          }
        /**
        * 带参构造,可以指定该线程的名称
        */
        public MyThread(String name) {
          
          
            super(name);	//调用父类Thread的构造方法指定线程名称
        }
        
        /**
        * 重写run方法 -- 必须
        */
        @Override
        public void run() {
          
          
        	/* 线程执行的代码  */
        }
    }
    
  2. 创建实例

    public class Test {
          
          
        public static void main(String[] args) {
          
          
            MyThread myThread = new MyThread("新线程名称");	//创建一个线程实例
            myThread.start();	//开启新线程 -- 执行线程中run方法的内容
        } 
    }
    

    注意:要调用start方法才会自动执行run中的方法,并不是调用run方法来执行。

调用start()方法开启一个新线程的时候,并不是立刻就会执行该线程中run中的代码。因为java是采用抢占式调度的方式来分配CPU的使用,新创建的线程在不设置其优先级的情况下优先级是相同的。因此在调用start方法之后,该线程参与CPU使用权的抢夺,和main线程抢夺或者是和另外创建的线程抢夺,能不能抢夺到还是一回事,只有抢夺到了才会执行该线程对应的run方法。

Thread类常用方法

构造方法

  • public Threa():无参构造,创建一个新的Thread对象

  • public Thread(Runnable target):传入Runnable接口实现类对象,在启动线程的时候调用target接口的run方法。

  • public Thread(String name):自定义线程名称

  • public Thread(Runnable target, String name):传入Runnable接口对象,并自定义线程名称

    上述构造方法中传入Runnable接口实现类时需要重写该接口的方法,该接口只有run一个抽象方法,具体实现会在后面举例

常用的基础方法

  • public String getName():获取当前线程名称

    main	//main线程名称
    Thread-0	//未设置名称时的线程名称
    
  • public static void sleep(long millis):使当前正在执行的线程停止执行指定的毫秒数,时间一到就自动CPU就自动恢复到该线程继续执行下去。

  • public final void setPriority​(int newPriority): 设置此此线程的优先级

  • public static Thread currentThread():获取当前正在执行的线程

  • public void start():开启一个新线程

  • public void run():在该方法中指定新线程要执行的代码

继承Runnable类创建

在Thread的构造方法中,有一个**public Thread(Runnable target)**的构造方法,即传入Runnable接口的实现类对象,重写里面的run方法创建一个新线程。值得注意的是,Thread类本身就是实现Runnable接口的,Runnable接口只有一个run方法(函数式接口)。

  1. 定义Runnable实现类接口
public class RunnableDemo implements Runnable{
    
    
    @Override
    public void run() {
    
    		//重写run方法
        /* 线程执行代码 */
    }
}
  1. 创建实例
public static void main(String[] args) {
    
    
	Runnable run = new RunnableDemo();
	Thread thread = new Thread(run);
	thread.start();		//注意最后要调用start方法才会开启该多线程
}

使用匿名内部类的方式

上面创建Runnable接口实现类的时候可以发现,创建实现类仅仅是为了重写其run方法,如果要在线程中执行的代码有各种各样的话,就要创建各种各样的实现类,还只是重写其run方法,反倒有点复杂了。

而使用匿名内部类的方式可以更方便的创建一个Thread接口:

Thread thread = new Thread(new Runnable() {
    
    
    @Override
    public void run() {
    
    
    	System.out.println("这是一个匿名内部类");
    }
});
thread.start();
//也可以
new Thread(new Runnable() {
    
    
    @Override
    public void run() {
    
    
    	System.out.println("这是一个匿名内部类");
    }
}).start();

使用Lambda表达式创建

在JDK8中引入了Lambda表达式的新特性,有关于Lambda表达式会在以后中讲到,先来看看怎么用:

new Thread(()->{
    
    
	/* 这里即可书写run方法中的内容 */
}).start();

使用Thread和Runnable的区别

  1. 使用Runnable接口是很容易实现资源共享的,同时可以实现代码实现和线程相互独立
  2. 后面的线程池中只能放入实现Runnable接口的类,不能直接放入Thread类。
  3. 可以使用Lambda表达式,代码书写更加简便

小结

  1. 进程是系统执行程序的一个基本单位,是一个静态的概念,相当于内存中的一块运行空间。
  2. 线程是系统CPU调度的一个基本单位,是一个动态的概念,存在与进程中。
  3. 创建线程的方式多种多样,使用匿名内部类或Lambda表达实现Runnable接口会更简洁。

但是上述创建线程的方法在工作中应该基本不会用到,阿里的开发手册中就有提到:“线程资源必须通过线程提供,不允许在应用中显性的创建”。只是在学习过程中还是有必要知道最开始的创建方式~

猜你喜欢

转载自blog.csdn.net/weixin_44184990/article/details/108375374