首页 > 其他分享 >多线程

多线程

时间:2023-04-24 21:57:51浏览次数:30  
标签:同步 Thread 创建 Runnable 线程 多线程 方法

章节概述:

程序(program)是为了完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和小王的过程。————生命周期

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

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

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

并行:多个cpu同时执行多个任务
并发:一个cpu采用时间片同时执行多个任务

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

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

线程的创建和使用:

java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来实现

/**
 *多线程的创建,方式一:继承于Thread类
 * 1.创建一个继承于Thread类的子类
 * 2.重写Thread类的run()
 * 3.创建Thread类的子类的对象
 * 4.通过此对象调用start()
 *
 * 例子:遍历100以内的所有的偶数
 */
//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():1、启动当前线程2、调用当前线程的run()
        t1.start();
        //问题一:我们不能通过直接调用run()的方式启动线程
        //t1.run();

        //问题二:再启动一个线程,遍历100内的偶数,不可以还让已经start()的线程去执行,报IllegalThreadStateException我们需要重新创建一个线程的对象
        t1.start();
        MyThread t2 = new MyThread();
        t2.start();

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

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

Thread类的有关方法:
void start():启动线程,并执行对象的run()方法
run():线程在被调度时执行的操作
String getName():返回线程的名称
void setName(String name):设置该线程名称
static Thread currentThread():返回当前线程。在Thread子类中就是this,通常于主线程和Runnable实现类
static void yield():线程让步

  • 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
  • 若队列中没有同优 先级的线程,忽略此方法

join(): 当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,知道join()方法加入的join 线程执行完为止 低优先级的线程也可以获得执行

static void sleep(long millis):(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU的控制,使其他线程有机会被执行,时间到后重排队,抛出InterruptedException异常

stop():强制线程生命期结束,不推荐使用

boolean isAlive():返回boolean,判断线程是否还活着。

class HelloThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            if (i%20==0){
                yield();
            }
        }
    }
    HelloThread (String name){
        super(name);

    }
}



public class ThreadMethodTest {

    public static void main(String[] args) {
        HelloThread h1 = new HelloThread("Thread:1");
        h1.start();
        //给主线程命名
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            if (i==20){
                try {
                    h1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

线程的调度:
调度策略:时间片,抢占式:高优先级的线程抢占CPU
Java的调度方式:

同优先级线程组成先进先出队列(先到先服务),使用时间片策略
对高优先级,使用优先调度的抢占式策略

线程的有限级等级:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5

涉及的方法:
getPriority():返回线程有限制
setPriority(int newPriority):改变线程的优先级

说明:
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

创建多线程的方式二:实现Runnable接口

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

//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(i);
            }
        }
    }
}


public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建实现类的对象
        MyThread myThread= new MyThread();
        //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(myThread);
        //5.通过Thread类的对象调用start()
        t1.start();

        //再启动一个线程
        Thread t2 = new Thread(myThread);
        t2.start();
    }
}

解决线程安全问题

说明:
1、操作共享数据的代码,即为需要被同步的代码。共享数据:多个线程共同操作的变量。
2、同步监视器,俗称锁。任何一个类的对象都可以充当锁。要求:多个线程必须要公用同一把锁
补充:在实现Runnable接口创建多线程的方式中,我们考虑使用this充当同步监视器

方式一:同步代码块

synchronized(同步监视器){
	//需要被同步的代码
}

方式二:同步方法:如果操作共享数据的代码在一个方法中,我们不妨将此方法声明为同步的即在方法前加synchronized
1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
2.非同步的同步方法,同步监视器是:this。静态的同步方法,同步监视器是:当前类本身。

方式三:Lock(锁)
从JDK5.0开始,Java提供了更强大的线程同步机制————通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
Java.util.concurrent.locks.Lock接口时控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能由一个线程对Lock对象加锁。线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁

//1.实例化
private ReentrantLock lock = new ReentrantLock();
try{
	//调用锁定的方法
	lock.lock();

}finally{
	//调用解锁方法,
	lock.unlock();

}

线程的死锁问题

死锁:不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。出现死锁后,不会出现异常,不会出现提示,只是所有线程都处于阻塞状态,无法继续

线程的通信

涉及到的三个方法:
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
notify():一旦执行此方法,就会唤醒被wait的一个线程,如果又多个线程被wait,就唤醒优先级高的
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
说明:

  1. wait(),notify(),notifyAll()三个方法必须是使用在同步代码块或同步方法中。
  2. wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则回出现异常
  3. wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中

面试题:sleep()和wait()的一同
1、相同点:一旦执行方法,都可以使用当前线程进入阻塞状态。
2、不同点:两个方法声明的位置不同,Thread类声明在sleep(),Object类中声明wait();调用的要求不同,sleep()可以在任意需要场景下调用,wait()必须使用在同步代码块或同步方法中;关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。

JDK5.0新增线程创建方式

新增方式一:实现Callable接口
与Runnable相比,Callable功能更强大些,相比run()方法可以有返回值,方法可以抛出异常,支持泛型的返回值,需要借助FutureTask类,比如获取返回结果

Future接口:可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。FutureTask是Future接口的唯一的实现类。FutureTask同时实现了Ruannable,Future接口,它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

