多线程程序设计中如何尽早发现问题_1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、多线程(并发编程)、I/O(NIO)、Socket、JDB...

本文深入探讨了Java中的并发编程和多线程概念,涵盖了线程的生命周期、同步机制、线程池以及并发工具类。详细解释了线程的创建、运行、阻塞和死亡状态,强调了线程安全与死锁问题,以及如何通过synchronized、wait/notify等方法进行线程间通信。此外,还提到了线程优先级、线程池的使用和最佳实践,以及Java中的线程局部变量ThreadLocal。
摘要由CSDN通过智能技术生成

. 进程和线程之间有什么不同?

一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程可以被称为轻量级进程。线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。

2. 多线程编程的好处是什么?

在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。

3. 用户线程和守护线程有什么区别?

当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。

4. 我们如何创建一个线程?

有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类,并重写run方法。

5. 线程生命周期?

① 新建状态(New Thread):在Java语言中使用new 操作符创建一个线程后,该线程仅仅是一个空对象,它具备类线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。

线程处于创建状态时,可通过Thread类的方法来设置各种属性,

② 就绪状态(Runnable):使用start()方法启动一个线程后,系统为该线程分配所需资源,使该线程处于就绪状态。

③ 运行状态(Running):Java运行系统通过调度选中一个处于就绪状态的线程,使其占有CPU并转为运行状态。此时,系统真正执行线程的run()方法。

④ 阻塞和唤醒线程

阻塞状态(Blocked):一个正在运行的线程因某些原因不能继续运行时,就进入阻塞 状态。这些原因包括:

a)          当执行了某个线程对象的sleep()等阻塞类型的方法时,该线程对象会被置入一个阻塞集内,等待超时而自动苏醒。

b)          当多个线程试图进入某个同步区域时,没能进入该同步区域的线程会被置入锁定集,直到获得该同步区域的锁,进入就绪状态。

c)          当线程执行了某个对象的wait()方法时,线程会被置入该对象的等待集中,知道执行了该对象的notify()方法wait()/notify()方法的执行要求线程首先获得该对象的锁。

⑤ 死亡状态(Dead):线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。

终止线程的三种方法

① 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,推荐使用。

② 使用stop方法强制终止线程(这个方法不推荐使用,因为stop和suppend、resume一样,也可能发生不可预料的结果)。

③ 使用interrupt方法中断线程。

6. 可以直接调用Thread类的run()方法么?

当然可以,但是如果我们调用了Thread的run()方法,它的行为就会和普通的方法一样,为了在新的线程中执行我们的代码,必须使用Thread.start()方法。

7. 如何让正在运行的线程暂停一段时间?

3******线程中sleep()方法和Object类中的wait()方法的区别

这两者的施加者是有本质区别的.

sleep()是让某个线程暂停运行一段时间,是由当前线程决定控制的,也就是说,在线程里面决定.好比如说,我要做的事情是 "点火->烧水->煮面",而当我点完火之后我不立即烧水,我要休息一段时间再烧.对于运行的主动权是由我的流程来控制.

而wait(),首先,这是由某个确定的对象来调用的,将这个对象理解成一个传话的人,当这个人在某个线程里面说"暂停!",也是 thisOBJ.wait(),这里的暂停是阻塞,还是"点火->烧水->煮饭",thisOBJ就好比一个监督我的人站在我旁边,本来该线 程应该执行1后执行2,再执行3,而在2处被那个对象喊暂停,那么我就会一直等在这里而不执行3,但正个流程并没有结束,我一直想去煮饭,但还没被允许, 直到那个对象在某个地方说"通知暂停的线程启动!",也就是thisOBJ.notify()的时候,那么我就可以煮饭了,这个被暂停的线程就会从暂停处 继续执行.

其实两者都可以让线程暂停一段时间,但是本质的区别是一个线程的运行状态控制,一个是线程之间的通讯的问题

在java.lang.Thread类中,提供了sleep(),

而java.lang.Object类中提供了wait(), notify()和notifyAll()方法来操作线程

