java笔试、面试深度剖析之多线程相关

匿名网友 匿名网友 发布于: 2015-08-30 00:00:00
阅读 144 收藏 0 点赞 0 评论 0

题目:请编写一个多线程程序,实现两个线程,其中一个线程完成对某个对象的int成员变量增加操作,即每次加1;另一个线程完成对该对象成员变量的减操作,即每次减1,同时保证该变量的值不会小于0,不会大于1,该变量的初始值为0.也就是说此int变量只能在0和1之间切换。

增加线程类:IncreaseThread

减少线程类:

操作线程的Main方法类:

结果输出:1 0 1 0 1 0 1 0……..一共有20个1、20个0输出。

假如题目要求发生变化,要求使用四个线程,两个增加线程,两个减少线程实现同样的目的,该怎么做??
有的人可能会天真的认为,只要在MainTest类中再分别添加一个增减和减少的线程就OK了。。那么这样做对嘛?

答案是错误的,这样做得到的结果是完全错误的。那么问题到底出现在哪里呢??
我们不妨做个假设:我们知道thread1和thread3是增加线程、thread2和thread4是减少线程,如果说开始thread2先运行,因为number初始值是0,所以thread2执行到if(number == 0)时放弃CPU,wait在那里,接着thread4抢占到了CUP,因为thread2并没有把number的值改变,number仍然为0,所以thread4执行到if(number == 0)时也wait住了。之后thread1抢占到CPU,因为number=0,所以number执行加1操作变为1,然后调用notify()会唤醒thread2或者thread4中的其中一个(假设是thread2)对number进行减1操作,number变为0,到此为止并没有任何问题,关键的时刻到了,就是:如果继续notify线程thread4 话问题就出现了,number的值就会变为-1。 这样我们就发现了问题的所在:在某个线程被notify醒来之后不应该继续向下执行,而是返回上面再次检查是否满足number的相关条件然后才继续向下执行。所以程序中的if应该改为while。 如下图:

这样修改之后,无论是四个线程八个线程还是10000个线程我们都可以得到如期的结果。
让我们查看相关的JDK文档:
在Object类中wait的三种重载方法:

第一种重载方法:

第二种重载方法,要么过特定时间自动醒来,要么在此段时间当中被被的线程notify醒来。

notify方法:只叫醒一个线程,将其从等待池放入锁池去争夺对象的锁:

notifyAll方法:叫醒等待池中所有的线程,将它们从等待池转移到锁池中共同去争夺对象的锁。

sleep方法,线程睡眠,但是该线程是抱着锁在锁在睡觉,睡觉过程中并不释放对象的锁。

由以上JDK文档可以总结出如下知识点:
线程中的相关知识点:
关于wait、notify、notifyAll以及sleeep方法的关系(重点)
1、所有对象都有锁、都有等待池,因此这些方法都是定义在Object类中
2、如果一个线程调用了某个对象的wait的方法,那么该线程首先必须拥有该对象的锁(换句话说,一个线程如果调了某
个对象的wait方法,那么该wait方法必须要在synchronized中)。
3、如果一个线程调用了某个对象的wait方法,那么该线程就会释放该对象的锁。
4、在java对象中,有两种池(锁池、等待池)。
5、如果一个线程调用了某个对象的wait方法,那么该线程进入了改对象的等待池中(释放锁),如果未来的某一时刻另
外一个线程调用了相同的对象的notify或者notifyAll方法,那么在该等待池中等待的线程就会起来进入该对象的锁
池中,去等待获得该对象的锁,如果获得锁成功后,那么该线程将继续沿着wait方法之后的路径去执行。
6、Thread.sleep(),如果一个线程调用了sleep方法睡眠,那么在睡眠的同时,它不会失去对象的锁的拥有权。

看synchronized关键字相关的实例:

新建两个线程类:

写Main方法:

关于Java中synchronized关键字的作用
1、在某个对象的所有synchronized方法中,在某一时刻,只能有唯一的一个线程去访问这些synchronized方法
2、如果一个方法是synchronized方法,那么该synchronized关键字表示给当前对象上锁(即this)。
若在Main方法中左如下修改怎输出结果和上述结果截然不同:

分析:因为在上述修改中生成了C类的两个对象分别赋值给这两个线程,而synchronized关键字只锁定一个对象。

若在synchronized修身的方法上再添加static修饰的话,那么这个synchronized就不是针对一个对象上锁,而是针对此对象对应的那个类上锁。例如在此例中做相应修改:

如果一个synchronized方法是静态的(static的),那么该synchronized关键字表示给当前对象锁对应的class对象上锁。(每个类不管生成多少对象,其对应的class对象只有一个)。

评论列表
文章目录