第5章-多线程-补充案例

第5章-多线程-补充案例
第5章-多线程-补充案例

第五章补充案例

案例5-1继承Thread类创建多线程

一、案例描述

1、考核知识点

编号:00105002

名称:继承Thread类创建多线程

2、练习目标

?掌握如何通过继承Thread类实现多线程的创建。

?掌握Thread类中run()方法和start()方法的使用。

3、需求分析

在程序开发中,会遇到一个功能需要多个线程同时执行才能完成的情况。这时,可以通过继承线程类Thread,并重写Thread类中的run()方法来实现。为了让初学者熟悉如何创建多线程,在案例中将通过继承Thread类方式创建线程,并实现多线程分别打印0~99的数字的功能。

4、设计思路(实现原理)

1)自定义一个类Demo,使其继承Thread类。

2)在Demo类中重写run()方法,在run()方法内编写一个for循环,循环体内打印:“Demo:”

+当前循环次数。

3)编写测试类Example01,在Example01类的main()方法中,创建一个Demo对象,并执

行其start()方法,接着编写一个for循环,循环体内打印:“main:”+当前循环次数。

二、案例实现

class Demo extends Thread {

public void run() {

for (int x = 0; x < 100; x++) {

System.out.println("Demo:"+x);

}

}

}

public class Example01{

public static void main(String[] args) {

Demo d = new Demo();

d.start();

for(int x=0; x<100; x++){

System.out.println("main:"+x);

}

}

}

运行结果如图5-1所示。

图5-1运行结果

三、案例总结

1、通过继承Thread类,并重写Thread类中的run()方法可以实现多线程。

2、Thread类中,提供的start()方法用于启动新线程,线程启动后,系统会自动调用run()方法。

3、main()方法中有一条主线程在运行。

案例5-2实现Runnable接口创建多线程

一、案例描述

1、考核知识点

编号:00105003

名称:实现Runnable接口创建多线程

2、练习目标

?掌握如何通过实现Runnable接口方式创建多线程。

?掌握如何使用Thread类的有参构造方法创建Thread对象。

3、需求分析

在Java中只支持单继承,因此通过继承Thread类创建线程有一定的局限性,这时可以使用另一种方式,即实现Runnable接口来创建线程。通过这种方式需要在Thread(Runnable target)的构造方法中,传递一个实现了Runnable接口的实例对象。接下来在案例中将通过实现Runnable接口方式创建线程,并实现多线程分别打印0~99的数字的功能。

4、设计思路(实现原理)

1)自定义一个类Demo,使其实现Runnable接口。

2)在Demo类中覆写run()方法,在方法编写一个for循环,循环体内打印:当前线程名称:

+当前循环次数。

3)编写测试类Example02,在Example02类的main()方法中,创建一个Demo对象,利用

Thread(Runnable target)构造方法创建2个线程对象,分别命名为“蜘蛛侠”和“钢铁

侠”,并执行线程对象的start()方法,同时编写for循环,循环内打印“main:”+当前循环

次数。

二、案例实现

class Demo implements Runnable {

public void run() {

for(int x=0; x<100; x++){

System.out.println(Thread.currentThread().getName()+":"+x);

}

}

}

public class Example02 {

public static void main(String[] args) {

Demo d = new Demo();

Thread t1 = new Thread(d,"蜘蛛侠");

Thread t2 = new Thread(d,"钢铁侠");

t1.start();

t2.start();

for (int x = 0; x < 100; x++) {

System.out.println("main:" + x);

}

}

}

运行结果如图5-2所示。

图5-2运行结果

三、案例总结

1、可以把实现了Runnable接口并重写run()方法的实例对象,作为Thread有参构造方法的参数来

创建多线程程序。

2、使用Thread类的构造方法Thread(Runnable target, String name)创建线程对象时,还可以给

线程指定新名称。

3、思考一下:既然有了继承Thread类的方式,为什么还要有实现Runnable接口的方式?

a) 可以避免由于Java的单继承带来的局限性。在开发中经常碰到这样一种情况,就是使用一个

已经继承了某一个类的子类创建线程,由于一个类不能同时有两个父类,所以不能用继承Thread 类的方式,那么就只能采用实现Runnable接口的方式。

b) 实现接口的方式,适合多个相同程序代码的线程去处理同一个资源的情况,把线程同程序代

