归纳整理Java并发知识点 您所在的位置:网站首页 挤出水的痘痘是怎么回事 归纳整理Java并发知识点

归纳整理Java并发知识点

#归纳整理Java并发知识点| 来源: 网络整理| 查看: 265

本篇文章给大家带来了关于java的相关知识,其中主要介绍了java并发的相关问题,总结了一些问题,大家来看一下会多少,希望对大家有帮助。

推荐学习:《java教程》

1.并行跟并发有什么区别?

从操作系统的角度来看,线程是CPU分配的最小单位。

并行就是同一时刻,两个线程都在执行。这就要求有两个CPU去分别执行两个线程。 并发就是同一时刻,只有一个执行,但是一个时间段内,两个线程都执行了。并发的实现依赖于CPU切换线程,因为切换的时间特别短,所以基本对于用户是无感知的。

就好像我们去食堂打饭,并行就是我们在多个窗口排队,几个阿姨同时打菜;并发就是我们挤在一个窗口,阿姨给这个打一勺,又手忙脚乱地给那个打一勺。

2.说说什么是进程和线程?

要说线程,必须得先说说进程。

进程:进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。 线程:线程是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。

操作系统在分配资源时是把资源分配给进程的, 但是 CPU 资源比较特殊,它是被分配到线程的,因为真正要占用CPU运行的是线程,所以也说线程是 CPU分配的基本单位。

比如在Java中,当我们启动 main 函数其实就启动了一个JVM进程,而 main 函数在的线程就是这个进程中的一个线程,也称主线程。

一个进程中有多个线程,多个线程共用进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈。

3.说说线程有几种创建方式?

Java中创建线程主要有三种方式,分别为继承Thread类、实现Runnable接口、实现Callable接口。

继承Thread类,重写run()方法,调用start()方法启动线程 public class ThreadTest { /** * 继承Thread类 */ public static class MyThread extends Thread { @Override public void run() { System.out.println("This is child thread"); } } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); }} 实现 Runnable 接口,重写run()方法 public class RunnableTask implements Runnable { public void run() { System.out.println("Runnable!"); } public static void main(String[] args) { RunnableTask task = new RunnableTask(); new Thread(task).start(); }}

上面两种都是没有返回值的,但是如果我们需要获取线程的执行结果,该怎么办呢?

