Java多线程的常见创建方式与区别、以及同步问题

本文详细介绍了并发与多线程的概念,包括并发、并行、进程、线程的区别,以及多线程的实现方法,如继承Thread类和实现Runnable接口。探讨了线程调度、资源分配和线程安全问题。

相关概念:

  • 并发:指两个或多个事件在同一个时间段内发生。
  • 并行:指两个或多个事件在同一时刻发生(同时发生)。

所谓的并发实质是宏观上同时进行,微观上交替运行,单核处理器同一时刻只能处理一个任务,所以单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。

同理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
多核处理器的系统同一时刻就能有多个任务得到执行。
注意区分时刻和时间段。

  • 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
  • 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
    【总结】进程是资源分配的基本单位,线程是cpu调度的基本单位。线程共享进程的资源,每个线程又有自己的独立空间。
    分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

扩展:在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用 java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程。

创建线程方法:

方法一:继承Thread类来创建并启动多线程

  • 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把 run()方法称为线程执行体
  • 创建Thread子类的实例,即创建了线程对象
  • 调用线程对象的start()方法来启动该线程
    有关Thread类
    常用构造方法:
  • public Thread() :分配一个新的线程对象。
  • public Thread(String name) :分配一个指定名字的新的线程对象。
  • public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
  • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。
    常用方法:
  • public String getName() :获取当前线程名称。
  • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
  • public void run() :此线程要执行的任务在此处定义代码。 public
    static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
  • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

代码如下:
测试类:


public class TestThread {
    public static void main(String[] args) {
        //创建自定义线程对象firstThread
        FirstThread firstThread = new FirstThread("myFirstThread");
        //开启新线程firstThread
        firstThread.start();
        //主线程中的方法
        for (int i = 0; i < 5; i++) {
            System.out.println("main线程正在执行"+i);
        }
    }
}

自定义线程类:

public class FirstThread extends Thread {
    //定义指定线程名称的构造方法
    public FirstThread(String name) {
        super(name);
    }

    /**
     * 重写run方法,定义线程要执行的代码,如不重写,则该线程什么都不执行
     */
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(getName()+":正在执行"+i);
        }
    }
}

结果:
在这里插入图片描述
分析:
程序执行的流程图:
在这里插入图片描述

方法二:利用 java.lang.Runnable接口

步骤如下:

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正 的线程对象。
  3. 调用线程对象的start()方法来启动线程。
    代码如下:
    测试类:
public class TestRunnable {
    public static void main(String[] args) {
        //创建RunnableTarget对象
        RunnableTarget runnableTarget = new RunnableTarget();
        //把RunnableTarget对象和thread的名字作为Thread构造函数的参数传入,让thread能执行RunnableTarget的run方法中的内容
        Thread thread = new Thread(runnableTarget,"runnableThread");
        //开启线程
        thread.start();
        //主线程的执行内容
        for (int i = 0; i < 5; i++) {
            System.out.println("main线程正在执行"+i);
        }
    }
}

自定义的Runnable接口实现类:

/**
 * 自己写一个Runnable接口实现类,作为Thread构造函数的参数targer传入,创建Thread对象,
 * 注意,这种方式最终执行的对象是thread,而执行的内容是Runnable实现类中重写的run方法内的内容。
 */
public class RunnableTarget implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"执行到了"+i);
            
        }
    }
}

执行结果;
在这里插入图片描述
Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。 而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。

在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread 对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。

因此,不管是继承Thread类还是实现 Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程 编程的基础。

继承Thread和实现Runnable两种方式的区别:

继承Thread实现的模式是定义多个线程,各自完成各自的任务.,而实现Runnable实现的模式是 定义多个线程,实现一个任务.可理解为一个是多个线程分别完成自己的任务,一个是多个线程共同完成一个任务。
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。继承Thread类的线程有着自己独立的资源空间,而实现Runnable接口的方式,所有线程都享有该Runable实现类的资源,比如在该类中放一笔钱,每个线程都能从中取钱放钱,并且效果是叠加的。
实现Runnable接口比继承Thread类所具有的优势:

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

当多个线程同时对共享资源进行写操作时就会产生线程安全的问题,使得多线程运行结果和单线程运行结果不一致。为此需要采用一些方法来保证线程安全。
线程安全的分析见:多线程的线程安全问题

有关多线程的优缺点:
优点:虽然正确的使用多线程能够能适当提高程序的执行效率和资源利用率(CPU、内存利用率)

缺点:
开启线程需要占用一定的内存空间,如果开启大量的线程,会占用大量的内存空间,降低程序的性能,切换上下文有很大的开销,线程越多,CPU在调度线程上的开销就越大,程序设计更加复杂:比如线程之间的通信、多线程的数据共享。

所以使用多线程需谨慎,不能为了多线程而多线程,有时反而会使效率变低。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值