多线程几个简单的题

简介: 《基础系列》

Java线程的几种状态

答:线程是一个动态执行的过程,它有一个从产生到死亡的过程,共五种状态:

新建(new Thread)

当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)

例如:Thread t1=new Thread();

就绪(runnable)

线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();

运行(running)

线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。

死亡(dead)

当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。

自然终止:正常运行run()方法后终止

异常终止:调用stop()方法让一个线程终止运行

堵塞(blocked)

由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。

正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。

正在等待:调用wait()方法。(调用motify()方法回到就绪状态)

被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)


在Java多线程中,请用下面哪种方式不会使线程进入阻塞状态()

A. sleep()
B. Suspend()
C. wait()
D. yield()

答案:D

分析:yield会是线程进入就绪状态


volatile关键字是否能保证线程安全?

答:不能。虽然volatile提供了同步的机制,但是知识一种弱的同步机制,如需要强线程安全,还需要使用synchronized。

Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

一、volatile的内存语义是:

当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新到主内存中。

当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量。

二、volatile底层的实现机制

如果把加入volatile关键字的代码和未加入volatile关键字的代码都生成汇编代码,会发现加入volatile关键字的代码会多出一个lock前缀指令。

1 、重排序时不能把后面的指令重排序到内存屏障之前的位置

2、使得本CPU的Cache写入内存

3、写入动作也会引起别的CPU或者别的内核无效化其Cache,相当于让新写入的值对别的线程可见。


请写出常用的Java多线程启动方式,Executors线程池有几种常用类型?

(1) 继承Thread类


publicclassjava_thread extendsThread{

    publicstaticvoidmain(String args[]) {

        newjava_thread().run();

        System.out.println("main thread run ");

    }

    publicsynchronizedvoidrun() {

        System.out.println("sub thread run ");

    }

}

(2) 实现Runnable接口


publicclassjava_thread implementsRunnable{

    publicstaticvoidmain(String args[]) {

        newThread(newjava_thread()).start();

        System.out.println("main thread run ");

    }

    publicvoidrun() {

        System.out.println("sub thread run ");

    }

}

在Executor框架下,利用Executors的静态方法可以创建三种类型的常用线程池:

1)FixedThreadPool这个线程池可以创建固定线程数的线程池。

2)SingleThreadExecutor是使用单个worker线程的Executor。

3)CachedThreadPool是一个”无限“容量的线程池,它会根据需要创建新线程。


关于sleep()和wait(),以下描述错误的一项是()

A. sleep是线程类(Thread)的方法,wait是Object类的方法
B. Sleep不释放对象锁,wait放弃对象锁
C. Sleep暂停线程、但监控状态任然保持,结束后会自动恢复
D. Wait后进入等待锁定池,只针对此对象发出notify方法后获取对象锁进入运行状态。

答案:D

分析:针对此对象的notify方法后获取对象锁并进入就绪状态,而不是运行状态。另外针对此对象的notifyAll方法后也可能获取对象锁并进入就绪状态,而不是运行状态


进程和线程的区别是什么?

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

区别 进程 线程
根本区别  作为资源分配的单位  调度和执行的单位
开销  每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。  线程可以看成时轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。 
所处环境  系统在运行的时候会为每个进程分配不同的内存区域  除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源 
分配内存  系统在运行的时候会为每个进程分配不同的内存区域  除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源 
包含关系  没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。  线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。 


以下锁机机制中,不能保证线程安全的是()

A. Lock
B. Synchronized
C. Volatile

答案:C


创建n多个线程,如何保证这些线程同时启动?看清,是“同时”。

答:用一个for循环创建线程对象,同时调用wait()方法,让所有线程等待;直到最后一个线程也准备就绪后,调用notifyAll(), 同时启动所有线程。

比如:给你n个赛车,让他们都在起跑线上就绪后,同时出发,Java多线程如何写代码?

思路是,来一辆赛车就加上一把锁,并修改对应的操作数,如果没有全部就绪就等待,并释放锁,直到最后一辆赛车到场后唤醒所有的赛车线程。代码参考如下:

1

2

3

4

5