sleep()可以将一个线程睡眠,参数可以指定一个时间。

而wait()可以将一个线程挂起,直到超时或者该线程被唤醒。

wait有两种形式wait()和wait(milliseconds).

sleep和wait的区别有:

1,这两个方法来自不同的类分别是Thread和Object

2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在

任何地方使用

synchronized(x){

x.notify()

//或者wait()

}

4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

8. 你对线程优先级的理解是什么?

每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级。

12.线程之间是如何通信的?

当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法可以用于线程间通信关于资源的锁的状态。点击这里有更多关于线程wait, notify和notifyAll.

14. 为什么wait(), notify()和notifyAll()必须在同步方法或者同步块中被调用?

当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用

18. 同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。

20. 什么是ThreadLocal?

ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。

每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。在ThreadLocal例子这篇文章中你可以看到一个关于ThreadLocal的小程序。

23. 什么是死锁(Deadlock)?如何分析和避免死锁?

死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。

分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。

避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法,阅读这什么是线程池?如何创建一个Java线程池?

Java并发面试问题

1. 什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)?

原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。

线程调度

线程调度是指系统为线程分配处理器使用权的过程:协同式、抢占式。

协同式:线程的执行时间由线程本身控制,线程把自己的工作执行完了之后,要主动通知系统切换到另一个线程上。坏处:线程执行时间不可控制。

抢占式:每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。Java使用该种调用方式。

线程优先级:在一些平台上(操作系统线程优先级比Java线程优先级少)不同的优先级实际会变得相同;优先级可能会被系统自行改变。

2*****IO流

4. 字符流与字节流的区别

经过以上的描述,我们可以知道字节流与字符流之间主要的区别体现在以下几个方面:

字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。

字节流默认不使用缓冲区;字符流使用缓冲区。

字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。

1.java中有几种类型的流?jdk为每种类型的流提供了一些抽象类以供继承,请说出它们分别是什么?

字符流和字节流。字节流继承inputStream和OutputStream,字符流继承自InputSteamReader和OutputStreamWriter。在java.io包中还有许多其他的流,主要是为了提高性能和使用方便。

我们有时候将一个java对象变成字节流的形式传出去或者从一个字节流中恢复成一个java对象,例如,要将java对象存储到硬盘或者传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个java对象变成某个格式的字节流再传输,但是,jre本身就提供了这种支持,我们可以调用OutputStream的writeObject方法来做,如果要让java帮我们做,要被传输的对象必须实现serializable接口,这样,javac编译时就会进行特殊处理,编译的类才可以被writeObject方法操作,这就是所谓的序列化。需要被序列化的类必须实现Serializable接口,该接口是一个mini接口,其中没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的。

例如,在web开发中,如果对象被保存在了Session中,tomcat在重启时要把Session对象序列化到硬盘,这个对象就必须实现Serializable接口。如果对象要经过分布式系统进行网络传输或通过rmi等远程调用,这就需要在网络上传输对象,被传输的对象就必须实现Serializable接口。

3.流按照传输的方向可以分为哪两种,分别举例说明

答案

输入输出相对于程序

输入流InputStream

,输出流OutputStream

7.如果我要对字节流进行大量的从硬盘读取,要用那个流,为什么

答案

BufferedInputStream 使用缓冲流能够减少对硬盘的损伤

8.如果我要打印出不同类型的数据到数据源,那么最适合的流是那个流,为什么

答案

Printwriter 可以打印各种数据类型

11.怎么样把输出字节流转换成输出字符流,说出它的步骤

答案

使用 转换处理流OutputStreamWriter可以将字节流转为字符流New OutputStreamWriter(new FileOutputStream(File file));

13.把一个对象写入数据源或者从一个数据源读出来,用哪两个流

答案

ObjectInputStream ObjectOutputStream

14.什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作

答案

对象序列化,将对象以二进制的形式保存在硬盘上反序列化;将二进制的文件转化为对象读取实现serializable接口

不想让字段放在硬盘上就加transient

