collection和collections有什么区别?
collection是一个接口集合,它提供了一系列对集合对象进行操作的通用接口方法,设计这个接口的目的是为各种不同的集合提供一个统一的接口方法。
collections是集合类的一个包装类,它提供了一系列静态的方法和对集合进行索引、排序、线程安全化等操作,而大多数方法是用来进行处理线性表。collections不能被实例化,它如同一个工具类,服务于collection框架,当调用collection类中的方法时,如果对应的collection对象为null,则会抛出nullponiterException.
线程是什么?进程是什么?它们有什么区别和联系?
线程是程序执行过程中,可以执行一段程序代码的执行单元,在java语言中,线程的状态有:运行,就绪,挂起,结束。
进程是一段正在执行的程序,线程有时也被称为轻量级的进程,一个进程中通常有多个线程,各个线程在运行过程中,会共享程序的内存空间(代码段、数据段、堆空间)以及进程级的资源(比如打开的文件),但是各个线程之间都有自己的栈空间。
在操作系统的层面上,进程是程序运行的基本单位,一个进程中通常都有多个线程在互不影响的并发执行。那么为什么要使用多线程呢?
使用多线程的优点是:
1.使用多线程可以减少程序的响应时间。
2.创建线程比进程的开销更小。
3.多cpu和多核本来就可以运行多线程。
4.使用多线程可以简化程序,便于程序的理解和维护,可以将复杂的问题转化为简单的多线程来执行。
同步是什么?异步是什么?
在多线程环境中,经常会遇到资源共享的问题。即多个线程调用同一个资源时,它们会按某种先后顺序以确保每个时段只有一个线程调用资源,否则,将会出现无法预料的结果。在这种情况下就必须对数据进行同步,例如多个线程同时对同一数据进行写操作,线程A需要使用某个资源时,而这个资源正在被B使用中,只有当线程B结束对资源的使用后,线程A才能使用该资源,同步机制可以保证资源的安全。
想要实现同步操作,就必须获得对象的锁,锁可以确保每个时段只有一个线程进入临界区(访问互斥资源的代码块),并且在这个锁释放之前,其他线程不能进入临界区,如果还有其他线程想要获得该对象的锁,只能进入等待队列等待,只有当拥有该对象的锁的线程退出临界区时,等待区内运行级别最高的锁才能进入临界区,使用共享的代码。
java为同步机制提供了语言级的支持,可以通过直接调用sychronized关键字来实现同步,但同步也不是“万金油”。它是以很大的系统开销作为代价,使用同步有时候会造成死锁。所以同步控制并非越多越好,应避免无谓的使用同步。实现同步的方法有两种:一种是通过代码块实现同步,另一种是通过调用方法实现同步。
异步类似于非阻塞,由于每个线程都包含运行时所需的数据或方法,所以在进行输入输出处理时,不需要关心其他线程的行为和状态,也不需要等输入输出处理完成后在返回,如果程序在对象上调用一个非常耗时间的方法返回时,而且也不需要等待返回,这是就需要异步编程,异步可以提高程序的效率。
举个生活中的例子,同步就是我叫你吃饭,你马上和我一起走去吃饭,而异步是我叫你吃饭,你可以先在一起吃,也可以之后再吃,也可以不吃。
如何实现多线程?
java虚拟机允许程序并发的运行多个线程,那么如何实现多线程呢,主要有以下3种方法:
1.继承Thread类,重写run()方法。
thread类本质上也是Runnable的一个实例,它是线程的实例,并且开启线程的唯一方法就是调用thread类的start()方法,start()方法是一个native(本地)方法,它将启动一个新线程并执行run()方法(thread类的run方法是一个空方法),这种方式通过自定义直接extend Thread类,并重写run方法,就可以创建新的线程并使用自定义的run方法,需要注意的是,开启线程并不是直接执行多线程的代码,而是将多线程设置为可运行太,具体什么时候开启多线程有操作系统控制。
2.实现Runnable接口,并且实现该接口的run()方法。
(1)自定义类并实现Runnable接口,实现该接口的run()方法
(2)创建一个对象thread,用实现Runnable的对象作为参数实例化Thread
(3)调用thread的start()方法。
其实,不管通过多线程还是实现runnable接口,都是调用thread的API来处理多线程。
3.实现callable接口,重写call()方法。
callable接口是execute框架的一个功能类,它的功能和Runnable类似,但是比runnanble的功能更加强大,主要表现在以下三点:
(1)callabble在任务结束后会有一个返回值,而runnable没有。
(2)callable中的call方法可以抛出异常,而runnable中的run()方法不能抛出异常。
(3)使用callable会产生一个future对象,这个对象是异步计算的结果,它提供了检查计算是否完成的方法,线程属于异步计算模型,所以它不能从其他线程中获得函数的返回值,在这种情况下,就可以使用future来监视目标线程调用call方法的情况,当调用future中的get()方法时,线程会处于阻塞状态,直到call结束返回结果。
以上3种方法,前两种执行完都没有返回值,只有最后一种是带返回值的。当需要实现多线程时,推荐使用的是实现runnable接口,原因主要是:首先,thread类提供了很多方法供派生类调用或重写,只有run()方法是必须重写的,run方法中实现线程的主要功能。这当然是实现Runnabel接口的必需的方法,其次,很多java开发人员认为只有加强或者修改时才会发生继承,所以thread和实现runnnable接口的效果一样,在这种该情况下最好通过实现runnanble接口来实现多线程。
多线程同步的实现方法有哪些?
当使用多线程访问同一个资源时,非常容易出现线程安全的问题(例如,当多个线程同时对一个线程修改时,会出现一些线程对数据的修改丢失),在这种情况下就需要对数据进行同步,在java语言中,提供了3种实现多线程同步的方法。
<1>synchronized关键字
在java语言中,每个对象都有一个对象锁与之关联,该锁表明对象在任何时候只能被一个线程所拥有,当一个线程需要调用一段synchronized代码段时,需要获取这个代码段的锁,才能继续执行,执行完成后,释放锁。
在java语言中,sychronized关键字主要有两种用法(synchronized方法和synchronized块),也可以用来修饰可静态方法和实例等,但是会影响程序的运行效率。
(1)在方法声明前加上synchronized关键字,示例如下:
public synchronized void MutiThreadAccess()
在上面方法中,把需要共享的资源放到MutiThreadAccess()中,就能保证这个方法在同一时刻R只能被一个线程访问,从而保证了多线程访问的安全性。然而,当一个方法的方法体非常大时,使用synchronized方法会非常影响效率,为了解决这一问题,java提供了synchroonized块。
(2)synchronized块,synchronized既可以把任意一段代码设置为synchronized,也可以指定对象上锁,有非常高的灵活性。代码示例如下:
synchronized(syncObject){
//访问syncObject代码
}
2.wait()与notify()方法。
当使用synchronized修饰共享资源时,如果一个线程A1在执行一个synchronized代码,另外一个线程也要执行同一个对象的同一段sychronized代码,线程a2要等到线程a1执行完成后才能继续执行,这时就可以调用对象的wait()方法和notify()方法。
在synchronized代码被执行过程中,线程可以调用对象的wait()方法,释放对象锁,进入等待状态,并且可以调用对象的notify()方法或notifyAll()方法,通知线程获取对象锁。notify()方法只唤醒一个线程(等待队列中的第一个),并且允许获得对象锁,notifyAll()方法会唤醒全部的线程允许它们获得锁(不保证所有的线程获得锁,让它们去竞争)。
<2>Lock()方法。
jdk5提供了lock接口以及它的实现类Reentrantlock(重入锁),Lock也可以用来实现多线程的同步,具体而言,主要通过以下方法进行实现:
(1)Lock(),通过阻塞线程来实现同步,如果获取到锁,会立即返回,如果锁被其他线程拥有,会一直等待,知道获取到锁。
(2)tryLock().只是尝试性的去获取一下锁。如果获取到锁,返回true,没有获取到锁,则返回false。
(3)tryLock(long timeout,timeunit unit)。如果获取到锁,立即返回,如果没有获取到锁,等到参数指定的时间单元,在等待过程中,如果获取到了锁,返回true,否则,返回false.
(4)lockInterruptibly(),如果获取了锁,立即返回,如果没有获取锁,当前线程处于休眠状态,直到获得锁,或者当前线程被别的线程中断。它与lock方法最大的区别是如果lock()方法获取不到锁,会一直处于阻塞状态,且会忽略Interrupt方法。
sleep()与wait()方法有什么区别?
sleep()是用来将线程暂停执行的程序,wait()也是用来将线程暂停执行的程序,例如,当线程交互时,如果线程对一个同步对象x发出一个wait()调用请求,那么该线程会暂停执行,
具体而言:sleep()与wait()方法的区别主要表示在一下几个方面:
1.原理不同,sleep()方法是Thread类的静态方法,是线程用来控制自身流程的,他会使线程暂停执行一段时间,而把执行机会让给其他线程,等到计时时间一到,此线程会自动苏醒。例如:当线程执行报时功能时,每一秒钟打印出一个时间,那么此时就需要在打印方法前面加上一个sleep()方法,以便让自己每隔1s执行一次,该过程如同闹钟一样。而wait()方法是object()方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,直到其他线程调用notify()方法(或notifyAll方法) 时才“醒”来,不过开发人员也可以给它指定一个时间,自动醒来,与wait()方法配套的方法还有还有notify和notifyAll()。
2.对锁的处理机制不同,由于sleep()方法的主要作用是让线程暂停执行一段时间,时间一到则自动恢复,不涉及线程间的通信,因此调用sleep()方法并不会释放锁,从而使线程所在对象中的其他synchronized数据可被别的线程使用。举个例子来说:比如说小明拿遥控器的期间,可以用自己的sleep方法每隔10min调一次频道,而在这10min里,遥控器还在他的手上。
3.使用区域不同,由于wait()方法的特殊意义,因此它必须放在同步控制方法或者同步语句块中使用,而sleep()方法则可以放在任何地方使用。
sleep()方法必须捕获异常,而wait()、notify()以及notifyall()不需要捕获异常,在sleep的过程中,有可能被其他对象调用它的interrupt(),产生InterruptedException异常。
由于sleep不会释放“锁标志”,容易导致死锁问题的发生,因此,一般情况下,不推荐使用sleep方法,而推荐使用wait()方法。
如何结束一个线程?
在java语言中,可以通过调用stop()方法或者suspend()方法来结束线程。当用thread.stop()来终止一个线程时,会停止这个线程的一切监视资源,而被这些监视资源监视的对象如果存在“不一致”,就会被其他线程所发现,从而会造成输出结果不一定是原设定的。使用suspend()方法停止线程,则可能会发生死锁(死锁是两个或者两个以上的线程在资源竞争中造成相互等待的问题,如果没有外界帮助,两个线程都无法执行)。由于使用suspend时不需要释放锁,所以会造成一个问题:如果调用suspend()挂起一个带锁的线程,只有当锁恢复是才会释放。如果调用suspend(),其他线程想要获得一个一样的锁,这种下就会造成死锁。所以以上两种不安全的终止线程的方法都不推荐。
那么,如何终止一个线程呢?推荐让线程自行结束进入dead状态,即线程执行完run()方法,也就是说,使用一种方式让线程结束执run()方法。具体而言,可以在线程加一个flag标志来控制循环是否执行,从而使线程离run()方法的执行结束线程。实例如下
public class Thread implement(){
public static void main String(args[]){
public void stop{
flag=false;
}
public void run(){
while(flag);
//dosomething
}
}
}
在上述实例中,调用thread的stop方法虽然能够终止线程,但是同样存在问题,当线程处于非运行状态时(当sleep()方法被调用或当wait()方法被调用或的那个I/O阻塞时),上面介绍的方法就不可用了。
此时可以用interrupt()方法来打破阻塞的情况,当interruot()方法被调用时,会抛出interruptException()异常,通过在run()中捕获到该异常来让线程安全退出。
synchronized()和lock()有什么区别?
java提供了两种锁机制来实现资源的同步,synchronized()和lock(),其中synchronized()使用的事object对象的wait();notify();notifyAll(),而lock()是通过codition来进行线程之间的调度,来完成synchronized()实现的功能
具体而言,synchronized()和lock()主要有以下几点不同:
(1)用法不同。在需要同步的对象前面加上synchronized,synchronized也可以放在方法,代码块中,被锁定的对象放在括号中,而lock()必须显示的指定起始位置和终止位置。synchronized是托管于JVM虚拟机去执行,而lock()是通过代码实现的,lock()比synchronized有着更明确的线程语义。
(2)性能不同。在jdk5中增加了一个lock接口的实现ReeTrantLock(),它不仅拥有和synchronized相同的并发性和内存语义,还多了锁投票、等候、定时锁、中断锁等。就功能方面来说:当资源竞争中等时,synchronized的性能和lock()类似,当资源竞争很激烈时,synchronized的性能就会急速下降,而lock的性能基本保持不变。
(3)锁机制不同。synchronized()获得锁和释放锁都是在块结构中,当获取多个锁时,必须以相反的顺序释放,而且是自动释放,不会因为发生异常而不释放锁造成死锁,而lock则需要开发人员手动释放,必须在final中释放,否则会造成死锁。同时,lock()比synchronized()有更强大的功能,它的trylock()方法可以用非阻塞的方式获得锁。
虽然synchronized和lock都可以实现多线程的同步,但是最好不要同时使用这两种锁,因为这两种锁的机制是不同的,它们都是独立运行的,可以看作两种不同的锁,在运行时互不影响,但是运行结果可能不同。
什么是守护线程?
java提供了两种线程:守护线程和用户线程,守护线程又被称为“服务进程”、“精灵线程”、“后台线程”,是指程序运行时在后台提供通常服务的线程,它不是程序不可或缺的部分,通俗来讲,它是jvm虚拟机中所有非守护线程的“保姆”。
用户线程和守护线程几乎一样,唯一的不同之处是只要有一个用户线程没有结束,程序就不能结束,所有非守护线程结束了,程序也就结束了,Jvm也就结束了,原因是需要被守护的线程没有了,守护线程也没有工作了,所以程序就结束了,程序会“杀死”守护线程。通俗来讲,只要程序中有一个非守护线程存在,程序就不会结束。
在java语言中,守护线程一般拥有较低的优先级,它并非只由jvm提供,用户在编写程序时自行设定守护线程,设定的方法是,在start()开启线程之前调用对象的setDaemon(ture)方法,如果参数设置为false,表示设置为用户进程模式。需要注意的是,如果一个守护线程中产生了其他线程,新的线程默认是守护线程,用户线程也是这样的。
join方法有什么作用?
在java语言中,join方法的作用就是使调用该方法的线程在执行完run()方法之后,还能执行join()方法。简单来说,就是合并两个线程,用来实现同步的功能。具体来说,线程A调用线程A的join方法等待线程A的结束。比如线程A调用线程A的join(2000)等待线程A的结束,只等待2s。