码、数据有效的分离,很好的体现了面向对象的设计思想。例如:一个售票程序继承了Thread类,在售票时启动了多个售票程序,但他们不是同一个对象,数据没有共享,这样就会出现票数重复出售的情况;而当售票程序实现Runnable接口后,多个线程运行同一个售票程序,实现了票数共享的好处。

案例5-3设置后台线程

一、案例描述

1、考核知识点

编号:00105005

名称:后台线程

2、练习目标

?了解后台线程的生命周期

?掌握如何将线程设置为后台线程

3、需求分析

默认情况下,新创建的线程都是前台线程,若想使前台线程变为后台线程,可以使用setDaemon(true)方法实现,为了让初学者熟悉后台线程,案例中将通过设置一个后台线程并演

示后台线程和程序结束之间的关系。

4、设计思路(实现原理)

1)自定义一个类Watcher,使其实现Runnable接口。

2)在Watcher类中覆写run()方法,在方法内编写一个for循环,循环体内打印:线程名称+

循环次数。

3)编写测试类Example03,在Example03类的main()方法中,创建一个Watcher对象,利

用Thread(Runnable target)构造方法创建线程对象并命名,将线程设置为后台线程,执

行该线程的start()方法,接着编写一个for循环,循环内打印循环次数。

二、案例实现

class Watcher implements Runnable {

public void run() {

for (int x = 0; x < 1000; x++) {

System.out.println("我是"+Thread.currentThread().getName()

+"守护者,"+"我在守护雅典娜"+x);

}

}

}

public class Example03 {

public static void main(String[] args) {

Watcher watcher = new Watcher();

Thread t = new Thread(watcher,"星矢");

t.setDaemon(true);

t.start();

for (int i = 3; i >0; i--) {

System.out.println("我是雅典娜女神,我马上死了!"+i);

if(i==1){

System.out.println("我是雅典娜女神,我死了!");

}

}

}

}

运行结果如图5-3所示。

图5-3运行结果

三、案例总结

1、在多线程程序中,一旦前台线程(例如主线程)结束,后台线程也就结束了。

2、要将某个线程设置为后台线程,该线程的setDaemon()方法必须在start()方法之前调用,否则会

引发IllegalThreadStateException异常。

案例5-4线程的优先级

一、案例描述

1、考核知识点

编号:00105007

名称:线程的优先级

2、练习目标

?了解线程中优先级的概念和作用

?掌握设置线程优先级的方法

?掌握线程设置优先级方法中的三个静态常量

3、需求分析

在应用程序中,如果要对线程进行调度,最直接的方式就是设置线程的优先级。这时,可以通过线程的setPriority()方法来设置线程优先级别,实现对线程的调度功能。为了让初学者掌握

线程的优先级,在案例中创建3个线程,分别为它们设置不同的优先级来演示不同优先级线程的调度。

4、设计思路(实现原理)

1)自定义一个类Demo,使其实现Runnable接口。

2)在Demo类中重写run()方法,在方法内编写一个for循环,循环体内打印:线程名称+循

环次数。

3)编写测试类Example04,在Example04类的main()方法中,创建一个Demo对象,利用

Thread的构造方法创建三个线程对象并命名,使用setPriority()方法将其中两个线程的优

先级设为最大和最小,最后开启三个线程的start()方法。

二、案例实现

class Demo implements Runnable {

public void run() {

for (int x = 0; x < 5; x++) {

System.out.println(Thread.currentThread().getName() + "---" + x);

}

}

}

public class Example04 {

public static void main(String[] args) {

Demo d = new Demo();

Thread t1 = new Thread(d,"杨过");

Thread t2 = new Thread(d,"岳不群");

Thread t3 = new Thread(d,"金龙");

//设置线程的优先级

t3.setPriority(Thread.MAX_PRIORITY);

t2.setPriority(1);

t1.start();

t2.start();

t3.start();

}

}

运行结果如图5-4所示。

图5-4运行结果

三、案例总结

1、在多线程程序中,可以通过设置线程的优先级别来控制线程获得CPU执行的几率。

2、线程的优先级用1~10之间的整数来表示,数字越大优先级越高。也可以使用静态常量表示线程

的优先级,如:MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY。

3、虽然Java中提供了10个线程优先级,但是这些优先级需要操作系统的支持,不同的操作系统对

优先级的支持是不一样的,不能很好的和Java中线程优先级一一对应,因此,在设计多线程应用程序时,其功能的实现一定不能依赖于线程的优先级,而只能把线程优先级作为一种提高程序效率的手段。