15.如果在对象序列化的时候不想给一个字段的数据保存在硬盘上面,采用那个关键字?

答案

transient关键字

19.流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?

答案

流一旦打开就必须关闭,使用close方法放入finally语句块中(finally语句一定会执行)调用的处理流就关闭处理流多个流互相调用只关闭最外层的流

20.Java中的所有的流可以分为几大类,它们的名字是什么,各代表什么

答案

分为 字节输入流InputStream字节输出流 OutputStream字符输入流 Reader字符输出流 Writer所有流都是这四个流的子类

说下常用的io流

Icon

InputStream,OutputStream,

FileInputStream,FileOutputStream,

BufferedInputStream,BufferedOutputStream

Reader,Writer

BufferedReader,BufferedWriter

21 写一段代码读取一个序列化的对象一般使用哪种Stream?

Icon

A、InputStream B、FileReader C、DataInputStream D、ObjectStream

22 io流怎样读取文件的?

Icon

使用File对象获取文件路径,通过字符流Reader加入文件,使用字符缓存流BufferedReader处理Reader,再定义一个字符串,循环遍历出文件。代码如下:

File file = new File("d:/spring.txt");

try {

Reader reader = new FileReader(file);

BufferedReader buffered = new BufferedReader(reader);

String data = null;

while((data = buffered.readLine())!=null){

System.out.println(data);

}

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

23 说说你对io流的理解

Icon

Io流主要是用来处理输入输出问题,常用的io流有InputStream,OutputStream,Reader,Writer等

24 JAVA的IO流和readLine方法

Icon

Java的io流用来处理输入输出问题,readLine是BufferedReader里的一个方法,用来读取一行。

25 用什么把对象动态的写入磁盘中,写入要实现什么接口。

Icon

ObjectInputStream,需要实现Serializable接口

28 请问你在什么情况下会在你得java代码中使用可序列化? 如何实现java序列化?

Icon

把一个对象写入数据源或者从一个数据源读出来,使用可序列化,需要实现Serializable接口

3****反射

3、哪里用到反射机制?

jdbc中有一行代码:Class.forName(‘com.MySQL.jdbc.Driver.class‘).newInstance();这就是反射,现在

很多框架都用到反射机制,hibernate,struts都是用反射机制实现的。

4、反射机制的优缺点?

静态编译:在编译时确定类型,绑定对象,即通过

动态编译:运行时确定类型,绑定对象。动态编译最大限度的发挥了java的灵活性,体现了多态的应用,有利于降低类之间的耦合性。

2.3 Java反射应用场合

在Java程序中许多对象在运行时都会出现两种类型:编译时类型和运行时类型

编译时的类型由声明该对象时使用的类型决定,运行时的类型由实际赋给对象的类型决定

如:Person p =new Student();

编译时类型为Person,而运行时为Student

除此之外,程序在运行时还可能接收到外部传入的一个对象,该对象的编译时类型为Object,但程序又需要调用该对象运行时类型的方法。为了这些问题程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,此时就必须使用反射

二,反射机制的作用:

1,反编译:.class-->.java

2,通过反射机制访问java对象的属性,方法,构造方法等;

这样好像更容易理解一些,下边我们具体看怎么实现这些功能。

三,在这里先看一下sun为我们提供了那些反射机制中的类:

4*****XML

XML是什么?

答:XML即可扩展标记语言(Extensible Markup language),你可以根据自己的需要扩展XML。使用DTD和XML Schema标准化XML结构。XML主要用于从一个系统到另一系统的数据传输,比如企业级应用的客户端与服务端。

问题2:DTD与XML Schema有什么区别?

答:DTD与XML Schema有以下区别:DTD不使用XML编写而XML Schema本身就是xml文件,这意味着XML解析器等已有的XML工具可以用来处理XML Schema。DTD即文档类型描述(Document Type definition)是定义XML文件结构的传统方式。

l 五、

l XML

l 用来干什么

l ?

l 你说了解的

l XML

l 技术及其应用

l ?

l 答:保存配置

l ,

l 站与站之间的交流

l ,

l 都用得到它,

l Xml

l 不仅可以用来描述数据

l ,

l 还可以作为数据的载体。

l 1.

l 用

l xml

l 分离数据

l ,

l 数据单独放在

l xml

l 中

l ,

l 集中精力更好的完成

l html

l 的数据显

l 示和布局,方便维护。

l 2.xml可以作为商业系统之间交换金融信息所使用的语言。

l 3.交换数据,用xml可以减少交换数据的复杂性。

l 4.xml可以用来共享数据.因为xml无关于硬件和软件。5.xml可以充分的利用数据。

l 6.可以用于创建新语言,xml是WML语言的母亲,WML无线标记语言,用于标识运行于手持设备上的internet程序。

l 六、XML和HTML的区别?1.设计上的区别:xml用来存储数据,重在数据本身,HTML用来定义数据,重在数据的显示模式。

l 2.xml可扩展性强

l ,因为他本身就是可扩展性标记语言,可创建个性化的标记语言

l ,提供更多数据操作。

l 3.xml语法比HTML严格。a.

l 起始标签和结束标签要匹配,b.嵌套标签不能互相嵌套,c.区分大小写。

l 4.xml属性值必须放在引号中,html可有可无。

l 5.xml必须有相应的值

l ,但html可以有不带值的属性名。

l 七、

l XML

l 文件和普通配置文件相比有哪些优点?

l 1.xml是一个标准的技术,在处理上可以更加的统一。

l 2.从对配置文件的后期处理上,对xml的操作的api

l 更多,更方面,而文本文件不是特别

l 的方便。

l 3. xml可以有定义语法的dtd

l 文件,这样读取配置信息的时候,可以先根据dtd检查当前的

l xml配置文件是否语法正确,而文本文件没有办法检查语法。(对于配置文件非常关键)

l 4.xml文件可以非常方便的转换成其他格式的文件,而文本文件不可以。

l 5 xml利用xslt可以有更好的显示效果,而文本文件很难做到。

l 6.xml可以非常方便的搜索其中的一些配置信息,试想如果配置文件很大,这个优点是很明显的,而文本文件则不太方便。

l 7.xml可以携带更多、更丰富的的配置信息,而文本文件不容易做到。

XML和json的优缺点xml的优点

(1)格式统一

(2)容易与其他系统进行远程交互,数据共享比较方便

xml的缺点

(1)xml文件庞大,文件格式复杂,传输占带宽

(2)服务器和客户端都需要花费大量代码来解析xml,导致服务器和客户端代码变得异常复杂且不易维护

(3)客户端和服务端解析xml花费较多的资源和时间

json的优点

(1)数据格式比较简单,易于读写,格式是压缩的,占用带宽小

(2)易于解析,包括JavaScript可以通过简单的通过eval_r()进行json数据的读取

json的缺点

(1)没有xml那么通用

(2)json格式目前还在推广阶段

<1>.XML的优点

A.格式统一,符合标准;

B.容易与其他系统进行远程交互,数据共享比较方便。

<2>.XML的缺点

A.XML文件庞大,文件格式复杂,传输占带宽;

B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;

C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;

D.服务器端和客户端解析XML花费较多的资源和时间。

(2).JSON的优缺点

<1>.JSON的优点:

A.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;

B.易于解析,客户端JavaScript可以简单的通过eval_r()进行JSON数据的读取;

C.支持多种语言,包括ActionScript, C, C#, ColdFusion,Java, javascript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;

D.在php世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取;

E.因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。

<2>.JSON的缺点

A.没有XML格式这么推广的深入人心和喜用广泛,没有XML那么通用性;

B.JSON格式目前在Web Service中推广还属于初级阶段。

3、你在项目中用到了xml技术的哪些方面?是如何实现的?

答:用到了数据存贮,系统配置两方面。在做数据交换平台时,将数据源的数据封装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。

在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。

毕竟XML方面的知识不是几句话就能说清楚的,在java的面试过程中,经常是会让被面试说说DOM和SAX之间的区别,适合的场合。

6****JDBC

Java 原生JDBC操作数据库,是比较常见的面试题,所以一定要牢牢掌握住。

记住一般步骤,就好写代码了。

1、Class.forName()加载数据库连接驱动。

2、DriverManager.getConnection()获取数据连接对象。

3、根据SQL语句获取会话对象,会话对象一般用PreparedStatement类型,conn.prepareStatement(),注意方法名和类名不一样哟。

4、执行SQL处理结果集,执行SQL前如果有参数值就设置参数值setXXX()。

5、关闭结果集、关闭会话、关闭连接。

JDBC是如何实现Java程序和JDBC驱动的松耦合的?

JDBC API使用Java的反射机制来实现Java程序和JDBC驱动的松耦合。随便看一个简单的JDBC示例,你会发现所有操作都是通过JDBC接口完成的,而驱动只有在通过Class.forName反射机制来加载的时候才会出现。

我觉得这是Java核心库里反射机制的最佳实践之一,它使得应用程序和驱动程序之间进行了隔离,让迁移数据库的工作变得更简单。在这里可以看到更多JDBC的使用示例。

什么是JDBC连接,在Java中如何创建一个JDBC连接?

JDBC连接是和数据库服务器建立的一个会话。你可以想像成是一个和数据库的Socket连接。

创建JDBC连接很简单,只需要两步:

A. 注册并加载驱动:使用Class.forName(),驱动类就会注册到DriverManager里面并加载到内存里。B.用DriverManager获取连接对象:调用DriverManager.getConnnection()方法并传入数据库连接的URL,用户名及密码,就能获取到连接对象。

相对于Statement,PreparedStatement的优点是什么?

它和Statement相比优点在于:

PreparedStatement有助于防止SQL注入,因为它会自动对特殊字符转义。

PreparedStatement可以用来进行动态查询。

PreparedStatement执行更快。尤其当你重用它或者使用它的拼量查询接口执行多条语句时。

使用PreparedStatement的setter方法更容易写出面向对象的代码,而Statement的话,我们得拼接字符串来生成查询语句。如果参数太多了,字符串拼接看起来会非常丑陋并且容易出错。

什么是JDBC的最佳实践?

下面列举了其中的一些:

数据库资源是非常昂贵的,用完了应该尽快关闭它。Connection, Statement, ResultSet等JDBC对象都有close方法,调用它就好了。

养成在代码中显式关闭掉ResultSet,Statement,Connection的习惯,如果你用的是连接池的话,连接用完后会放回池里,但是没有关闭的ResultSet和Statement就会造成资源泄漏了。

在finally块中关闭资源,保证即便出了异常也能正常关闭。

大量类似的查询应当使用批处理完成。

尽量使用PreparedStatement而不是Statement,以避免SQL注入,同时还能通过预编译和缓存机制提升执行的效率。

如果你要将大量数据读入到ResultSet中,应该合理的设置fetchSize以便提升性能。

你用的数据库可能没有支持所有的隔离级别,用之前先仔细确认下。

数据库隔离级别越高性能越差,确保你的数据库连接设置的隔离级别是最优的。

如果在WEB程序中创建数据库连接,最好通过JNDI使用JDBC的数据源,这样可以对连接进行重用。

如果你需要长时间对ResultSet进行操作的话,尽量使用离线的RowSet。

JDBC的DataSource是什么,有什么好处?

DataSource即数据源,它是定义在javax.sql中的一个接口,跟DriverManager相比,它的功能要更强大。我们可以用它来创建数据库连接,当然驱动的实现类会实际去完成这个工作。除了能创建连接外,它还提供了如下的特性:

缓存PreparedStatement以便更快的执行

可以设置连接超时时间

提供日志记录的功能

ResultSet大小的最大阈值设置

通过JNDI的支持,可以为servlet容器提供连接池的功能

7*****泛型

1.Java中的泛型是什么 ? 使用泛型的好处是什么?

这是在各种Java泛型面试中,一开场你就会被问到的问题中的一个,主要集中在初级和中级面试中。那些拥有Java1.4或更早版本的开发背景的人 都知道,在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。它提供了编译期的类型安全,确保你只能把正确类型的对象放入 集合中,避免了在运行时出现ClassCastException

什么是泛型中的限定通配符和非限定通配符 ?

这是另一个非常流行的Java泛型面试题。限定通配符对类型进行了限制。有两种限定通配符,一种是 extends T>它通过确保类型必须是T的子类来设定类型的上界,另一种是 super T>它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面>表 示了非限定通配符,因为>可以用任意类型来替代。更多信息请参阅我的文章泛型中限定通配符和非限定通配符之间的区别。

初步了解泛型的来由

在没有泛型之前,一旦吧一个对象“丢进”Java集合中,集合就会忘记对象的类型,把所有的对象都当成是Object类型处理。当程序从集合中取出对象之后,就要进行强制类型转换,这种强制类型转换不仅代码臃肿还容易引起ClassCastException异常。

在JDK1.5之后,Java引用了“参数化类型(parameterized type)”的概念,允许我们在创建集合是指定集合元素的类型。如List,这表明该List只能保存字符串类型的对象。Java的参数化类型被称为泛型(Generic)

8*****Socket

一,网络编程中两个主要的问题

一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输。

在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机。

而TCP层则提供面向应用的可靠(tcp)的或非可靠(UDP)的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。

目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务器等待客户提出请求并予以响应。客户则在需要服务时向服务器提出申请。服务器一般作为守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的客户也能及时得到服务。

二,两类传输协议:TCP;UDP

TCP是Tranfer Control Protocol的 简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。

UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。

比较:

UDP:1,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。

2,UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。

3,UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方

TCP:1,面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接

时间。

2,TCP传输数据大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的

数据。

3,TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。

应用:

1,TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。

2,UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

三,基于Socket的java网络编程

1,什么是Socket

网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。

但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

2,Socket通讯的过程

Server端Listen(监听)某个端口是否有连接请求,Client端向Server端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client端都可以通过Send,Write等方法与对方通信。

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:

(1)创建Socket;

(2)打开连接到Socket的输入/出流;

(3)按照一定的协议对Socket进行读/写操作;

(4)关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)

