public class use {
public static void main(String[] args) {
book b1 = new book();
myThread1 t1 = new myThread1(b1);
myThread2 t2 = new myThread2(b1);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
// 线程类1
class myThread1 extends Thread {
book b;
public myThread1(book b) {
super();
this.b = b;
}
@Override
public void run() {
while(true){
b.addbook();
try {
System.out.println("t1睡觉了哦");
Thread.sleep(5000);
System.out.println("t1醒了呐");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//线程类2
class myThread2 extends Thread {
book b;
public myThread2(book b){
this.b = b;
}
@Override
public void run() {
while(true){
b.delbook();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//资源类
class book{
public int number;
//增加数据
public synchronized void addbook(){
System.out.println(Thread.currentThread().getName()+"------"+(++number));;
}
//减少数据
public synchronized void delbook(){
System.out.println(Thread.currentThread().getName()+"------"+(--number));;
}
}
上面这个程序,分别对增加书籍与减少书籍方法使用锁。它们使用的是book的内置锁。
在t1调用sleep之前与之后输出语句,这样,如果运行结果没有t2的运行输出就说明没有释放锁,
如果有输出就说明释放了锁。
这个结果很明显是释放了锁呀,为什么大家都说sleep不释放锁?
其实这混淆了一个概念:
锁是加在实例对象上的,不论是线程实例的sleep还是Object实例的wait,都指的是对象实例。
wait是所有实例都有的方法,sleep只能是线程实例拥有。
例中的myThread1的实例对象t1属于对象实例的特殊情况的线程实例
wait应用在Object对象上,那么线程实例对象也有wait
更改上面的程序:
在myThread2中添加t1实例,不断打印myThread1的实例t1的线程状态
package com.baidu.li;
public class use {
public static void main(String[] args) {
book b1 = new book();
myThread1 t1 = new myThread1(b1);
myThread2 t2 = new myThread2(b1,t1);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
// 线程类1
class myThread1 extends Thread {
book b;
public myThread1(book b) {
super();
this.b = b;
}
@Override
public void run() {
while(true){
b.addbook();
try {
System.out.println("t1睡觉了哦");
Thread.sleep(50000);
System.out.println("t1醒了呐");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//线程类2
class myThread2 extends Thread {
book b;
myThread1 t1;
public myThread2(book b,myThread1 t1){
this.b = b;
this.t1 = t1;
}
@Override
public void run() {
while(true){
b.delbook();
/*
* 如果t1线程处于sleep状态就调用t1对象实例的wait方法
* 因为wait方法必须要获得对象实例的锁才能成功运行
* 不然会抛出
* 获取不到对象锁异常IllegalMonitorStateException
*/
if(t1.getState()==State.TIMED_WAITING){
try{
t1.wait();
}catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//资源类
class book{
public int number;
public synchronized void addbook(){
System.out.println(Thread.currentThread().getName()+"------"+(++number));;
}
public synchronized void delbook(){
System.out.println(Thread.currentThread().getName()+"------"+(--number));;
}
}
可以看出t1的线程在调用sleep之后为TIMED_WAITING后,调用t1的wait方法抛出了获取不到对象锁错误,说明sleep没有释放t1的对象锁。
这里有一个小疑惑,找了很久都没有找到Object提供的锁状态函数,为什么不给提供呐?
以上两步分析了sleep不会释放线程自身的锁,看似sleep释放了它操作的对象book实例的对象锁,但是真的释放了吗?我们是在t1线程的run函数里面,add()函数之后,调用的t1的sleep。此时add函数执行完毕才会去调用sleep函数,自然是释放了book实例对象的对象锁。我们为了要验证它sleep之后会不会释放操作对象的对象锁,等到时间到了自动唤醒接着原来的位置执行,我们就需要在它持有操作数锁的过程中,让他sleep,即在加了同步的add函数中让线程sleep。
更改代码如下:
public class use {
public static void main(String[] args) {
book b1 = new book();
myThread1 t1 = new myThread1(b1);
myThread2 t2 = new myThread2(b1,t1);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
// 线程类1
class myThread1 extends Thread {
book b;
public myThread1(book b) {
super();
this.b = b;
}
@Override
public void run() {
while(true){
try {
b.addbook();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
//线程类2
class myThread2 extends Thread {
book b;
myThread1 t1;
public myThread2(book b,myThread1 t1){
this.b = b;
this.t1 = t1;
}
@Override
public void run() {
while(true){
b.delbook();
}
}
}
//资源类
class book{
public int number;
public synchronized void addbook() throws InterruptedException{
System.out.println(Thread.currentThread().getName()+"------"+(++number));
System.out.println(Thread.currentThread().getName()+"说完这句我就睡觉了哦");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+"我又醒来了");
}
public synchronized void delbook(){
System.out.println(Thread.currentThread().getName()+"------"+(--number));
}
}
在代码中,让运行add函数的线程即t1休眠5秒,同时没有让t2线程休眠,这样如果t1释放了锁,那么在它休眠期间,t2将获得book的实例对象对象锁,不断执行,那么在输出t1睡觉了与醒来了之间应该输出t2运行delbook的结果。
如果没有释放就说明它没有释放锁。如上图,我们可以看出,t1线程并没有释放锁
这说明,如果a线程调用了sleep方法不仅它自身的线程对象锁不释放,而且它所持有的其他对象的对象锁也不会释放。
/*....The thread does not lose ownership of any monitors....*/
//这是sleep函数源码中的注释的一句话,这个线程不会释放它所持有的任何监视器(锁)。
上面的验证结论与源码注释一致。sleep是native(本地)方法。
而wait的作用对象是Object对象,它会释放自身的对象锁,那么它会释放它持有的操作对象的对象锁吗?
更改add()函数中的Thread.sleep(5000);
为Thread.currentThread().getName().wait();
同时去掉myThread1中的while循环,让它执行一次。
即让t1在持有book的实例对象锁的时候,调用t1对象的wait,我们没有写唤醒t1的代码,且t1与t2时不同对象,t2的运行不依赖t1的对象锁。如果它不释放book实例对象的对象锁,那么t2将等待而没有输出。t1因为循环会再一次调用
public class use {
public static void main(String[] args) {
book b1 = new book();
myThread1 t1 = new myThread1(b1);
myThread2 t2 = new myThread2(b1, t1);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
// 线程类1
class myThread1 extends Thread {
book b;
public myThread1(book b) {
super();
this.b = b;
}
@Override
public void run() {
try {
b.addbook();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 线程类2
class myThread2 extends Thread {
book b;
myThread1 t1;
public myThread2(book b, myThread1 t1) {
this.b = b;
this.t1 = t1;
}
@Override
public void run() {
while (true) {
b.delbook();
}
}
}
// 资源类
class book {
public int number;
public synchronized void addbook() throws InterruptedException {
System.out.println(Thread.currentThread().getName() + "------"
+ (++number));
System.out.println(Thread.currentThread().getName() + "说完这句我就wait了哦");
Thread.currentThread().getName().wait();
}
public synchronized void delbook() {
if (number >-10 && number < 10) {
System.out.println(Thread.currentThread().getName() + "------"
+ (--number));
}
}
}
运行结果中,t1抛出了异常,t2继续输出,说明释放了book的实例对象锁。
但是注意运行状态是红色,说明程序挂起了。t2执行完了肯定线程结束了,那么就是t1线程在wait状态,没有被唤醒仍旧在等待。