线程相关基础概念

并发与并行

需要通过不断切换需要运行的线程让其运行的方式叫做并发。
在多cpu系统中,可以让两个以上的线程同时运行,这种方式叫做并行

线程与进程

每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。一个进程包含1-n个线程
同意类线程共享代码和数据空间。每个线程有独立的运行栈和程序计数器,线程的切换开销小。

多进程是指操作系统可同时运行多个任务(程序)
多线程是指同一程序中有多个顺序流在执行

进程

一个进程对一个程序,每个进程都有一定的内存地址空间,并且只能使用它自己的内存空间,各个进程间互不干扰。并且进程保存了程序每个时刻的运行状态,这样就为进程切换提供了可能。当进程暂停时,他会保存当前进程的状态,在下一次重新切换回来时,便根据保存的状态进行回复,然后继续执行。从而实现并发。

线程

让一个线程去执行一个子任务。也就是说,一个进程包含多个线程,每个线程负责一个独立的子任务。也就是说,进程让并行成为可能,线程让内部并发成为可能。

注意:
一个进程虽然包含多个线程,但是这些线程共享进程战友的资源和地址空间。
进程是操作系统进行资源分配的最小单位,而线程是操作系统进行资源调度的最小单位。
由于多个线程是共同占有所属进程的资源和地址空间的,那么就会出现多个线程同时访问某个资源的同步问题,这就是线程同步问题。

线程和进程的区别

a.地址空间和其他资源: 进程间相互独立,同一进程的个县城间共享。能够进程内的线程在其他进程中不可见。
b.通信: 进程间通信,线程间可直接读写进程数据段来进行通信----需要进程同步和互斥的手段的辅助,以保证数据的一致性。
c.调度和切换: 线程上下文切换要比进程上下文切换快
d,在多线程操作系统中,进程不是一个可执行的实体。

线程对象和线程

线程对象时jvm生成的一个Object子类。线程是cpu分配给这个对象的一个运行过后才能。

线程的实现方式

实现多线程的方式主要有四种:继承Thread类,实现Runnable接口,实现Callable接口通过FutureTask包装器来创建Thread线程,使用ExecutorService、Callable、Future实现有结果的多线程。其中前两种方式线程执行完后没有返回值,后两种方式存在返回值

1. 继承Thread类创建线程

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方式就是调用Thread类的start方法。start()是一个native方法,它将启动一个线程,并执行run()。这种实现方式通过继承Thread类,并重写run()。

public class My extends Thread{
    
    public void run(){
        System.out.println("aaaaa");
    }
}

My my = new My();
my.start

2. 实现Runnable接口创建多线程

实现Runnable接口,解决了Java语言单继承的问题。
可以达到资源共享的效果。

public class My extends SuperClass implements Runnable{
    public void run(){
        System.out.print("sadada");
    }
}

My my = new My();
Thread thread = new Thread(my);
thread.start();

//事实上,在给Thread传入Runnable target参数之后,Thread的run(),会调用target.run()
public void run(){
    if(target!=null){
        target.run();
    }
}
3. 实现Callable接口通过FutureTask包装器创建Thread线程
//Callable接口源码,只有一个方法
public interface Callable<V>{
    V call() throws Exception;    
}

//实例类
public class SomeCallable<V> extends OtherClass implements Callable<V>{
    @Override
    public V call() throws Exceptin{
        System.out.println();
        return null;
    }
}

//创建并运行
Callable<V> calllable = new SomeCallable<V>();
//使用Callable<V>创建一个FutureTask<V>对象
FutureTask<V> task = new FutureTask<V>(callable);
//FutureTask<V>是一个包装类,它通过接受Callable<V>来创建,它同时实现了Future和Runnable接口
//根据Future创建一个Thread对象
Thread thread  = new Thread(task);
thread.start();

4. 使用ExecutorService、Callable、Future创建有返回结果的多线程

ExecutorService、Callable、Future三个接口实际都是属于Executor框架。可返回值得任务必须实现Callable接口。无需返回值的的任务必须实现Runnable接口。执行Callable任务后获取一个Futrue的对象,在该对象上调用get就可以获取Callable任务返回的Object了。
get()方法是阻塞的,也就是说线程无返回结果的时候,get()会一直阻塞等待。
再结合线程池接口ExecutorService就可以实现有返回值的多线程

public class Test{
    public static void main(String []aaa) throws Exception{
            Date date = new Date();
            int taskSize = 5;
            //创建一个线程池
            ExecutorService pool = Executors.new FixedThreadPool(taskSize);
            //创建多个有返回值的任务
            List<Future> list = new ArrayList<Future>();
            for(int a = 1 ;a < taskSize;a++){
                Callable c = new MyCallable(i + "");
                //执行任务并获取Future对象
                Future f = pool.submit(c);
                list.add(f);
            }
            //关闭线程池
            pool.shutdown();
            for(Future future : list){
                System.out.print(f.get().toString);
            }
    }
}
class myCallable implement Callable<Object>{
    private String taskNum;
    MyCallable(String num){
        this.taskNum = num;
    }
    
    @Override
    public Object call() throws Exception{
        Thread.sleep(1000);
        return null
    }
    
}