1、如何新建一个客户端Socket服务

//new出一个对象即可//指定链接的IP地址,端口号

Socket socket =new Socket("www.baidu.com",1000);

1

2

3

1

2

3

2、写出Socket连接服务器与服务器进行交互的代码

//新建Socket服务,将数据发送给服务端

Socket socket =new Socket("www.baidu.com",1000);//得到写入流

BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//写入数据bw.write("i love you");bw.newLine();bw.close();//从服务端读取数据

BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream));String line =null;

StringBuilder sb=new StringBuilder();while(line=((br.readLine())!=null)

{

sb.append(line);

}

System.out.println(sb.toString());

3、什么时候会关闭Socket服务

有4种情况会关闭,分为手动或者系统。

手动的方式:

1、socket.close();

2、InputStream.close();

系统:

1、程序退出

2、新的Socket请求覆盖

总结,一般而言,手动的话容易造成网络被占用,因为需要一段时间的缓冲,不然会占用大量的网络资源

4、如何使用Socket.connect();的方法连接服务器

4、5、网络的超时

我们要知道,我们客户端与服务端的连接的异常会出现在两个方面,第一个是发送请求,一个是接收服务端传送回来的信息

那么网络延时也是发生在这个时候,第一个是请求超时,第二是获取数据超时,为了提供用户的体验,我们可以设置在发送请求的时候设置超时时间,方法是setSOTimeout(time);

ps:SO是Socket的缩写

5、5、网络的超时

我们要知道,我们客户端与服务端的连接的异常会出现在两个方面,第一个是发送请求,一个是接收服务端传送回来的信息

那么网络延时也是发生在这个时候,第一个是请求超时,第二是获取数据超时,为了提供用户的体验,我们可以设置在发送请求的时候设置超时时间,方法是setSOTimeout(time);

ps:SO是Socket的缩写

6、5、网络的超时

我们要知道,我们客户端与服务端的连接的异常会出现在两个方面,第一个是发送请求,一个是接收服务端传送回来的信息

那么网络延时也是发生在这个时候,第一个是请求超时,第二是获取数据超时,为了提供用户的体验,我们可以设置在发送请求的时候设置超时时间,方法是setSOTimeout(time);

ps:SO是Socket的缩写

8.网络编程中设计并发服务器,使用多进程与多线程,请问有什么区别?

答案一:

1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。

2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。两者都可以提高程序的并发度,提高程序运行效率和响应时间。

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

答案二:

根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:

1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。

2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。

3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内

.多线程

1.java中实现线程的方式

在java中实现线程有两种方式:继承Thread类,实现Runable接口,一个java main程序默认会开启两个线程一个是主线程,一个垃圾回收线程。

2.线程不安全与安全:

多个线程访问同一个资源,导致结果和期望值不同,我们就说它是非线程安全的(线程不安全),反之我们就说它是线程安全的。

了解:

a.多个线程访问同一个资源(这里的资源通常指的是全局变量或者静态变量),如果每次运行结果和单线程运行的结果是一样的,就是线程安

全的。

b.线程安全问题都是由全局变量及静态变量引起的。

c.若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;

若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

3.线程的状态

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

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

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

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

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

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

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

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

了解:

1、线程的实现有两种方式,一是继承Thread类,二是实现Runnable接口,但不管怎样,当我们new了这个对象后,线程就进入了初始

状态;

2、当该对象调用了start()方法,就进入可运行状态;

3、进入可运行状态后,当该对象被操作系统选中,获得CPU时间片就会进入运行状态;

4、进入运行状态后情况就比较复杂了

4.1、run()方法或main()方法结束后,线程就进入终止状态;

4.2、当线程调用了自身的sleep()方法或其他线程的join()方法,就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资

源)。当sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片;

