线程进程总结

1、什么是进程?什么是线程?线程与进程之间的关系?

        进程(Processor):活动的程序,已经启动的程序,进驻到内存中,正在使用的程序。

        线程(Thread):线程是进程中最小的调度的单元(单位),cpu控制的最小的执行单元。属于轻量级的进程。任何一个程序都至少有一个线程在用,多个线程共享内存。多线程切换消耗的资源少。

        线程是进程中最小的单元,举个例子:进程是工厂的厂房,那么线程就是厂房中的流水线。

        线程是进程中最小的调度单元,线程必须存在于进程中。

        进程是有自己的独立的内存空间。

        多线程提高开发效率,多线程共享进程中的内存空间。

        线程是由CPU进行控制调度,线程执行需要抢到CPU的时间片。

        线程可以并发执行可以并行执行。

2、什么是并行,什么是并发?两者之间的区别

        并发:在同一时间间隔内,同时有多个线程运行。一个CPU在一段时间内可以操作多个线程,但是在同一时刻也是操作一个线程。

        并行:在同一时刻,同时有多个线程运行。一个CPU在同一时刻只能操作一个线程。

        并发和并行的联系和区别:并行合并发原则上是相同的,只是并发在时间宏观上上看好像是可以同时操作多个线程,并行是从时间微观角度上观察的。

3、如何创建线程?创建线程的方法?

        3.1、继承Thread类(详细:重写run方法,创建线程对象,启动线程(start()) )

                继承Thread:简单,受限于java单继承;

        3.2、实现Runnable接口(实现接口,重写run方法)

                实现Runnable接口:可以实现多个接口,实现资源的共享;推荐优先使用;

        3.3、实现Callable接口(可以获取线程的返回值)

4、列举一些Thread类中的常用API

        Thread currentThread() 返回当前的线程

        String getName()/setName() 获取和设置线程名

        setPriority(int priority)/getPriority(int priority) 设置和获取当前线程的优先级

        start() 开始执行线程

        sleep(long m) 线程休眠 (默认单位是毫秒)会让当前线程处于阻塞状态,指定时间过后,线程就绪状态 。

        yield() 线程礼让 暂停目前的线程,运行其他线程

        join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态

        run() 当前线程要执行的任务

注:线程的run()和start()有什么区别?

        run() 不会启动线程,只是普通的调用方法而已。不会分配新的分支栈(这种方式就是单线程。)

        start() 方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。

