目录

一、辨析线程与进程

二、单线程运行和多线程运行的区别

三、线程创建

3.1 继承Thread类

3.2 实现Runnable接口

3.3 实现Callable接口

四、Thread 和Runnable的区别


一、辨析线程与进程

进程Process

线程Thread

基本单位

系统资源分配的基本单位

CPU调度和执行的基本单位

状态

动态

静态

区别

①是可并发执行的具有独立功能的应用程序(相当于电脑可同时打开多个浏览器的多个界面)

②拥有运行空间和资源(相当于应用程序中的图像、声音、文字等)

①只是执行的代码

② 不具有资源

联系

一个进程中的每个任务称为一个线程

        线程的基本概念:

                1)线程就是独立的执行路径;
                2)在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程(用户线程),gc线程(守护线程);main()称之为主线程,为系统的入口,用于执行整个程序;
                3)在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,操作系统紧密相关的,先后顺序是不能人为干预的。
                4)对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;线程会带来额外的开销,如cpu调度时间,并发控制开销。

二、单线程运行和多线程运行的区别

java多线程怎么释放_eclipse

                 单线程:进程由main()函数开始执行,只包含一个主线程

                多线程:在main()函数中创建多个线程对象,并调用start()函数启动并发执行

三、线程创建

3.1 继承Thread类

线程类(类声明为Thread子类)

线程类名称

线程类中重写run()方法

                                                public void run(){

                                                                ......;  //该线程想要执行的操作

                                                }

线程类对象,并调用start()函数启动

                                              线程类().start();//函数 线程类() 是初始化线程类对象

      2、 示例:创建两个线程,观察多线程的运行次序的实现机制

//通过继承Thread类创建线程,“线程1”和“线程2”分别执行10次

package Thread;

public class TwoThreadsTest {
     public static void main (String[] args) {
         new SimpleThread("线程1").start();//虽然线程的定义位于run()方法里,线程的运行调用start(),相当于调用线程管理器
         //调用start相当于去线程管理器注册线程运行的方式及准备工作,主线程调用start(),子线程执行run()方法
        SimpleThread("线程2").start();
      }
     }

class SimpleThread {
     public SimpleThread(String str) {   //线程对象的初始化函数定义
         super(str);//继承父类Thread属性
     }
     public void run() {//重写run函数
         for (int i = 0; i < 10; i++) {//输出10次,getName:获得线程的名字
             System.out.println(i + " " + getName());
             try {
                 sleep((int)(Math.random() * 1000));//暂停随机的将近1s(sleep(1)是暂停1ms)
             } catch (InterruptedException e) {}//有异常干扰时,进程结束
         }
         System.out.println("DONE! " + getName());//获得线程名字
     }
 }

        运行结果:"线程1"和"线程2"交叉输出,说明两个线程是同时执行的;每一次点击运行时,各个线程输出顺序是不同(随机)的。

 

java多线程怎么释放_java多线程怎么释放_02

3.2 实现Runnable接口

线程类(该类是实现类Runnable接口)

线程类名称

线程类中重写run()方法

                                                public void run(){

                                                                ......;  //该线程想要执行的操作

                                                }

                  ③在main()函数中创建该线程类对象,并类型转化为Thread类型,调用start()函数启动

线程类  t = new 线程类();

t).start();

      2、同样的示例如下:

//通过实现Runnable类创建线程,“线程1”和“线程2”分别执行10次

package Thread;

public class ThreadRunnable2 {
     public static void main (String[] args) {
         
         SimpleThread t1 = new SimpleThread("线程1");
         SimpleThread t2 = new SimpleThread("线程2");
         
         new Thread(t1).start();   //强制类型转化,转化为Thread类型才能启动start()函数
         new Thread(t2).start();
      }
     }

class SimpleThread implements Runnable {
     String string;
     public SimpleThread(String str) {
         //由于没有“继承”的关系,不使用super,而是关键字this定义子类变量
         this.string = str;
     }
     public void run() {//重写run函数
         for (int i = 0; i < 10; i++) {//输出10次,由于没有“继承”的关系,不能使用Thread内置的getName函数获得变量名称
             System.out.println(i + " " + string);
             try {
                 Thread.sleep((int)(Math.random() * 1000));//sleep函数的使用继承Thread类,即Thread.sleep
             } catch (InterruptedException e) {}//有异常干扰时,进程结束
         }
         System.out.println("DONE! " + string);//获得线程名字
     }
 }

运行结果:

java多线程怎么释放_java_03

3.3 实现Callable接口

线程类,实现Callable接口,需要返回值类型

线程类名称

线程类中重写call()方法,主函数声明时需要抛出异常

    

public <返回值类型> call(){
                                                                ......;          //该线程想要执行的操作
                                                                try {          
                                                                ......;
                                                                      } catch (InterruptedException e) {}
r;
                                                }

                        ③在main()函数中创建该线程类对象,通过创建执行服务启动(步骤④~⑦)

线程类  t = new 线程类();

                        ④创建执行服务

                                                ExecutorService ser = Executors.newFixedThreadPool(n);   //n为线程池中线程的个数,ser为执行服务类对象

                        ⑤提交执行

                                                Future<返回值类型> result = ser.submit(t);

                        ⑥获取结果

                                                <返回值类型>   r

                        ⑦关闭服务

                                                ser.shutdownNow();

        2、示例如下:

//通过实现Callable类创建线程,“线程1”和“线程2”分别执行10次

package Thread;


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class ThreadCallable {
     public static void main (String[] args) throws InterruptedException, ExecutionException {
         
         SimpleThread2 t1 = new SimpleThread2("线程1");
         SimpleThread2 t2 = new SimpleThread2("线程2");
         
        //创建执行服务(线程池创建2个线程)
         ExecutorService ser = Executors.newFixedThreadPool(2);
         
         //提交执行
         Future<Boolean> r1 = ser.submit((Callable<Boolean>) t1);
         Future<Boolean> r2 = ser.submit((Callable<Boolean>) t2);
         
         //获取结果
         boolean rs1 = r1.get();
         boolean rs2

         //关闭服务
         ser.shutdown();
      }
     }

class SimpleThread2 implements Callable<Boolean> {  //这里假设返回的是布尔类型(不影响结果的输出)
     String string;
     public SimpleThread2(String str) {
         //由于没有“继承”的关系,不使用super,而是关键字this定义子类变量
         this.string = str;
     }
     public Boolean call() {//重写call函数
         for (int i = 0; i < 10; i++) {//输出10次,由于没有“继承”的关系,不能使用Thread内置的getName函数获得变量名称
             System.out.println(i + " " + string);
             try {
                 Thread.sleep((int)(Math.random() * 1000));//sleep函数的使用继承Thread类,即Thread.sleep
             } catch (InterruptedException e) {}//有异常干扰时,进程结束
         }
         System.out.println("DONE! " + string);//获得线程名字
         return true;
     }
 }

        3、运行结果:

java多线程怎么释放_Boo_04

四、Thread 和Runnable的区别

        若一个类继承Thread,则不适合资源共享;但是若一个类实现了Runable接口,容易实现资源共享

        总结:实现Runnable接口比继承Thread类所具有的优势

        1. 适合多个相同的程序代码的线程去共享同一个资源。
        2. 可以避免java中的单继承的局限性。
        3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
        4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。