案例5-5线程休眠

一、案例描述

1、考核知识点

编号:00105008

名称:线程休眠

2、练习目标

?了解线程休眠的概念和作用

?掌握如何使用sleep(long millis)方法使线程休眠

3、需求分析

如果希望人为地控制线程,使正在执行的线程暂停,将CPU让给别的线程。这时,可以使用静态方法sleep(long millis),该方法可以让当前正在执行的线程暂停一段时间,进入休眠等待状态。为了让初学者更好地掌握线程休眠,案例中将启动三个线程共同出售10张票来演示线程休眠及休眠引发的结果。

4、设计思路(实现原理)

1)自定义一个类Ticket,使其实现Runnable接口,并在该类中创建int类型私有属性tickets,

赋初值为10。

2)在Ticket类中重写run()方法,在方法内编写一个while循环。循环体内判断ticket值,当

大于0时,使用sleep(long millis)方法使线程休眠1秒钟,并打印:当前线程名称+“正在

出售第”+循环次数;否则结束循环。每执行一次while循环,tickets值减一。

3)编写测试类Example05,在Example05类的main()方法中,创建一个Ticket对象,利用

Thread的构造方法创建三个线程对象并命名,并开启三个线程的start()方法。

二、案例实现

class Ticket implements Runnable {

private int tickets = 10;

public void run() {

while (true) {

if (tickets > 0) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()

解决多线程中11个常见问题

并发危险 解决多线程代码中的11 个常见的问题 Joe Duffy 本文将介绍以下内容:?基本并发概念 ?并发问题和抑制措施 ?实现安全性的模式?横切概念本文使用了以下技术: 多线程、.NET Framework 目录 数据争用 忘记同步 粒度错误 读写撕裂 无锁定重新排序 重新进入 死锁 锁保护 戳记 两步舞曲 优先级反转 实现安全性的模式 不变性 纯度 隔离 并发现象无处不在。服务器端程序长久以来都必须负责处理基本并发编程模型,而随着多核处理器的日益普及,客户端程序也将需要执行一些任务。随着并发操作的不断增加,有关确保安全的问题也浮现出来。也就是说,在面对大量逻辑并发操作和不断变化的物理硬件并行性程度时,程序必须继续保持同样级别的稳定性和可靠性。 与对应的顺序代码相比,正确设计的并发代码还必须遵循一些额外的规则。对内存的读写以及对共享资源的访问必须使用同步机制进行管制,以防发生冲突。另外,通常有必要对线程进行协调以协同完成某项工作。 这些附加要求所产生的直接结果是,可以从根本上确保线程始终保持一致并且保证其顺利向前推进。同步和协调对时间的依赖性很强,这就导致了它们具有不确定性,难于进行预测和测试。 这些属性之所以让人觉得有些困难,只是因为人们的思路还未转变过来。没有可供学习的专门API,也没有可进行复制和粘贴的代码段。实际上的确有一组基础概念需要您学习和适应。很可能随着时间的推移某些语言和库会隐藏一些概念,但如果您现在就开始执行并发操作,则不会遇到这种情况。本

文将介绍需要注意的一些较为常见的挑战,并针对您在软件中如何运用它们给出一些建议。 首先我将讨论在并发程序中经常会出错的一类问题。我把它们称为“安全隐患”,因为它们很容易发现并且后果通常比较严重。这些危险会导致您的程序因崩溃或内存问题而中断。 当从多个线程并发访问数据时会发生数据争用(或竞争条件)。特别是,在一个或多个线程写入一段数据的同时,如果有一个或多个线程也在读取这段数据,则会发生这种情况。之所以会出现这种问题,是因为Windows 程序(如C++ 和Microsoft .NET Framework 之类的程序)基本上都基于共享内存概念,进程中的所有线程均可访问驻留在同一虚拟地址空间中的数据。静态变量和堆分配可用于共享。请考虑下面这个典型的例子: static class Counter { internal static int s_curr = 0; internal static int GetNext() { return s_curr++; } } Counter 的目标可能是想为GetNext 的每个调用分发一个新的唯一数字。但是,如果程序中的两个线程同时调用GetNext,则这两个线程可能被赋予相同的数字。原因是s_curr++ 编译包括三个独立的步骤: 1.将当前值从共享的s_curr 变量读入处理器寄存器。 2.递增该寄存器。 3.将寄存器值重新写入共享s_curr 变量。 按照这种顺序执行的两个线程可能会在本地从s_curr 读取了相同的值(比如42)并将其递增到某个值(比如43),然后发布相同的结果值。这样一来,GetNext 将为这两个线程返回相同的数字,导致算法中断。虽然简单语句s_curr++ 看似不可分割,但实际却并非如此。 忘记同步 这是最简单的一种数据争用情况:同步被完全遗忘。这种争用很少有良性的情况,也就是说虽然它们是正确的,但大部分都是因为这种正确性的根基存在问题。 这种问题通常不是很明显。例如,某个对象可能是某个大型复杂对象图表的一部分,而该图表恰好可使用静态变量访问,或在创建新线程或将工作排入线程池时通过将某个对象作为闭包的一部分进行传递可变为共享图表。 当对象(图表)从私有变为共享时,一定要多加注意。这称为发布,在后面的隔离上下文中会对此加以讨论。反之称为私有化,即对象(图表)再次从共享变为私有。 对这种问题的解决方案是添加正确的同步。在计数器示例中,我可以使用简单的联锁: static class Counter { internal static volatile int s_curr = 0; internal static int GetNext() { return Interlocked.Increment(ref s_curr);

JAVA多线程试题 答案

多线程 一.选择题 1.下列说法中错误的一项是(A) A.线程就是程序 B.线程是一个程序的单个执行流 B.多线程是指一个程序的多个执行流D.多线程用于实现并发 2.下列哪个一个操作不能使线程从等待阻塞状态进入对象阻塞状态(D) A.等待阴塞状态下的线程被notify()唤 B.等待阻塞状态下的纯种被interrput()中断 C.等待时间到 D.等待阻塞状态下的线程调用wait()方法 3.下列哪个方法可以使线程从运行状态进入其他阻塞状态(A) A.sleep B.wait C.yield D.start 4.下列说法中错误的一项是(D) A.一个线程是一个Thread类的实例 B.线程从传递给纯种的Runnable实例run()方法开始执行 C.线程操作的数据来自Runnable实例 D.新建的线程调用start()方法就能立即进入运行状态 5.下列关于Thread类提供的线程控制方法的说法中,错误的一项是(D) A.在线程A中执行线程B的join()方法,则线程A等待直到B执行完成 B.线程A通过调用interrupt()方法来中断其阻塞状态 C.若线程A调用方法isAlive()返回值为true,则说明A正在执行中 D.currentThread()方法返回当前线程的引用 6.下列说法中,错误的一项是() A.对象锁在synchronized()语句执行完之后由持有它的线程返还 B.对象锁在synchronized()语句中出现异常时由持有它的线程返还 C.当持有锁的线程调用了该对象的wait()方法时,线程将释放其持有的锁 D.当持有锁的线程调用了该对象的构造方法时,线程将释放其持有的锁 7.下面的哪一个关键字通常用来对对象的加锁,从而使得对对象的访问是排他的A A.sirialize B transient C synchronized D static 二.填空题 1.在操作系统中,被称做轻型的进程是线程 2.多线程程序设计的含义是可以将一个程序任务分成几个并行的任务 3.在Java程序中,run()方法的实现有两种方式:实现Runnable接口和继承Thread类 4.多个线程并发执行时,各个线程中语句的执行顺序是确定的,但是线程之间的相对执行顺序是不确定的 6.Java中的对象锁是一种独占的排他锁 7.程序中可能出现一种情况:多个线种互相等待对方持有的锁,而在得到对方的锁之前都不会释放自己的锁,这就是死锁 8.线程的优先级是在Thread类的常数MIN_PRIORITY和MAX_PRIORITY 之间的一个值 9.处于新建状态的线程可以使用的控制方法是start()和stop()。 10.一个进程可以包含多个线程

15个Java多线程面试题及答案

15个Java多线程面试题及答案 1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。 2)在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? lock接口在多线程和并发编程中最大的优势是它们为读和写分别提 供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。Java线程面试的问题越来越会根据面试者的回答来提问。芯学苑老师强烈建议在你在面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。 3)在java中wait和sleep方法的不同?

通常会在电话面试中经常被问到的Java线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。 4)用Java实现阻塞队列。 这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用Java线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的Java 5中的并发类来再写一次。 5)用Java写代码来解决生产者——消费者问题。 与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在Java中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。 6)用Java编程一个会导致死锁的程序,你将怎么解决?