5、线程有哪几种状态?

 

        1、新建状态(New):新创建了一个线程对象。

        2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

        3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

        4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

        (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

        (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

        (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

        5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

6、多线程存在的数据安全问题?

         6.1、数据安全问题的条件:

                条件1:多线程并发。

                条件2:有共享数据。

                条件3:共享数据有修改的行为。

        6.2、如何解决线程安全问题

        第一种方案:尽量使用局部变量代替“实例变量和静态变量”。

        第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样 实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象, 对象不共享,就没有数据安全问题了。)

         第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候 就只能选择synchronized了。线程同步机制。

7、什么是线程同步?如何实现?

         7.1、线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。那相对的线程异步处理就不用阻塞当前线程,而是允许后续操作,直至其他线程将处理完成,并回调此线程。

        线程同步的利弊  好处:解决了线程同步的数据安全问题。   弊端:当线程很多的时候,每个线程都会去判断同步上面的这个锁,很耗费资源,降低效率。

        7.2、实现同步的方式:

        1、基于synchornized(同步)实现

        同步语句块: synchronized(this){方法体}(synchronized括号后的数据必须是多线程共享的数据,才能达到多线程排队)

        普通同步方法:修饰符 synchronized 返回值类型 方法名(形参列表){方法体}

        静态同步方法:修饰符synchronized static 返回值类型 方法名(形参列表){方法体}

        2、基于Lock实现   

        Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应鲜活的Lock对象。

        ReentrantLock类实现了Lock,它拥有和synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。

8、synchronized 和Lock有什么区别?

        Lock是显示锁,需要自己手动开启和关闭,synchronized是隐式锁,出了作用于自动释放,无需自己释放。

        Lock只能锁代码块,synchronized可以锁代码块和方法。 使用Lock锁,JVM将花费更少的时间来调度线程,性能更好。

9、sleep方法和wait方法的区别?

        1、sleep是Thread类种的方法,wait是Object中的方法。

        2、wait必须用在同步代码中,释放锁,让当前线程处于等待状态;sleep不会解锁;

        native:本地方法,内部;该方法的实现并不是用java语言实现的,可能是系统底层C语言已经实现好的方法,java程序仅仅是调用。

10、什么是线程池?为什么要使用线程池?如何使用线程池?

        线程池就是首先创建一些线程,他们的集合称之为线程池。线程池在系统启动时会创建大量空闲线程,程序将一个任务传递给线程池,线程池就会启动一条线程来执行这个任务,执行结束后线程不会销毁(死亡),而是再次返回到线程池中成为空闲状态,等待执行下一个任务。

        线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后再需要执行新的任务时重用这些线程而不是新建线程

        10.1、 为什么要使用线程池

        多线程运行时,系统不断创建和销毁新的线程,成本非常高,会过度的消耗系统资源,从而可能导致系统资源崩溃,使用线程池就是最好的选择。

        10.2、创建线程池所必须要的参数

        1、corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。

                设置规则: CPU密集型(CPU密集型也叫计算密集型,指的是运算较多,cpu占用高,读/写I/O(硬盘/内存)较少):corePoolSize = CPU核数 + 1

                IO密集型(与cpu密集型相反,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。):corePoolSize = CPU核数 * 2

        2、maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。默认为Integer.MAX_VALUE,一般设置为和核心线程数一样

        3、keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。线程空闲时间,默认为60s,一般设置为默认60s

        4、unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

        5、workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。

        6、threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。

        7、handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。默认是AbortPolicy,丢弃任务并抛出 RejectedExecutionException 异常。

10.3、通过Executor工厂类中的静态方法获取线程池对象

        1、通过newCachedThreadPool获取线程池对象。该方式特点是:创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建。

        2、通过newFixedThreadPool获取线程池对象。该方式特点是:可指定创建线程数,并且可以重复用。

        3、通过newSingleThreadExecutor获取线程池对象。该方式特点是:只会创建一个线程。

10.4、常见的任务队列(workQueue)

        任务队列是基于阻塞队列实现的,即采用生产者消费者模式,在 Java 中需要实现 BlockingQueue 接口。但 Java 已经为我们提供了 7 种阻塞队列的实现:

        ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。

        LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为 Integer.MAX_VALUE。

        PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现 Comparable 接口也可以提供 Comparator 来对队列中的元素进行比较。跟时间没有任何关系,仅仅是按照优先级取任务。

        DelayQueue:类似于PriorityBlockingQueue,是二叉堆实现的无界优先级阻塞队列。要求元素都实现 Delayed 接口,通过执行时延从队列中提取任务,时间没到任务取不出来。

        SynchronousQueue: 一个不存储元素的阻塞队列,消费者线程调用 take() 方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用 put() 方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。

        LinkedBlockingDeque: 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样 FIFO(先进先出),也可以像栈一样 FILO(先进后出)。

        LinkedTransferQueue: 它是ConcurrentLinkedQueue、LinkedBlockingQueue 和 SynchronousQueue 的结合体,但是把它用在 ThreadPoolExecutor 中,和 LinkedBlockingQueue 行为一致,但是是无界的阻塞队列。

10.5、关闭线程方法:shutdown和shutdownNow

    shutdown():仅仅是不再接受新的任务,以前的任务还会继续执行

    shutdownNow():立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些任务。

11、三种创建线程池有什么区别?

        第一种:newCachedThreadPool:线程的数据是不做限制的,每次有任务来的时候都会以任务优先,性能最大化(也就是服务器压力比较大)

        第二种:newFixedThreadPool:可以让压力不那么大,并且可以规定线程的数量,当线程的数量达到指定数量的时候,这个时候就不会再有新的线程了

        第三种:newSingleThreadExecutor:绝对的安全,不考虑性能,因为是单线程,永远只有一个线程来执行任务。
 


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值