步骤:1.创建一个实现Callable的实现类。2.实现call方法,将此线程需要执行的操作声明在call()中。3、创建Callable接口实现类的对象。4、将此Callable接口实现类的对象传递到FutureTask的构造器中,创建FutureTask的对象。5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并start()调用。6.获取Callable中call方法的返回值(get()返回值几位FutureTask构造前期参数Callable实现类重写的call()的返回值。
本人在项目中遇到的多线程:

 // 定义线程列表
        List<CompletableFuture<ModelAndView>> futureList = new ArrayList<>();
        // 循环查询,每次查询一页
        for (int pageNo = 1; pageNo <= listSize / pageSize + 1; pageNo++) {
            int number = pageNo;
            // 开启线程查询
            CompletableFuture<ModelAndView> completableFuture = CompletableFuture.supplyAsync(() -> {
                return addDataToModelAndView(new ModelAndView(), queryWrapper, number, pageSize);
            }, executorService);
            futureList.add(completableFuture);
        }
        // 等待每个线程结束后
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();
        List<ParkBlackList> resultList = new ArrayList<>();
        futureList.forEach(a -> {
            try {
                // 将结果添加到列表中
                resultList.addAll((List<ParkBlackList>) a.join().getModel().get(NormalExcelConstants.DATA_LIST));
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 将数据添加到Excel文件内
        mv.addObject(NormalExcelConstants.CLASS, ParkBlackList.class);
        mv.addObject(NormalExcelConstants.DATA_LIST, resultList);

如何理解实现Callable接口的方式比Runnable接口更强大:
1、call()可以有返回值的。2.call()方法可以跑出异常。3.callable是支持泛型的

新增方式二:使用线程池的方式
背景:经常创建和销毁、使用量特别打的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
好处:提高响应速度、降低资源消耗、便于线程管理
JDK5.0起提供了线程池相关的API:ExecutorService和Executors
ExecutorService:真正的线程池接口,常见子类ThreadPoolExcecutor:
void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
<T>Future<T> submit(Callable<T>task):执行任务,有返回值,一般用来执行Callable
void shutdown():关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool():创建一个根据需要创建新线程的线程池
Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池
Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或定期地执行。

步骤:
1.提供指定线程数量地线程池 2.执行指定地线程地操作,需要提供实现Runnable接口实现类地对象

标签:同步,Thread,创建,Runnable,线程,多线程,方法
From: https://www.cnblogs.com/rhy2103/p/17299932.html

相关文章

  • HashMap 以及多线程基本感念
     接口Map:映射项,(键值对)的容器注意: 键是唯一的 值是可以重复的实现类HashMap :哈希表结构允许使用null值和null键线程不安全键唯一无序 linkedHashMap: 保证插入顺序和迭代顺序一致 Hashtable:数据结构:哈希表不允许使用nullnull值和null键,线程安全 ......
  • C# 多线程记录
    ​ 开发中经常遇到不同的业务访问同一个数据源,而每一个业务的执行流就是一个线程,此时线程一多就会产生多线程最容易遇到的问题——并发。什么是并发?        举个很经典的例子:程序中我们经常要操作一些对象,尤其是内存中的数据             例如当......
  • Android多线程通信-handler机制
    AndroidStudio主线程不允许耗时操作,最后通过多线程实现了AndroidStudio远程连接数据库但是主线程可以分线程传值,分线程却不可以直接向主线程传值AndroidStudio通过handler和massage解决了这个问题 话不多说,看代码——privateHandlerhandler=newHandler(){@Overridep......
  • Python之多进程与多线程
     1.1线程frommultiprocessingimportProcessdefshow(name):print("Processnameis"+name)if__name__=="__main__":proc=Process(target=show,args=('subprocess',))proc.start()proc.join()......
  • python--多线程:锁 、全局锁、Queue队列以及线程池
    关于如何加锁,获取钥匙,释放锁:lock=threading.Lock():生成锁对象,全局唯一;lock.acquire():获取锁。未获取到会阻塞程序,直到获取到锁才会往下执行;lock.release():释放锁,归回后,其他人也可以调用;【注意事项】:lock.acquire()和lock.release()必须成对出现,否则就有可能造成......
  • C++ - 多线程之初识多线程
    1.认识多线程传统的C++(C++11之前)中并没有引入线程这个概念,在C++11出来之前,如果我们想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的<pthread.h>,或者windows下的<windows.h>。C++11提供了语言层面上的多线程,包含在头文件<thread>中。它解决了跨平台的问题,提供......
  • ZSTD多线程压缩
    测试代码:1//main.cpp2//34#include<iostream>5#include<fstream>6#include<vector>7#include<chrono>8#include<thread>9#include"./zstd/lib/zstd.h"1011usingbyte=uint8_t;12usingbuff......
  • python多线程接口请求
    importthreadingimportrequestsrequests.packages.urllib3.disable_warnings()urls_list=["http://127.0.0.1:5000"for_inrange(1,101)]response_ok=0response_error=0defget_response_code(url):globalresponse_errorglobalr......
  • 多线程 —— 线程的六种状态
    一、线程的六种状态,如下图所示二、六种状态介绍:线程状态具体含义NEW一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThreadt=newMyThread()只有线程对象,没有线程特征。RUNNABLE当我们调用线程对象的s......
  • 多线程的创建和使用
    多线程点击标题旁可查看目录一.基本概念1.线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小......