windows 并发的多线程的应用

(1)苹果香蕉问题 #include using namespace std; #include #include int k; HANDLE Apple_;HANDLE Banana_; CRITICAL_SECTION mmutex; DWORD WINAPI Son(LPVOID n) {//HANDLE Apple_; CRITICAL_SECTION mmutex; int i=1; OpenSemaphore(MUTEX_ALL_ACCESS,false,"Apple_"); while(1) { ::WaitForSingleObject(Apple_,INFINITE);//等苹果 cout<<"Son eats"<

多线程与并发面试题

多线程与并发面试题

JAVA多线程和并发基础面试问答 原文链接译文连接作者:Pankaj 译者:郑旭东校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,可是你依然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:非常赞同这个观点) Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它能够被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程能够被称为轻量级进程。线程需要较少的资源来创立和驻留在进程中,而且能够共享进程中的资源。 2. 多线程编程的好处是什么?

在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创立多个线程去执行一些任务会比创立多个进程更好。举个例子,Servlets比CGI更好,是因为Servlets支持多线程而CGI不支持。 3. 用户线程和守护线程有什么区别? 当我们在Java程序中创立一个线程,它就被称为用户线程。一个守护线程是在后台执行而且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序而且退出。一个守护线程创立的子线程依然是守护线程。 4. 我们如何创立一个线程? 有两种创立线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创立一个Thread对象;二是直接继承Thread类。若想了解更多能够阅读这篇关于如何在Java中创立线程的文章。 5. 有哪些不同的线程生命周期?