实现Callable接口,重写call()方法,这种方式可以通过FutureTask获取任务执行的返回值 public class CallerTask implements Callable { public String call() throws Exception { return "Hello,i am running!"; } public static void main(String[] args) { //创建异步任务 FutureTask task=new FutureTask(new CallerTask()); //启动线程 new Thread(task).start(); try { //等待执行完成,并获取返回结果 String result=task.get(); System.out.println(result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }} 4.为什么调用start()方法时会执行run()方法,那怎么不直接调用run()方法?

JVM执行start方法,会先创建一条线程,由创建出来的新线程去执行thread的run方法,这才起到多线程的效果。

**为什么我们不能直接调用run()方法?**也很清楚, 如果直接调用Thread的run()方法,那么run方法还是运行在主线程中,相当于顺序执行,就起不到多线程的效果。

5.线程有哪些常用的调度方法?

线程等待与通知

在Object类中有一些函数可以用于线程的等待与通知。

wait():当一个线程A调用一个共享变量的 wait()方法时, 线程A会被阻塞挂起, 发生下面几种情况才会返回 :

(1) 线程A调用了共享对象 notify()或者 notifyAll()方法;

(2)其他线程调用了线程A的 interrupt() 方法,线程A抛出InterruptedException异常返回。

wait(long timeout) :这个方法相比 wait() 方法多了一个超时参数,它的不同之处在于,如果线程A调用共享对象的wait(long timeout)方法后,没有在指定的 timeout ms时间内被其它线程唤醒,那么这个方法还是会因为超时而返回。

wait(long timeout, int nanos),其内部调用的是 wait(long timout)函数。

上面是线程等待的方法,而唤醒线程主要是下面两个方法:

notify() : 一个线程A调用共享对象的 notify() 方法后,会唤醒一个在这个共享变量上调用 wait 系列方法后被挂起的线程。 一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。 notifyAll() :不同于在共享变量上调用 notify() 函数会唤醒被阻塞到该共享变量上的一个线程,notifyAll()方法则会唤醒所有在该共享变量上由于调用 wait 系列方法而被挂起的线程。

Thread类也提供了一个方法用于等待的方法:

join():如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待thread线程终止之后才

从thread.join()返回。

线程休眠

sleep(long millis) :Thread类中的静态方法,当一个执行中的线程A调用了Thread 的sleep方法后,线程A会暂时让出指定时间的执行权,但是线程A所拥有的监视器资源,比如锁还是持有不让出的。指定的睡眠时间到了后该函数会正常返回,接着参与 CPU 的调度,获取到 CPU 资源后就可以继续运行。

让出优先权

yield() :Thread类中的静态方法,当一个线程调用 yield 方法时,实际就是在暗示线程调度器当前线程请求让出自己的CPU ,但是线程调度器可以无条件忽略这个暗示。

线程中断

Java 中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理。

void interrupt() :中断线程,例如,当线程A运行时,线程B可以调用钱程interrupt() 方法来设置线程的中断标志为true 并立即返回。设置标志仅仅是设置标志, 线程A实际并没有被中断, 会继续往下执行。 boolean isInterrupted() 方法: 检测当前线程是否被中断。 boolean interrupted() 方法: 检测当前线程是否被中断,与 isInterrupted 不同的是,该方法如果发现当前线程被中断,则会清除中断标志。 6.线程有几种状态?

在Java中,线程共有六种状态:

状态 说明 NEW 初始状态:线程被创建,但还没有调用start()方法 RUNNABLE 运行状态:Java线程将操作系统中的就绪和运行两种状态笼统的称作“运行” BLOCKED 阻塞状态:表示线程阻塞于锁 WAITING 等待状态:表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) TIME_WAITING 超时等待状态:该状态不同于 WAITIND,它是可以在指定的时间自行返回的 TERMINATED 终止状态:表示当前线程已经执行完毕

线程在自身的生命周期中, 并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换,Java线程状态变化如图示:

7.什么是线程上下文切换?

使用多线程的目的是为了充分利用CPU,但是我们知道,并发其实是一个CPU来应付多个线程。

为了让用户感觉多个线程是在同时执行的, CPU 资源的分配采用了时间片轮转也就是给每个线程分配一个时间片,线程在时间片内占用 CPU 执行任务。当线程使用完时间片后,就会处于就绪状态并让出 CPU 让其他线程占用,这就是上下文切换。

8.守护线程了解吗?

Java中的线程分为两类,分别为 daemon 线程(守护线程)和 user 线程(用户线程)。

在JVM 启动时会调用 main 函数,main函数所在的钱程就是一个用户线程。其实在 JVM 内部同时还启动了很多守护线程, 比如垃圾回收线程。

那么守护线程和用户线程有什么区别呢?区别之一是当最后一个非守护线程束时, JVM会正常退出,而不管当前是否存在守护线程,也就是说守护线程是否结束并不影响 JVM退出。换而言之,只要有一个用户线程还没结束,正常情况下JVM就不会退出。

9.线程间有哪些通信方式?

volatile和synchronized关键字

关键字volatile可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。

关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。

等待/通知机制

可以通过Java内置的等待/通知机制(wait()/notify())实现一个线程修改一个对象的值,而另一个线程感知到了变化,然后进行相应的操作。

管道输入/输出流

管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。

管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、PipedInputStream、 PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。

使用Thread.join()

如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待thread线程终止之后才从thread.join()返回。。线程Thread除了提供join()方法之外,还提供了join(long millis)和join(long millis,int nanos)两个具备超时特性的方法。

使用ThreadLocal

ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。

可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。

关于多线程,其实很大概率还会出一些笔试题,比如交替打印、银行转账、生产消费模型等等,后面老三会单独出一期来盘点一下常见的多线程笔试题。

ThreadLocal

ThreadLocal其实应用场景不是很多,但却是被炸了千百遍的面试老油条,涉及到多线程、数据结构、JVM,可问的点比较多,一定要拿下。

10.ThreadLocal是什么?

ThreadLocal,也就是线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。

创建

创建了一个ThreadLoca变量localVariable,任何一个线程都能并发访问localVariable。

//创建一个ThreadLocal变量public static ThreadLocal localVariable = new ThreadLocal(); 写入

线程可以在任何地方使用localVariable,写入变量。

localVariable.set("鄙人三某”); 读取

线程在任何地方读取的都是它写入的变量。

localVariable.get(); 11.你在工作中用到过ThreadLocal吗?

有用到过的,用来做用户信息上下文的存储。

我们的系统应用是一个典型的MVC架构,登录后的用户每次访问接口,都会在请求头中携带一个token,在控制层可以根据这个token,解析出用户的基本信息。那么问题来了,假如在服务层和持久层都要用到用户信息,比如rpc调用、更新用户获取等等,那应该怎么办呢?

一种办法是显式定义用户相关的参数,比如账号、用户名……这样一来,我们可能需要大面积地修改代码,多少有点瓜皮,那该怎么办呢?

这时候我们就可以用到ThreadLocal,在控制层拦截请求把用户信息存入ThreadLocal,这样我们在任何一个地方,都可以取出ThreadLocal中存的用户数据。

很多其它场景的cookie、session等等数据隔离也都可以通过ThreadLocal去实现。

我们常用的数据库连接池也用到了ThreadLocal:

数据库连接池的连接交给ThreadLoca进行管理,保证当前线程的操作都是同一个Connnection。 12.ThreadLocal怎么实现的呢?

我们看一下ThreadLocal的set(T)方法,发现先获取到当前线程,再获取ThreadLocalMap,然后把元素存到这个map中。

public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //获取ThreadLocalMap ThreadLocalMap map = getMap(t); //讲当前元素存入map if (map != null) map.set(this, value); else createMap(t, value); }

ThreadLocal实现的秘密都在这个ThreadLocalMap了,可以Thread类中定义了一个类型为ThreadLocal.ThreadLocalMap的成员变量threadLocals。

public class Thread implements Runnable { //ThreadLocal.ThreadLocalMap是Thread的属性 ThreadLocal.ThreadLocalMap threadLocals = null;}

ThreadLocalMap既然被称为Map,那么毫无疑问它是型的数据结构。我们都知道map的本质是一个个形式的节点组成的数组,那ThreadLocalMap的节点是什么样的呢?

static class Entry extends WeakReference


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有