4.3、线程调用了yield()方法,意思是放弃当前获得的CPU时间片,回到可运行状态,这时与其他进程处于同等竞争状态,OS有可能会接

着又让这个进程进入运行状态;

4.4、当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被synchronized(同步),获取不到锁标记,将会立即进入锁

池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获

得锁标记后,就转入可运行状态,等待OS分配CPU时间片;

4.5、当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自

动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的

是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后

会进入锁池,等待获取锁标记。

补充:(wait和sleep的区别)

wait时会释放锁资源但sleep不会释放锁资源,wait通常和notify以及notifyAll结合使用,需要notify或者notifyAll对其进行唤醒,sleep通常在指定的时间内自动唤醒。

4.解决线程安全的问题的方案:

a.通过加锁(synchronized)的方式解决线程安全问题

1.synchronized 方法

2.synchronized 块(同步代码块)

b.避免使用全局变量

c.使用ThreadLocal(参考:http://blog.csdn.net/drifterj/article/details/7782706)

1. 为多线程并发的互斥控制提供了另一种全新的解决思路

2. 通过ThreadLocal为其他模块的API传递参数

5.java线程池 (可参考:http://www.oschina.net/question/565065_86540)

1.减少了创建和销毁线程的次数,

每个线程都可以被重复利用,

可执行多个任务。

2.可以根据系统的承受能力,

调整线程池中线程的数目,

防止因为消耗过多的内存,

而导致服务器宕机

(每个线程需要大约1MB内存,线程开的越多,

消耗的内存也就越大,最后宕机)。

通常我们使用的线程池是实现了ExecutorService的ThreadPoolExecutor。

6.死锁

死锁是因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程

中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等

待,而无法执行的情况。

死锁产生的原因:是由访问共享资源顺序不当所造成的.

简单的说:所谓死锁,是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

7.守护线程

在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)

用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM

一同结束工作。

Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon

Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值