多线程练习题卷

多线程 一、单项选择题(从下列各题四个备选答案中选出一个正确答案,并将其代号写在答题纸相应位置处。答案错选或未选者,该题不得分。)50 1.下述哪个选项为真?( ) A.Error类是一个RoutimeException异常 B.任何抛出一个RoutimeException异常的语句必须包含在try块之内 C.任何抛出一个Error对象的语句必须包含在try块之内 D. 任何抛出一个Exception异常的语句必须包含在try块之内 2.下列关于Java线程的说法哪些是正确的?( ) A.每一个Java线程可以看成由代码、一个真实的CPU以及数据3部分组成 B.创建线程的两种方法,从Thread类中继承的创建方式可以防止出现多父类问题 C.Thread类属于java.util程序包 D.以上说法无一正确 3.哪个关键字可以对对象加互斥锁?( ) A.transient B.synchronized C.serialize D.static 4.下列哪个方法可用于创建一个可运行的类?() A.public class X implements Runable { public void run() {……} } B. public class X implements Thread { public void run() {……} } C. public class X implements Thread { public int ru n() {……} } D.public class X implements Runable { protected void run() {……} } 5.下面哪个选项不会直接引起线程停止执行?( ) A.从一个同步语句块中退出来 B.调用一个对象的wait方法 C.调用一个输入流对象的read方法 D.调用一个线程对象的setPriority方法 6.使当前线程进入阻塞状态,直到被唤醒的方法是( ) A.resume()方法 B.wait()方法 C.suspend()方法 D.notify()方法 7.运行下列程序,会产生的结果是( ) public class X extends Thread implements Runnable { public void run(){ System.out.println(“this is run()”); } public static void main(String[] args) { Thread t=new Thread(new X()); t.start(); }

java多线程并发面试题【java多线程和并发基础面试题】

java多线程并发面试题【java多线程和并 发基础面试题】 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。下面就由小编为大家介绍一下java多线程和并发基础面试题的文章,欢迎阅读。 java多线程和并发基础面试题篇1 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程可以被称为轻量级进程。线程需要较少的来创建和驻留在进程中,并且可以共享进程中的。 2. 多线程编程的好处是什么? 在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待而进入空闲状态。多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好。举个例子,Servlets比CGI更好,是因为Servlets支持多线程而CGI不支持。 3. 用户线程和守护线程有什么区别? 当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的

线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。 4. 我们如何创建一个线程? 有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。 java多线程和并发基础面试题篇2 1. 有哪些不同的线程生命周期? 当我们在Java程序中新建一个线程时,它的状态是New。当我们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running。其他的线程状态还有Waiting,Blocked 和Dead。 2. 可以直接调用Thread类的run()方法么? 当然可以,但是如果我们调用了Thread的run()方法,它的行为就会和普通的方法一样,为了在新的线程中执行我们的代码,必须使用Thread.start()方法。 3. 如何让正在运行的线程暂停一段时间? 我们可以使用Thread类的Sleep()方法让线程暂停一段时间。需要注意的是,这并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。

并发危险:解决多线程代码中的 11 个常见的问题

并发危险:解决多线程代码中的11 个常见的问题 并发危险 解决多线程代码中的11 个常见的问题 Joe Duffy 目录 数据争用忘记同步粒度错误读写撕裂无锁定重新排序重新进入死锁锁保护戳记两步舞曲优先级反转实现安全性的模式不变性纯度隔离并发现象无处不在。服 务器端程序长久以来都必须负责处理基本并发编程模型,而随着多核处理器的日益普及,客户端程序也将需要执行一些任务。随着并发操作的不断增加,有关确保安 全的问题也浮现出来。也就是说,在面对大量逻辑并发操作和不断变化的物理硬件并行性程度时,程序必须继续保持同样级别的稳定性和可靠性。 与对应的顺序代码相比,正确设计的并发代码还必须遵循一些额外的规则。对内存的读写以及对共享资源的访问必须使用同步机制进行管制,以防发生冲突。另外,通常有必要对线程进行协调以协同完成某项工作。

这些附加要求所产生的直接结果是,可以从根本上确保线程始终保持一致并且保证其顺利向前推进。同步和协调对时间的依赖性很强,这就导致了它们具有不确定性,难于进行预测和测试。 这 些属性之所以让人觉得有些困难,只是因为人们的思路还未转变过来。没有可供学习的专门 API,也没有可进行复制和粘贴的代码段。实际上的确有一组基础概念需要您学习和适应。很可能随着时间的推移某些语言和库会隐藏一些概念,但如果您现在就 开始执行并发操作,则不会遇到这种情况。本文将介绍需要注意的一些较为常见的挑战,并针对您在软件中如何运用它们给出一些建议。 首先我将讨论在并发程序中经常会出错的一类问题。我把它们称为“安全隐患”,因为它们很容易发现并且后果通常比较严重。这些危险会导致您的程序因崩溃或内存问题而中断。当 从多个线程并发访问数据时会发生数据争用(或竞争条件)。特别是,在一个或多个线程写入一段数据的同时,如果有一个或多个线程也在读取这段数据,则会发生 这种情况。之所以会出现这种问题,是因为Windows 程序(如C++ 和Microsoft .NET Framework

【2020最新Java面试题资料】15道面试常问的Java多线程面试题

1)现在有T1T2T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。 2)在中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? lock接口在多线程和并发编程中最大的优势是它们为读和写分别提供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。线程面试的问题越来越会根据面试者的回答来提问。我强烈建议在你去参加多线程的面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。 3)在java中wait和sleep方法的不同? 通常会在电话面试中经常被问到的线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。 4)用实现阻塞队列。 这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的Java 5中的并发类来再写一次。 5)用Java写代码来解决生产者——消费者问题。 与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。 6)用编程一个会导致死锁的程序,你将怎么解决? 这是我最喜欢的线程面试问题,因为即使死锁问题在写多线程并发程序时非常普遍,但是很多侯选者并不能写deadlock free code(无死锁代码?),他们很挣扎。只要告诉他们,你有N个资源和N个线程,并且你需要所有的资源来完成一个操作。为了简单这里的n可以替换为2,越大的数据会使问题看起来更复杂。通过避免中的死锁来得到关于死锁的更多信息。

