线程的锁详解
来源:互联网 发布:千牛卖家第三方软件 编辑:程序博客网 时间:2024/06/05 04:10
当线程通过 synchronized 方法或者代码块获得对象锁后,其他线程无法访问对象的任何synchronized方法或者代码块,但是可以访问非synchronized方法或者代码块。
如果对静态方法添加synchronized 或者对静态变量的修改添加synchronized 块,那么这个时候获得的是类对象的锁。(class对象)
当线程休眠的时候,是不会释放对象锁。
可以把对象锁理解为资源,线程使用完这个资源后,会释放这个资源,以便其他线程来使用。
线程中的wait() 与 锁的关系
我们先看一段代码:
/** * 计算输出其他线程锁计算的数据* */ public class ThreadA { public static void main(String[] args) throws InterruptedException{ ThreadB b = new ThreadB(); //启动计算线程 b.start(); //线程A拥有b对象上的锁。线程为了调用wait()或notify()方法,该线程必须是那个对象锁的拥有者 synchronized (b) { System.out.println("等待对象b完成计算。。。"); //当前线程A等待 b.wait(); System.out.println("b对象计算的总和是:" + b.total); } } } /** * 计算1+2+3 ... +100的和* */ class ThreadB extends Thread { int total; public void run() { synchronized (this) { for (int i = 0; i < 101; i++) { total += i; } //(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线程A被唤醒 notify(); System.out.println("计算完成"); } } }
执行结果:
等待对象b完成计算。。。
计算完成
b对象计算的总和是:5050
如果我们将b.wait()去掉呢?结果如下:
等待对象b完成计算。。。
b对象计算的总和是:0
计算完成
上述的结果表明,当去掉b.wait()时,新启动的线程ThreadB与主线程ThreadA是各自执行的,没有线程等待的现象。
我们想要的效果是,当线程ThreadB完成计算之后,再去取计算后的结果。所以使用了b.wait()来让主线程等待。
那为什么是使用b.wait(),而不是Thread.currentThread.wait(),或者其他的呢?
如果我们将b.wait()替换成Thread.currentThread.wait(),将会得到如下的结果:
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at pa.com.thread.ThreadA.main(ThreadA.java:18)
等待对象b完成计算。。。
计算完成
替换的代码Thread.currentThread.wait()好像理所当然应该如我们预期的正确啊,让当前线程处于等待状态,让其他线程先执行。
我们忽略了一个很重要的问题:线程与锁是分不开的,线程的同步、等待、唤醒都与对象锁是密不可分的。
线程ThreadA持有对象b的锁,我们要使用这把锁去让线程释放锁,从而让其他的线程能抢到这把锁。
从我们的程序来分析就是:线程ThreadA首先持有锁对象b,然后调用b.wait()将对象锁释放,线程ThreadB争抢到对象锁b,从而执行run()方法中的计算,计算完了之后使用notify()唤醒主线程ThreadA,ThreadA得以继续执行,从而得到了我们预期的效果。
(之所以ThreadB的对象锁也是b,是因为synchronized(this)中的this指向的就是ThreadB的实例b)
Thread.currentThread.wait()调用的是当前线程对象(即主线程ThreadA)的wait()方法,当前线程对象ThreadA是没有被加锁的,它只是获取了对象锁b。我基本没有看到过这样的调用,一般使用的是锁对象的wait(),本例中为b.wait()
顺带讲一下wait()与sleep()的区别。
如果我们将b.wait()换成Thread.sleep(1000),则会出现如下的结果:
等待对象b完成计算。。。
b对象计算的总和是:0
计算完成
从执行结果可以看出,Thread.sleep(1000)只是让主线程ThreadA睡眠了1秒钟,而并没有释放对象锁,所以在主线程ThreadA睡眠的过程中,ThreadB拿不到对象锁,从而不能执行。
所以我们也就得出了如下的结论:
wait()方法是让线程释放对象锁,让其他线程拿到锁之后去优先执行,当其他全程唤醒wait()中的线程 或者 拿到对象锁的线程都执行完释放了对象锁之后,wait()中的线程才会再次拿到对象锁从而执行。
sleep()方法是让线程睡眠,此时并没有释放对象锁,其他想要拿到睡眠线程的对象锁的线程也就拿不到相应的对象锁,从而不能抢在它前面执行。
补:
wait、notify和notifyAll方法是Object类的final native方法。所以这些方法不能被子类重写,Object类是所有类的超类,因此在程序中有以下三种形式调用wait等方法。
wait();//方式1:this.wait();//方式2:super.wait();//方式3
void wait()
导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
- 线程的锁详解
- 线程的详解
- java线程的详解
- Linux 线程锁详解
- Linux 线程锁详解
- Linux 线程锁详解
- Linux 线程锁详解
- Linux 线程锁详解
- Linux 线程锁详解
- java线程的作用详解
- 线程属性pthread_attr_t的详解
- 关于线程同步的详解
- JAVA线程的概念详解
- servlet线程安全问题的详解
- 【Linux】下的线程详解
- Lock锁 Condition 和线程的关系详解
- 嵌入式 Linux 线程锁详解
- Linux线程锁详解pthread_mutexattr_t
- 面向对象实验三 分析类图
- Python的ASCII, GB2312, Unicode , UTF-8 相互转换
- 有向无环图:AOV网与AOE网
- 使用iServer JAVA API访问iServer数据服务进行数据操作示例
- 堆表空间管理
- 线程的锁详解
- Git 的命令操作
- window.open不生效
- css3 制作三角
- 论文的页码设置
- SSIS package 更新 variable
- Java 数据结构和经典算法经验总结
- 微服务可靠性设计
- 【NGUI】各个组件的应用与数值获取