首页 > 其他分享 >高并发 (线程)

高并发 (线程)

时间:2024-04-16 17:36:25浏览次数:19  
标签:状态 调用 Thread 并发 线程 方法 public

我们都知道现在硬件水平越来越强,执行速度也越来越快。为充分利用CPU资源,我们可以通过多线程来执行分片任务提升整体的执行性能。

如何创建线程

在Java中通过new Thread().start()方法来开启一个线程,实际是调用系统层的native方法

private native void start0();

Java使用线程常用方式有四种:

1、继承Thread类,Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start()实例方法

2、实现Runnable接口,如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个Runnable接口

3、实现Callable接口,重写call()方法,可以返回一个 Future类型的返回值。

4、通过线程池来使用线程,将任务交给线程池执行

public class CreateThreadDemo {

	public static void main(String[] args){
		// 单继承
        new ThreadDemo().start();
        
        // 实现runnable
        new Thread(new RunnableDemo()).start();
        
        // futureTask获取返回值
        FutureTask futureTask = new FutureTask(new CallableDemo());
        new Thread(futureTask).start();
        //get()方法获取返回值
        System.out.println(futureTask.get());
	}
	
    // 缺点:单继承
    public static class ThreadDemo extends Thread {
        @Override
        public void run() {
            System.out.println("ThreadDemo:" + Thread.currentThread());
        }
    }
    
	// 不用担心单继承,但没有返回值
	public static class RunnableDemo implements Runnable {

		@Override
		public void run() {
			System.out.println("RunnableDemo:" + Thread.currentThread());
		}
	}
    
    // 
    public static class CallableDemo implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println("CallableDemo:" + Thread.currentThread());
            return "return CallableDemo:" + Thread.currentThread();
        }
    }
}

本质上都是调用Thread类的start0()方法来启用

线程的生命周期

1、新建状态(NEW) 当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配 内存,并初始化其成员变量的值

2、就绪状态(RUNNABLE):当线程对象调用了 start()方法之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和 程序计数器,等待调度运行。

3、运行状态(RUNNING):如果处于就绪状态的线程获得了CPU,开始执行 run()方法的线程执行体,则该线程处于运行状态。

4、阻塞状态(BLOCKED):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。 直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状 态。
阻塞的情况分三种: 等待阻塞(o.wait->等待对列): 运行(running)的线程执行 o.wait()方法,JVM 会把该线程放入等待队列(waitting queue) 中。 同步阻塞(lock->锁池) 运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线 程放入锁池(lock pool)中。 其他阻塞(sleep/join) 运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时, JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入可运行(runnable)状态。

5、线程死亡(DEAD) 线程会以下面三种方式结束,结束后就是死亡状态。
正常结束 1. run()或 call()方法执行完成,线程正常结束。 异常结束 2. 线程抛出一个未捕获的 Exception 或 Error。 调用 stop 3. 直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用

线程的6种状态

1、初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

2、运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

3、阻塞(BLOCKED):表示线程阻塞于锁。

4、等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

5、超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

6、终止(TERMINATED):表示该线程已经执行完毕。

如何关闭线程

1、设置退出标志,使线程正常退出

public class ThreadSafe extends Thread {
    public volatile boolean exit = false;
    public void run() {
        while (!exit){
            //do something
        }
    }
}

定义了一个退出标志,在定义exit时,使用了一个 Java 关键字 volatile,这个关键字的目的是使 exit 同步,也就是说在同一时刻只能由一个线程来修改 exit 的值。

2、使用interrupt()方法中断线程,在线程中使用isInterrupted()来判断是否存在中断标志

  • 线程处于阻塞状态:如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时, 会使线程处于阻塞状态。当调用线程的 interrupt()方法时,会抛出 InterruptException 异常。 阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后 break 跳出循环状态,从而让 我们有机会结束这个线程的执行。通常很多人认为只要调用 interrupt 方法线程就会结束,实际上是错的,一定要先捕获 InterruptedException 异常之后通过break来跳出循环,才能正常结束run方法。

  • 线程未处于阻塞状态:使用isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。

public class ThreadSafe extends Thread {
    public void run() {
        while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出
            try{
                Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出
            }catch(InterruptedException e){
                e.printStackTrace();
                break;//捕获到异常之后,执行 break 跳出循环
            }
        }
    }
}

3、使用stop方法强行终止线程

程序中可以直接使用 thread.stop()来强行终止线程,安全主要是:thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeatherror的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误