15个顶级Java多线程面试题及回答

Java 线程面试问题 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的问题。在投资银行业务中多线程和并发是一个非常受欢迎的话题,特别是电子交易发展方面相关的。他们会问面试者很多令人混淆的Java线程问题。面试官只是想确信面试者有足够的Java线程与并发方面的知识,因为候选人中有很多只浮于表面。用于直接面向市场交易的高容量和低延时的电子交易系统在本质上是并发的。下面这些是我在不同时间不同地点喜欢问的Java线程问题。我没有提供答案,但只要可能我会给你线索,有些时候这些线索足够回答问题。现在引用Java5并发包关于并发工具和并发集合的问题正在增多。那些问题中ThreadLocal、Blocking Queue、Counting Semaphore和ConcurrentHashMap比较流行。 15个Java多线程面试题及回答 1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。 2)在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? lock接口在多线程和并发编程中最大的优势是它们为读和写分别提供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。Java线程面试的问题越来越会根据面试者的回答来提问。我强烈建议在你去参加多线程的面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。 3)在java中wait和sleep方法的不同? 通常会在电话面试中经常被问到的Java线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。 4)用Java实现阻塞队列。 这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用Java线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的Java 5中的并发类来再写一次。 5)用Java写代码来解决生产者——消费者问题。 与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在Java中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。