6

publicclassCarCompetion {

    // 参赛赛车的数量

    protectedfinalinttotalCarNum = 10;

    // 当前在起跑线的赛车数量

    protectedintnowCarNum = 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

publicclassCar implementsRunnable{

    privateintcarNum;

    privateCarCompetion competion = null;

    publicCar(intcarNum, CarCompetion competion) {

        this.carNum = carNum;

        this.competion = competion;

    }

    @Override

    publicvoidrun() {

        synchronized(competion) {

            competion.nowCarNum++;

            while(competion.nowCarNum < competion.totalCarNum) {

                try{

                    competion.wait();

                catch(InterruptedException e) {

                    e.printStackTrace();

                }

            }

            competion.notifyAll();

        }

        startCar();

    }

    privatevoidstartCar() {

        System.out.println("Car num "this.carNum + " start to run.");

        try{

            Thread.sleep(3000);

        catch(InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("Car num "this.carNum + " get to the finish line.");

    }

}

1

2

3

4

5

6

7

8

publicstaticvoidmain(String[] args) {

    CarCompetion carCompetion = newCarCompetion();

    finalExecutorService carPool =

        Executors.newFixedThreadPool(carCompetion.totalCarNum);

    for(inti = 0; i < carCompetion.totalCarNum; i++) {

        carPool.execute(newCar(i, carCompetion));

 

}


同步和异步有何异同,在什么情况下分别使用它们?

答:1.如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。

2.当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

3.举个例子: 打电话是同步 发消息是异步

239.Java线程中,sleep()和wait()区别

答:sleep是线程类(Thread)的方法;作用是导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复;调用sleep()不会释放对象锁。

wait是Object类的方法;对此对象调用wait方法导致本线程放弃对象锁,进入等 待此对象的等待锁定池。只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池,准备获得对象锁进行运行状态。


下面所述步骤中,是创建进程做必须的步骤是()

A. 由调度程序为进程分配CPU
B. 建立一个进程控制块
C. 为进程分配内存
D. 为进程分配文件描述符

答案:BC


无锁化编程有哪些常见方法?()

A. 针对计数器,可以使用原子加
B. 只有一个生产者和一个消费者,那么就可以做到免锁访问环形缓冲区(Ring Buffer)
C. RCU(Read-Copy-Update),新旧副本切换机制,对于旧副本可以采用延迟释放的做法
D. CAS(Compare-and-Swap),如无锁栈,无锁队列等待

答案:D

分析:A 这方法虽然不太好,但是常见

B ProducerConsumerQueue就是这个,到处都是

C linux kernel里面大量使用

D 本质上其实就是乐观锁,操作起来很困难。。单生产者多消费者或者多生产者单消费者的情况下比较常见,也不容易遇到ABA问题。


sleep()和yield()有什么区别?

答:① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;

② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;

③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;

④ sleep()方法比yield()方法(跟操作系统相关)具有更好的可移植性。

243.当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法?

答:不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。 只有等待当前线程执行完毕释放锁资源之后,其他线程才有可能进行执行该同步方法!

延伸 对象锁分为三种:共享资源、this、当前类的字节码文件对象

244.请说出与线程同步相关的方法。

答:1. wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;

2. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException 异常;

3. notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;

4. notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争;

5. JDK 1.5通过Lock接口提供了显式(explicit)的锁机制,增强了灵活性以及对线程的协调。Lock接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通信的Condition对象;

6. JDK 1.5还提供了信号量(semaphore)机制,信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。

相关文章
|
网络协议 安全
libev与多线程
libev与多线程
libev与多线程
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
40 1
C++ 多线程之初识多线程
|
6月前
|
设计模式 安全 Java
多线程问题(三)
多线程问题(三)
35 0
|
6月前
|
Java API 调度
多线程 02
多线程 02
30 0
|
6月前
|
安全 数据库 芯片
多线程的使用
多线程的使用
56 0
|
安全 Java 容器
多线程
多线程
80 0
|
安全 C++
C++多线程(二)
C++多线程
145 0
|
API
多线程具体实现(下)
多线程具体实现
48 0