线程常用知识问答

Posted zghw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程常用知识问答相关的知识,希望对你有一定的参考价值。

1.什么是线程?
  线程是操作系统能够运行调度的最小单位,它被包含在进程之中。
  线程可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能。
  线程可以将大部分的异步工作流转换成串行工作流。
  线程可以降低代码的复杂度,是代码更容易编写、阅读和维护。
  线程是一把双刃剑。
  编写多线程对开发人员要求高,线程安全不易控制.线程会遇到难以分析的问题,如:死锁、饥饿。
  线程同步和切换会带来性能的问题。
   
  2.线程和进程有什么区别?
  线程是进程的子集,一个进程可以有很多线程,每条线程并行执行任务。
  不同的进程使用不同的内存空间,而同一进程中的所有的线程都将共享进程的内存地址空间。
   
  3.如何在java中实现线程?
  三种实现
  1.继承Thread
  2.实现Runnable接口
  3.使用Executor创建
  建议使用Exectutor来实现线程,它为灵活且强大的异步任务执行框架提供了基础,该框架能支持多种不同类型的任务执行策略。
  它提供了一种标准的方法将任务的提交过程与执行过程解耦开来,并用Runnable来表示任务。Executor的实现还提供了对生命
  周期的支持以及统计信息收集、应用程序管理机制和性能监视等机制。
   
  4.Thread类中的start()方法和run()方法有什么区别?
  调用start()方法是启动一个新的线程,然后start()方法内部调用了run()方法。
  run()方法只是一个普通的方法,直接调用run()不会启动一个新的线程。
   
  5.Runnable和Callable有什么不同?
  Runnable和Callable都代表那些要在不同的线程中执行的任务。
  主要区别在于:Callable()的call()方法可以返回装载有计算结果的Future对象和抛出异常,而Runnable不可以。
   
  6.volatile变量
  Java语言提供了一种稍弱的同步机制,即 volatile 变量.用来确保将变量的更新操作通知到其他线程,保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新.
  当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的.提供了线程的可见性。(想象成get()和set())
  volatile只能确保可见性,不能确保原子性。
  通常用在某个操作完成、发生中断或者状态的标志。(个人认为常作用在 boolean 枚举 常量)
   
  7.线程安全
  当多个线程访问某个类时,不管这些程序将如何交替执行,都能表现正确的行为,则代表线程安全。
  要保证线程安全,外修排斥,内修可见。
   
  8.竞态条件
  竞态条件称为“先检查后执行”:首先观察到某个条件为真,然后根据这个观察结果采用相应的动作,但事实上,
  在你观察到这个结果以及开始创建文件之间,观察结果可能变得无效了,从而导致各种问题。
  所以需要保证复合操作的原子性,使用synchronized或原子变量类。
   
  9.如何停止线程?
  如果使用Executor创建的线程直接shutdown就好了
  使用Thread停止线程
  1. 使用violate boolean变量来标识线程是否停止
  2. 停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性
  private volatile Thread myThread;
  public void stopMyThread()
  Thread tmpThread = myThread;
  myThread = null;
  if (tmpThread != null)
  tmpThread.interrupt();
 
 
  public void run()
  if (myThread == null)
  return; // stopped before started.
 
  // do some more work
 
  10.在java中wait和sleep方法的不同?
  java 线程中的sleep和wait有一个共同作用,停止当前线程任务运行,不同之处在于:
  wait是Object的方法,sleep是Thread的方法
  最主要的wait方法进入等待后,会释放对象锁。而sleep方法在同步块内等待,不会释放锁对象。
  wait方法必须在同步方法或者同步块中使用,sleep方法可以在很多地方使用。
  sleep必须捕获异常,wait不需要
   
  11.什么是不可变对象,它对写并发应用有什么帮助?
  不可变的对象指的是一旦创建之后,它的状态就不能改变。String类就是个不可变类,它的对象一旦创建之后,值就不能被改变了。
  不可变对象对于缓存是非常好的选择,因为你不需要担心它的值会被更改。
  不可变类的另外一个好处是它自身是线程安全的,你不需要考虑多线程环境下的线程安全问题。
  要创建不可变类,要实现下面几个步骤:
  1.将类声明为final,所以它不能被继承
  2.将所有的成员声明为私有的,这样就不允许直接访问这些成员
  3.对变量不要提供setter方法
  4.将所有可变的成员声明为final,这样只能对它们赋值一次
  5.通过构造器初始化所有成员,进行深拷贝(deep copy)
  6.在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝
   
  12.在Java中什么是线程调度?
  JVM调度的模式有两种:分时调度和抢占式调度。
  分时调度是所有线程轮流获得CPU使用权,并平均分配每个线程占用CPU的时间;
  抢占式调度是根据线程的优先级别来获取CPU的使用权。JVM的线程调度模式采用了抢占式模式。
  既然是抢占调度,那么我们就能通过设置优先级来“有限”的控制线程的运行顺序,注意“有限”一次。
   
  14.ThreadLocal
  ThreadLocal类用来提供线程内部的局部变量。
  这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。
  ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程的上下文。
  ThreadLocal对象通常用于防止对可变的单实例变量(Singleton)或全局变量进行共享。
   
  15.BlockingQueue
  BlockingQueue
  线程安全的队列集合。具有阻塞功能。
  提供了可阻塞的put和take方法,以及支持定义的offer和poll方法。
  简化了生产者-消费者设计的实现过程,它支持任意数量的生产者和消费者。
  一种常见的生产者-消费者设计模式就是线程池与工作队列的组合。
   
  以两个人包饺子为例,两者的劳动分工也是一种生产者-消费者模式:其中一个人把做好的面皮放在案板上,而另一个人从案板上拿走面皮并包饺子。
  这个实例中,面板相当于阻塞。如果案板上没有面皮,那么消费者会一直等待,直到有面皮。
  如果案板上放满了,那么生产者会停止做面皮,直到盘加上有更多的空间。我们可以将这种类比扩展为多个生产者和多个消费者,每个人只需和案板打交道。
  人们不需要直到究竟有多少生产者或者消费者,或者谁生产了指定的工作项。
   
  16.ConcurrentHashMap
  同步容器类在执行每个操作期间都持有一个锁,在大量工作的HashMap.get或List.contains如果hashcode分布糟糕,就会导致大量同步集中访问某一处,
  导致其他线程不能访问该容器。
  ConcurrentHashMap 并不是将每个方法都在同一个锁上同步并使得每次只有一个线程访问容器,而是使用一种力度更细的加锁机制来实现更大程度的共享。
  这种机制成为分段锁,在这种机制中,任意数量的读取线程都可以并发地访问Map,执行读取操作地线程和执行写入操作地线程可以并发地访问Map,并且一定数量地写入
  襄城可以并发地修改Map。
  ConcurrentHashMap带来地结果是,在并发访问环境下将实现更高地吞吐量,而在单线程环境中只损失非常小地性能。
  分段锁机制:
  在ConcurrentHashMap的实现中使用了一个包含16个锁的数组,每个锁保护所有散列桶的1/16,其中第N个散列桶有第(N%16)个锁来保护。
  假设散列函数具有合理的分布性,并且关键字能够实现均匀分布,那么这大约能把对于锁的请求减少到原来的1/16。
  正是这项技术使得ConcurrentHashMap能够支持多大16个并发的写入器。
   
  17.如何在两个线程间共享数据?
  多个线程访问共享对象和数据的方式
  1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。
  2.如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
  3.将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,
  这样容易实现针对该数据进行的各个操作的互斥和通信。
  4.将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
  5.上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
  6.总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。
  7.极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。
  18.Java中notify 和 notifyAll有什么区别?
  wait导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或被其他线程中断。wait只能由持有对像锁的线程来调用。
  notify唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程(随机)。
  直到当前的线程放弃此对象上的锁,才能继续执行被唤醒的线程。
  同Wait方法一样,notify只能由持有对像锁的线程来调用.notifyall也一样,不同的是notifyall会唤配所有在此对象锁上等待的线程。
  "只能由持有对像锁的线程来调用"说明wait方法与notify方法必须在同步块内执行,即synchronized(obj)之内.
  再者synchronized代码块内没有锁是寸步不行的,所以线程要继续执行必须获得锁。相辅相成
   
  19.为什么wait, notify 和 notifyAll这些方法不在thread类里面?
  因为这些是关于锁的,而锁是针对对象的,锁用于线程的同步应用,决定当前对象的锁的方法就应该在对象中
   
  20. Java中interrupted 和 isInterruptedd方法的区别?
  1.interrupt()是用来设置中断状态的。返回true说明中断状态被设置了而不是被清除了。我们调用sleep、wait等此类可中断
  (throw InterruptedException)方法时,一旦方法抛出InterruptedException,当前调用该方法的线程的中断状态就会被jvm自动清除了,
  就是说我们调用该线程的isInterrupted 方法时是返回false。如果你想保持中断状态,可以再次调用interrupt方法设置中断状态。
  这样做的原因是,java的中断并不是真正的中断线程

以上是关于线程常用知识问答的主要内容,如果未能解决你的问题,请参考以下文章

线程常用知识问答

多线程常用的一些知识点

初识多线程之基础知识与常用方法

C++11常用知识点(下)

jmeter接口常用知识

jmeter接口常用知识

(c)2006-2024 SYSTEM All Rights Reserved IT常识