wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。
16、为什么要使用线程池
避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。点击学习线程池详解。
17、怎么检测一个线程是否持有对象监视器
但是真正因为wait进入阻塞状态的,是持有对象监视器的线程,这里的对象监视器是jointhread,持有他的是main线程,因为在main线程中执行了join这个同步方法。由于线程a和线程b持有同一个myobject类的对象object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,比如:线程b需要等待线程a执行完了methoda()方法之后,它才能执行methodb()方法。关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,所以在上面的示例中,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。
18、synchronized和ReentrantLock的区别
synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:
(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁
(2)ReentrantLock可以获取各种锁的信息
(3)ReentrantLock可以灵活地实现多路通知
所以synchronized的底层操作含义是先对对象头的锁标志位用lock cmpxchg的方式设置成“锁住“状态,释放锁时,在用lock cmpxchg的方式修改对象头的锁标志位为”释放“状态,写操作都立刻写回主内存。对象,用户可以使用这些对象创建一些场景,然后调用底层的渲染机制。digester底层采用sax(simple api for xml)析xml文件,所以很自然的,对象转换由"事件"驱动,在遍历每个节点时,检查是否有匹配模式,如果有,则执行规则定义的操作,比如创建特定的java对象,或调用特定对象的方法等。
19、ConcurrentHashMap的并发度是什么
在引入线程的操作系统中,不仅进程之间可以并发执行,而且在一个进程中的多个线程之间,亦可并发执行,因而使操作系统具有更好的并发性,从而能更有效地使 用系统资源和提高系统吞吐量。-parallel methods|tests 如果指定, 设置运行测试时如何使用并发线程的默认机制.如果不设置,默认机制是完全不使用并发线程。-t 并发线程数量,并发主机数量 指定最大并发线程数量和并发主机数量, 默认数量为100,10。
20、ReadWriteLock是什么
首先明确一下,不是说ReentrantLock不好,只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。
因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。

21、FutureTask是什么
这个其实前面有提到过,FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。当然,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。
22、Linux环境下如何查找哪个线程使用CPU最长
这是一个比较偏实践的问题,这种问题我觉得挺有意义的。可以这么做:
(1)获取项目的pid,jps或者ps -ef | grep java,这个前面有讲过
(2)top -H -p pid,顺序不能改变
使用nsthread在进行多线程开发过程中操作比较简单,但是要控制线程执行顺序并不容易(前面万不得已采用了休眠的方法),另外在这个过程中如果打印线程会发现循环几次就创建了几个线程,这在实际开发过程中是不得不考虑的问题,因为每个线程的创建也是相当占用系统开销的。cpython的线程是操作系统的原生线程。这样就可以打印出当前的项目,每条线程占用cpu时间的百分比。
使用nsthread在进行多线程开发过程中操作比较简单,但是要控制线程执行顺序并不容易(前面万不得已采用了休眠的方法),另外在这个过程中如果打印线程会发现循环几次就创建了几个线程,这在实际开发过程中是不得不考虑的问题,因为每个线程的创建也是相当占用系统开销的。1.不能操作线程和线程api(线程api指非线程对象的方法如notify,wait等),2.不能操作awt,3.不能实现服务器功能,4.不能对静态属生存取,5.不能使用io操作直接存取文件系统,6.不能加载本地库.,7.不能将this作为变量和返回,8.不能循环调用。标准的代码优化技巧包括:消除循环、函数代码就地扩展、公共子表达式消除、改进寄存器分配、省略运行时对数据边界的检查、循环不变量代码移动、操作符长度削减(把指数操作转变为乘法操作,把乘法操作转变为移位操作或加法操作)等。
它是一个静态方法而且只保证当前线程放弃cpu占用而不能保证使其它线程一定能占用cpu,执行yield()的线程有可能在进入到暂停状态后马上又被执行。它是一个静态方法而且只保证当前线程放弃 cpu 占用而不能保证使其它线程一定能占用 cpu,执行 yield ()的线程有可能在进入到暂停状态后马上又被执行。使用"top -h -p pid"+"jps pid"可以很容易地找到某条占用cpu高的线程的线程堆栈,从而定位占用cpu高的原因,一般是因为不当的代码操作导致了死循环。
23、Java编程写一个会导致死锁的程序
第一次看到这个题目,觉得这是一个非常好的问题。很都知道死锁是怎么一回事儿:线程A和线程B相互等待对方持有的锁导致程序无限死循环下去。当然也仅限于此了,问一下怎么写一个死锁的程序就不知道了,这种情况说白了就是不懂什么是死锁,懂一个理论就完事儿了,实践中碰到死锁的问题基本上是看不出来的。
真正理解什么是死锁,这个问题其实不难,几个步骤:
1)两个线程里面分别持有两个Object对象:lock1和lock2。这两个lock作为同步代码块的锁;
(3)线程2的run()方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁,线程2肯定是要等待线程1释放lock1的对象锁的。3、当一个线程已经获取该object对象的synchronized(this)同步方法(或代码块)的锁时,该对象被类锁修饰的同步方法(或代码块)仍然可以被其他线程在同一cpu周期内获取,两种锁不存在资源竞争情况。(3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的。
(3)线程2的run()方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁,线程2肯定是要等待线程1释放lock1的对象锁的。在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放对象锁,进入对象的等待池。3、 在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放对象锁,进入对象的等待池。
这样,线程1"睡觉"睡完,线程2已经获取了lock2的对象锁了,线程1此时尝试获取lock2的对象锁,便被阻塞,此时一个死锁就形成了。代码就不写了,占的篇幅有点多,Java多线程7:死锁这篇文章里面有,就是上面步骤的代码实现。
点击提供了一个死锁的案例。
24、怎么唤醒一个阻塞的线程
如果线程因为调用wait、sleep、或者join方法而导致的阻塞,你可以中断线程,并且通过抛出interruptedexception来唤醒它。join()方法不能被中断,如果中断调用join()方法的线程,方法将抛出interruptedexception异常。上面的代码会阻塞线程,所以在android里边不能在主线程中调用它,否则会抛出networkonmainthreadexception异常,如果你想非要调用这个方法,必须在后台线程中调用它。
25、不可变对象对多线程有什么帮助
前面有提到过的一个问题,不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。
26、什么是多线程的上下文切换
多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。
27、如果你提交任务时,线程池队列已满,这时会发生什么
这里区分一下:
1、构造一个固定线程数目的线程池,配置的corepoolsize与maximumpoolsize大小相同,同时使用了一个无界linkedblockingqueue存放阻塞任务,因此多余的任务将存在再阻塞队列,不会由rejectedexecutionhandler处理。1.blockingcollection 与经典的阻塞队列数据结构类似,能够适用于多个任务添加和删除数据,提供阻塞和限界能力。信号量的发送数据函数,并不会真的把数据存储到队列数据存储区中去,而且也没有数据存储区,它仅仅记录数据项的个数,而且这个发送函数没有阻塞时间,也就是一旦满了,任务就不能再向队列尝试添加数据,如果有任务尝试添加,会直接返回一个满列错误,而不会进入阻塞以便等到队列有空间时再将数据添加到队列中区。
它使用了一个无限队列来保存需要执行的任务,而线程的数量则是通过构造函数传入,如果没有向构造函数中传入希望的线程数量,那么当前计算机可用的cpu数量会被设置为线程数量作为默认值。总结起来,也即是说,当有新的任务要处理时,先看线程池中的线程数量是否大于corepoolsize,再看缓冲队列workqueue是否满,最后看线程池中的线程数量是否大于maximumpoolsize。每个线程任务,执行时会先睡眠3秒,保证提交10任务时,线程数目被占用完,再提交30任务时,阻塞队列被占用完,,这样提交第41个任务是,会交给customrejectedexecutionhandler 异常处理类来处理。
28、Java中用到的线程调度算法是什么
java线程调度使用“抢占式”,可以通过设置线程优先级来给某些线程多分配一点执行时间。由于java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到cpu控制权的情况,为了让某些优先级比较低的线程也能获取到cpu控制权,可以使用thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡cpu控制权的一种操作。然而,一个处于备用状态的线程在真正被执行以前,有可能被更高优先级的线程抢占。
29、Thread.sleep(0)的作用是什么
(10)系统如何调度线程:一个抢先的操作系统必须使用某种算法来决定何时调用哪个线程以及调度多长时间。请注意,这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。一个线程可以调用pthread_mutex_lock获得mutex(操作系统调度执行此线程),如果这时另一个线程已经调用pthread_mutex_lock获得了该mutex,则当前线程需要挂起等待(操作系统仍可能在调度执行此线程),(操作系统调度切换运行另一线程)直到另一个线程调用pthread_mutex_unlock释放mutex,(另一线程释放mutex时操作系统调度运行此线程)当前线程被唤醒(即从此线程上次执行的地方继续执行),才能获得该mutex并继续执行。
30、什么是自旋
很多synchronized里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然synchronized里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞,而是在synchronized的边界做忙循环,这就是自旋。如果做了多次忙循环发现还没有获得锁,再阻塞,这样可能是一种更好的策略。
31、什么是Java内存模型
Java内存模型定义了一种多线程访问Java内存的规范。Java内存模型要完整讲不是这里几句话能说清楚的,我简单总结一下Java内存模型的几部分内容:
1)Java内存模型将内存分为了主内存和工作内存。类的状态,也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝,运行自己线程代码的时候,用到这些变量,操作的都是自己工作内存中的那一份。程代码执行完毕之后,会将最新的值更新到主内存中去
2)定义了几个原子操作,用于操作主内存和工作内存中的变量
3)定义了volatile变量的使用规则
4)happens-before,即先行发生原则,定义了操作A必然先行发生于操作B的一些规则,比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的代码、一个释放锁unlock的动作一定先行发生于后面对于同一个锁进行锁定lock的动作等等,只要符合这些规则,则不需要额外做同步措施,如果某段代码不符合所有的happens-before规则,则这段代码一定是线程非安全的
32、什么是CAS
CAS,全称为Compare and Swap,即比较-替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回truejava面试题及答案,否则什么都不做并返回false。当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功。更多CAS详情请点击学习。
33、什么是乐观锁和悲观锁
1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。
乐观锁,顾名思义,对并发事务持乐观态度(认为对数据的并发操作不会经常性的发生),通过更加宽松的锁机制来解决由于悲观锁排他性的数据访问对系统性能造成的严重影响。读线程从共享资源中读数据,同时写线程需要向共享资源中写数据,当多个读、写线程共用一个资源(缓冲区)的时候,据需要使用诸如critical section或者互斥量的排他性锁(独占锁)来控制资源的访问,但是针对读操作较为频繁,写操作相对较少的情况下,使用独占锁是很不划算的,需要耗费更多的时间和其他资源。读线程从共享资源中读数据,同时写线程需要向共享资源中写数据,当多个读、写线程共用一个资源(缓冲区)的时候,据需要使用诸如criticalsection或者互斥量的排他性锁(独占锁)来控制资源的访问,但是针对读操作较为频繁,写操作相对较少的情况下,使用独占锁是很不划算的,需要耗费更多的时间和其他资源。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-115504-2.html
死亡和无政府主义
唯一错的是这个社会已经形成的这个风气
作用上美国的独立战争近似于抗美援朝战争
不就是个死吗