上面的代码中的Executors类,提供了一系列的工厂方法用于创建多线程,返回的线程是也都实现了ExecutorService接口。

//创建固定数目线程的线程池
public static ExecutorService newFixedThreadPool(int nThreads);
//创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用,则创建一个新线程并添加到池中。终止并从缓存中一处那些已有60s未被使用的线程。
public static ExecutorServie newCachedThreadPool();
//创建一个单线程化得Executor
public static ExecutorService newSingleThreadExecutor();
//创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来代替Timer类
public static ScheduledExecutorServie newScheduledThreadPool(int corePoolSize);

ExecutorServie提供了submit()方法,传递一个Callable或Runnable,返回Futrue。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,知道阻塞计算完成。

Thread和Runnable的区别和联系

Thread创建的线程对象只能被start()一次。

联系

Thread实现了Runnable接口
都需要重写run方法

runnable的优势
  1. 实现runnable接口,适合多个相同程序代码去处理同一资源,把虚拟cpu(线程)同程序的代码,数据有效的分离。
  2. 避免Java单继承特性带来的问题。
  3. 代码可以被多个线程共享,代码和数据都是独立的。当多个线程执行的代码来自同一个类的实例时,就称他们为共享相同的代码。多个线程操作相同的数据,与其代码无关。
runnable实现资源共享
public static void main(String []asdsad){
    My m = new My();
    //两个Thread对象,包装同一个My线程对象,最终的结果只是打印一遍 5 4 3 2 1 0
    new Thread(m).start();
    new Thread(m).start();
    
}

class MyThread implements Runnable{
    private int aa = 5;
    public void run(){
        while(true){
            System.out.print("dasdad" + aa--);
            if(aa<0){
                break;
            }
            
        }
    }
}


//Thread实现
public static void main(String [] aa){
    //创建了两个没有任何关系的线程对象并启动,结果会将 5 4 3 2 1 0   打印两遍
    new My().start();
    new My().start();
    
    
}
class My extends Thread{
    private int aa = 5;
    @Override
    public void run(){
        while(true){
            sout("sadada"+ aa--);
            if(aa<0)
                break;                
        }
    }
    
}


多次start同一个线程会怎么样

同一个线程只能调用start()方法一次,第二次开始跑出IllegalThreadStateException。启动一个线程,需要调用start(),此时,当前线程会被添加到线程组中,进入就绪状态,等待线程调度器的调用,若获取到了资源,则可进入运行状态。

CountDownLatch、CyclicBarrier、Semaphore

CountDownLatch CyclicBarrier两个类似。都可以作为一个计数器,当达到指定数量的线程执行到某一行代码才能继续执行,否则在此处阻塞等待。
CountDownLatch常用方法  countDown()  将CountDownLatch中的参数减一,有一个线程执行到此处则便进行减一操作。当执行到await()时,阻塞等待。当参数减小到零时,所有阻塞的线程,再次开始抢锁,执行剩下的代码。
CyclicBarrier常用方法  await()  当线程执行到此处时,参数减一并进行阻塞操作。

Semaphore 信号量,可以指定同时最多有多少个线程并行执行。availablePermits()获取当前允许并行执行的最大线程数量。其返回值大于一的时候,证明有可用的线程执行位置。调用acquire()方法,获取位置,同时会将其参数减一,之后执行需要执行的代码。执行完毕之后,在finally模块中,调用release()方法,将之前获取的执行权限交回,同时,参数加一。

线程的状态

新建 new
新建一个线程对象   new Thread()
可运行状态 runnable
线程对象创建后,其他线程调用了该对象的start()方法,该线程就进入了可运行线程池中,等待被线程调用选中,获取CPU的使用权
运行 running
可运行状态的线程获取到CPU时间片之后,执行程序代码
阻塞 block
阻塞状态是指线程因为某种原因放弃CPU使用权,暂时停止执行。直到线程进入可运行状态,才有机会再次会的CPU时间片转到运行状态。
阻塞分为三种:
    等待阻塞:运行的线程执行wait()方法。JVM会把该线程放入等待队列中。
    同步阻塞:运行的线程在获取对象的同不锁的时候,如果该同步锁被别的线程占用,则jvm会将该线程放入锁池中
    其他阻塞:运行的线程执行Thread.sleep(long s) 或者t2.join()方法,或者发出I/O请求时,jvm会将该线程置为阻塞状态。当sleep状态超时,join等待线程终止或超时,i/o处理完毕时,线程重新进入可运行状态。
死亡 dead
线程run方法执行完毕,或者因为异常退出run方法,则该线程生命周期结束。死亡的线程不可再生

常见的线程池

线程之间的区别和联系

线程池的实现原理

多线程同步,锁

synchronized和ReentrantLock的区别

synchronized锁普通方法和静态方法

死锁的原理和排查方法

用户线程和守护线程

用户线程用来处理业务逻辑,守护线程主要用来进行一些监控等操作

GC线程

常见面试题

1.假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?
,java.util.concurrent

线程组ThreadGroup

ThreadGroup实现了Thread.UncaughtExceptionHandler接口,所以可以extends定义自己的线程组,并重写异常处理方法。以此来达到同一类型线程的分组和统一的线程异常捕获处理。

start源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值