多核技术与并发多线程技术的区别介绍

多核技术与并发多线程技术的区别介绍 2009-04-16 20:29:32 作者:admin来源:浏览次数:195 网友评论 0 条 很多人在多核技术与并发多线程技术上会把概念弄混淆,我这里给大家简单介绍一下这两个技术的不同。多核技术可以看成是一种cpu的集成技术,在一个CPU处理模块上,可以集成2个或者是多个CPU,但是,他们还是单独的物理cpu。并发多线程技术则需要OS 的支持,是在OS级别上,可以实现一个物理cpu的多线程并发处理,提高oltp环境模式下的cpu利用率。 先说多核技术,如在IBM的power5中,就有如下几种不同的cpu集成模式:如D ual-Core Module(双核),表示一个cpu模块中其实有2个物理的cpu(也叫core),他们共享L2与L3 cache。这种cpu模块一般用在520/550上,所以,象采用这种模式的550最多可以有4颗CPU(2个cpu模块)。而Quad Core Module [QCM],这个不知道怎么翻译了,这种技术下,在一个cpu模块中,有4个物理cpu,相当与2个Dual-Core Module集成在一个cpu模块中,从55Q以后的机型,如55Q、55A都支持这样的cpu模块,所以,如果采用这样的模块,55A就可以达到8颗物理cpu。最后说Multi-Chip Module,这个就叫多核技术了,如一个cpu模块中,可以有8个物理cpu,相当于4个Dual-Core Module,一般用在590与595上。如图,则是一个采用了Multi-Chip Module技术的cpu模块: 从以上的图片与描述可以看到,这种多核技术不过是物理cpu的高集成度技术,让更小的地方,可以放更多的cpu,如550空间大小不变,同样2个cpu模块,如果采用双

实验五、多线程并发服务器编程

实验五、多线程并发服务器编程 一、实验目的 1、学习Linux操作系统的多线程的基本概念以及进程与线程的区别; 2、掌握编写多线程程序的一般方法; 3、熟悉多线程并发服务器的设计思路,以及多线程程序的编译方法。 二、实验容 线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。现在多线程技术已经被许多操作系统所支持,包括Windows/NT以及Unix/Linux。 为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计一个进程的开销大约是一个线程开销的30倍左右。 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。 1、编写一个最简单的多线程程序 请仔细运行、分析下列程序,指出多进程和多线程如何区分?何谓父线程和子线程?当父线程终止会导致子线程发生何种情况? /***************************************************************** 文件名:pthread_example.c 演示了pthread_create函数创建子线程的使用 *******************************************************************/ #include #include //创建多线程

socket多线程并发

Socket()函数 Int socket(int domain,int type,int protcol); Socket用于创建一个socket的描述符, Domain :即协议族,常用的协议有AF_INET,AF_INET6,AF_LOCAL AF_INE:IPV4 type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。 protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP 传输协议、TIPC传输协议(这个协议我将会单独开篇讨论!) bind()函数 正如上面所说bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。 int bind(int sockfd, conststruct sockaddr *addr, socklen_t addrlen); 函数的三个参数分别为: ?sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。 bind()函数就是将给这个描述字绑定一个名字。 ?addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:?struct sockaddr_in { ? sa_family_t sin_family; /* address family: AF_INET */ ? in_port_t sin_port; /* port in network byte order */?struct in_addr sin_addr; /* internet address */

多线程与并发面试题

JAVA多线程和并发基础面试问答 原文链接译文连接作者:Pankaj 译者:郑旭东校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:非常赞同这个观点) Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程可以被称为轻量级进程。线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。 2. 多线程编程的好处是什么? 在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好。举个例子,Servlets比CGI更好,是因为Servlets支持多线程而CGI不支持。 3. 用户线程和守护线程有什么区别? 当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。 4. 我们如何创建一个线程? 有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。若想了解更多可以阅读这篇关于如何在Java 中创建线程的文章。 5. 有哪些不同的线程生命周期?

Linux下多线程并发控制的机制分析