sleep与wait区别

1、对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于 Object 类中的

2、sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然 保持者,当指定的时间到了又会自动恢复运行状态。

3、在调用 sleep()方法的过程中,线程不会释放对象锁。

4、而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此 对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

守护线程

1、守护线程--也称“服务线程”,他是后台线程,它有一个特性,即为用户线程提供公共服务,在没有用户线程可服务时会自动离开。
2、守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
3、通过 setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程 的方式是在 线程对象创建 之前 用线程对象的 setDaemon 方法。
4、在Daemon线程中产生的新线程也是Daemon的。
4、生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周 期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出

标签:状态,调用,Thread,并发,线程,方法,public
From: https://www.cnblogs.com/luojw/p/18138753

相关文章

  • netcore 并发锁 多线程中使用SemaphoreSlim
    SemaphoreSlim是一个用于同步和限制并发访问的类,和它类似的还有Semaphore,只是SemaphoreSlim更加的轻量、高效、好用。今天说说它,以及如何使用,在什么时候去使用,使用它将会带来什么优势。代码的业务是:在多线程下进行数据的统计工作,简单点的说就是累加数据。1.首先我们建立一个程......
  • thread(线程)
    动机一个应用通常需要处理很多工作,这些同时执行徳任务可以称为“执行流”,我们不希望他们是顺序执行的。进程的创建需要消耗大量的时间和资源现在,和一个应用相关的所有执行的任务都装在一个进程里面,这些进程内部的执行任务就是“线程”threadmultithreadedprocess(多线程......
  • 线程(不严谨)
    引子(关于进程)执行完fork()之后创建了一个子进程clone父进程pid是不一样fork()后面的代码,会执行2遍(父和子进程各执行1次)父子进程并发执行父子进程的内存空间是独立的wait()父进程等待子进程结束避免孤儿进程线程(Thread)多线程:很简陋的方式来说,在一个进程......
  • Docker安装部署Jenkins并发布NetCore应用
    Docker安装Jenkins#拉取镜像dockerpulljenkins/jenkins#查看镜像dockerimages#运行jenkins#8080端口为jenkinsWeb界面的默认端口13152是映射到外部:前面的是映射外部#50000端口为jenkins的默认代理节点(Agent)通信端口13153是映射到外部#--restart=on-fa......
  • 多线程整理
    一、简介    1.1、进程        当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。一个进程是由多个线程组成。    1.2、线程        线程是程序中的一个执行流,每个线程都有自己的专有寄......
  • Java并发编程实战读书笔记
    1.线程池模型    netty实战中讲到的线程池模型可以描述为:1.从线程池中选择一个空间的线程去执行任务,2.任务完成时,把线程归还给线程池。这个模型与连接池类似。    根据jdk源码的研究,具体的实现模型是,线程池ThreadPoolExecutor中有一个静态内部类Worker,使用装饰器模式扩......
  • 10.线程
    第十章【线程】一、进程和线程1、进程:代表了内存中正在运行的应用程序,计算机中的资源(cpu内存磁盘网络等),会按照需求分配给每个进程,从而这个进程对应的应用程序就可以使用这些资源了。进程就是在系统中运行一个应用程序的基本单位。2、线程:是进程中的一个代码执行单元,负责当......
  • 深入理解并发和并行
    深入理解并发和并行1并发与并行为什么操作系统上可以同时运行多个程序而用户感觉不出来?因为操作系统营造出了可以同时运行多个程序的假象,通过调度进程以及快速切换CPU上下文,每个进程执行一会就停下来,切换到下个被调度到的进程上,这种切换速度非常快,人无法感知到,从而产生了多个任......
  • 高并发场景QPS等专业指标揭秘大全与调优实战
    高并发场景QPS等专业指标揭秘大全与调优实战最近经常有小伙伴问及高并发场景下QPS的一些问题,特意结合项目经验和网上技术贴做了一些整理和归纳,供大家参考交流。一、一直再说高并发,多少QPS才算高并发?高并发的四个角度只说并发不提高可用就是耍流氓。可以从四个角度讨论这个问......
  • 通用的上传下载(线程)
    packagecom.duxiang.backgroundmanagement.controller;importcn.hutool.core.io.FileUtil;importcn.hutool.core.util.StrUtil;importcom.duxiang.backgroundmanagement.common.Result;importorg.springframework.web.bind.annotation.*;importorg.springframework.web.......