Linux下多线程并发控制的机制分析 一、Linux下并发控制原因及方法 1.1、并发控制 如果在对linux字符设备驱动实例进行分析时,我们可能会有一个疑问,当我们调用copy_to_user和copy_from_user函数完成从用户态、内核态的读写操作的时候,如果这两个操作并发执行的话会出现一个什么样的情况?那肯定是会出现问题的,这个很容易理解,那么我们怎么去解决这一类似的问题呢?这就是我现在要探讨的linux并发控制机制。 我们在设备驱动的编写过程中要解决的一个问题就是并发的控制,也就是进程对共享资源的并发访问,而在linux中,提供了多个解决并发控制的方式,比如:中断屏蔽、原子操作、互斥锁等,下面将逐一介绍。 1.2、中断屏蔽 中断屏蔽的概念实际上就是平常所说的开中断、关中断。在单cpu中使用中断屏蔽来避免竟态是很方便的一种方法,每次在进入临界区之前屏蔽所有的中断,访问完成后再打开中断。这项功能可以保障在执行的内核执行路径不被中断处理程序所抢占。它将使得中断与进程之间的并发不在发生,而且由于linux内核的进程调度都依赖于中断来实现,所以这样也就可以避免进程之间的并发。 中断对于内核的运行是非常重要的,在中断屏蔽期间,所有的中断都无法得到处理,因此长时间的屏蔽中断是很危险的,可能会造成数据丢失、系统崩溃等严重的后果。这就要求临界区的执行应该尽可能的快。 1.3、原子操作 原子操作,指的是在执行的过程中不会被中断的操作。linux内核提供了一系列的函数来完成原子操作,内核代码可以安全的调用他们而不被打断。 1.4、自旋锁

者生产产品后再取。 三、程序实现 环境:操作系统:RHEL5,语言:C++。在Linux中,使用Pthread库来操作线程。POSIX thread是一个标准线程定义,该标准定义一系列的多线程标准。pthreads线程库实现了POSIX线程标准,定义了一套C\C++语言下的线程库。 首先需要定义生产者线程、消费者线程、产品仓库等,在本例中,使用pthread 线程库,所以生产者线程和消费者线程都定义为pthread_t类型;产品仓库则使用一个先进先出的队列来实现,这个队列类型直接使用了stl库中的list 结构。 //定义生产者和消费者线程 pthread_t producer,consumer; //定义产品仓库,用List结构来保存。 list storeList; 在主线程中使用pthread_create方法来创建了生产者线程和消费者线程,创建线程主要需要传入pthread_t类型的线程对象,以及线程的工作方法。如果线程创建函数返回的值不为0,则表示线程创建失败,可能由于没有足够的系统资源。 //创建生产者线程,并使其工作。 ret = pthread_create(&producer,NULL, producer_work,NULL); //创建消费者线程,并使其工作。 ret1 = pthread_create(&consumer,NULL, consumer_work,NULL); //如果线程创建函数返回的值不为0,则表示线程创建失败,可能由于没有足够的系统资源。 if(ret != 0 || ret1 != 0) { cout << "创建线程错误! \n"; exit(1); }

操作系统实验报告线程并发拷贝程序定稿版

操作系统实验报告线程 并发拷贝程序 HUA system office room 【HUA16H-TTMS2A-HUAS8Q8-HUAH1688】

操作系统:线程(进程)并发拷贝程序附录一:程序代码 #include #include #include #include #include #include #include #define PSIZE 4096 /*管道文件的大小*/ #define BSIZE 128 /*默认缓冲区的大小*/ #define NOFILE 20 /*u_ofile3575表可分配的个数*/ #define NFILE 20 /*file表可分配的个数*/ #define NPIPE 20 /*pipecb3575可分配的个数*/ /*进程的u_file表*/ int u_ofile3575[NOFILE];

struct { char f_flag;/*读写标志,'w'表示写,'r'表示读*/ int f_count;/*表示此表项的状态,=0表示此表项没被使用,可分配;=1表示此表项在被使用,不可再分配*/ int f_inode;/*对应的pipecb3575表下标*/ long f_offset;/*读写指针,当前已读或已写个数*/ }file[NFILE]; /*管道控制块*/ struct { char *p_addr;/*管道文件基地址*/ int p_size;/*管道文件大小,PSIZE*/ int p_count;/*=2表示读写都在被进行,=1表示在被读或被写,=0表示管道没被使用,可分配*/ }pipecb3575[NPIPE];

相关文档
最新文档