java Thread源码分析(二)
一、sleep的使用
 public class ThreadTest {
     public static void main(String[] args) throws InterruptedException {
         Object obj = new Object();
         MyThread mt = new MyThread(obj);
         mt.start();
         MyThread mt2 = new MyThread(obj);
         mt2.start();
     }
     private static class MyThread extends Thread{
         private Object obj;
         public MyThread(Object obj) {
             this.obj = obj;
         }
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName() +
                     " synchronized之前: " + System.currentTimeMillis());
             synchronized(obj) {
                 System.out.println(Thread.currentThread().getName() +
                         " sleep之前: " + System.currentTimeMillis());
                 try {
                     Thread.sleep(2000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 System.out.println(Thread.currentThread().getName() +
                         " sleep之后: " + System.currentTimeMillis());
             }
         }
     }
 }
输出:
Thread-1 synchronized之前: 1546337474050
Thread-0 synchronized之前: 1546337474050
Thread-1 sleep之前: 1546337474051
Thread-1 sleep之后: 1546337476051
Thread-0 sleep之前: 1546337476051
Thread-0 sleep之后: 1546337478052
线程Thread-0和线程Thread-1监控同一个资源obj,Thread-1在sleep之后并没有释放对象锁。
好比,A和B去店里买衣服,只有一间试衣间,一把试衣间的钥匙,A先拿到试衣间钥匙(obj),进入试衣间(synchronized(obj) {...}),
并在试衣间睡了一觉(sleep(2000)),B只能等A醒来走出试衣间,才能有机会拿到钥匙并进入试衣间。
调用native方法:public static native void sleep(long millis) throws InterruptedException;
二、wait和notify的使用
 public class ThreadTest {
     public static void main(String[] args) throws InterruptedException {
         Object obj = new Object();
         MyThread mt = new MyThread(obj);
         mt.start();
         MyThread2 mt2 = new MyThread2(obj);
         mt2.start();
     }
     private static class MyThread extends Thread{
         private Object obj;
         public MyThread(Object obj) {
             this.obj = obj;
         }
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName() +
                     " synchronized之前: " + System.currentTimeMillis());
             synchronized(obj) {
                 System.out.println(Thread.currentThread().getName() +
                         " wait之前: " + System.currentTimeMillis());
                 try {
                     obj.wait();
                 } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }
                 System.out.println(Thread.currentThread().getName() +
                         " wait之后: " + System.currentTimeMillis());
             }
         }
     }
     private static class MyThread2 extends Thread{
         private Object obj;
         public MyThread2(Object obj) {
             this.obj = obj;
         }
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName() +
                     " synchronized之前: " + System.currentTimeMillis());
             synchronized(obj) {
                 System.out.println(Thread.currentThread().getName() +
                         " notify之前: " + System.currentTimeMillis());
                 obj.notify();
                 System.out.println(Thread.currentThread().getName() +
                         " notify之后: " + System.currentTimeMillis());
             }
         }
     }
 }
输出:
Thread-0 synchronized之前: 1546349737274
Thread-0 wait之前: 1546349737274
Thread-1 synchronized之前: 1546349737274
Thread-1 notify之前: 1546349737275
Thread-1 notify之后: 1546349737275
Thread-0 wait之后: 1546349737275
Thread-0在执行了obj.wait()之后,线程暂停并释放对象锁,之后,Thread-1获得对象锁,Thread-1执行obj.notify()后Thread-0苏醒,Thread-1执行完synchronized的代码块之后,Thread-0才有机会获得锁。
1、wait()让当前线程进入“等待状态”,并让当前线程释放它所持有的锁。直到其他线程调用此对象的notify()方法或notify()方法,当前线程被唤醒,进入“就绪状态”。
2、notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程。notify()是唤醒单个线程(随机唤醒),而notifyAll()是唤醒所有的线程。
3、wait(long timeout)让当前线程处于“等待(阻塞)状态”,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量,当前线程被唤醒,进入“就绪状态”。
4、调用 wait()、notify()、notifyAll() 方法之前,必须获得对象锁,即,只能在同步方法中调用。
5、执行 notify() 之后,并不会立即退出让wait的线程执行,必须要先将同步块中的程序执行完,退出同步块,才会释放锁,让等待线程执行。
6、每调用一次 notify() 只能唤醒一个线程,多次调用可通知多个线程。
wait()、notify()、notifyAll()都是Object的方法。
原理:
每个对象都有个monitor,初始是0,执行完synchronized值就是1。
wait/notify需要在获得monitor的线程中才可以执行。
所以,wait/notify需要在synchronized中执行。
其中,wait又会释放掉锁,破坏掉同步。
和synchronized的关系:
synchronized代码块生成的字节码,被monitorenter和monitorexit包围,持有对象的monitor,线程执行wait/notify方法时,必须持有对象的monitor,所以,wait/notify方法在synchronized同步快中执行,就持有了对象的锁。
互斥和协同:
java语言的同步机制在底层实现上就只有两种方式:互斥和协同。
互斥:即synchronized内置锁。
协同:即内置条件队列,wait/notify/notyfiAll。
条件队列是处于等待状态的线程,等待特定条件为真。每个java对象都可以作为一个锁,同样每个java对象都可以作为一个条件队列。通过wait/notify/notifyAll来操作条件队列。
可以理解为:有一个队列,o.wait()就push进去,o.notify()就pull出来。、
要调用条件队列的任何一个方法,都必须要获得对象上的锁。
三、join的使用
 public class ThreadTest {
     public static void main(String[] args) throws InterruptedException {
         Object obj = new Object();
         MyThread mt = new MyThread(obj);
         mt.start();
         //在main中调用mt.join()
         mt.join();
         System.out.println("main");
     }
     private static class MyThread extends Thread{
         private Object obj;
         public MyThread(Object obj) {
             this.obj = obj;
         }
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName() +
                     " synchronized之前: " + System.currentTimeMillis());
             synchronized(obj) {
                 System.out.println(Thread.currentThread().getName() +
                         " sleep之前: " + System.currentTimeMillis());
                 try {
                     Thread.sleep(3000);
                 } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }
                 System.out.println(Thread.currentThread().getName() +
                         " sleep之后: " + System.currentTimeMillis());
             }
         }
     }
 }
输出:
Thread-0 synchronized之前: 1546354871560
Thread-0 sleep之前: 1546354871560
Thread-0 sleep之后: 1546354874561
main
在main中调用了线程mt的join方法(mt.join()),则线程main会等线程mt执行完毕后再恢复运行。
join()方法由wait()实现。源码:
java Thread源码分析(二)的更多相关文章
- java Thread源码分析
		
一.使用 java 多线程 java多线程其中两种使用方式: 1.继承 Thread 类 2.实现 Runnable 接口 public class ThreadTest { public stati ...
 - 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>
		
目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...
 - Java Reference 源码分析
		
@(Java)[Reference] Java Reference 源码分析 Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互.即可以使 ...
 - 框架-springmvc源码分析(二)
		
框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...
 - java集合源码分析(三):ArrayList
		
概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...
 - java集合源码分析(六):HashMap
		
概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...
 - Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题
		
4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...
 - Java 集合源码分析(一)HashMap
		
目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ...
 - Tomcat源码分析二:先看看Tomcat的整体架构
		
Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Conn ...
 
随机推荐
- nginx回源使用localhost产生问题
			
最近测试ngx_http_slice模块,回源的时候填的localhost结果老是超时,还以为是slice模块有问题,后来无意间改成127.0.0.1后就没有问题了 真是见鬼了 #user root; ...
 - from sklearn import datasets运行错误:ImportError: DLL load failed: 找不到指定的程序------解决办法
			
在运行集成学习的多数投票分类代码时,出现错误 from sklearn import datasets from sklearn.model_selection import cross_val_sc ...
 - ORACLE同义词使用
			
多用户协同开发中,可以屏蔽对象的名字及其持有者.如果没有同义词,当操作其他用户的表时,必须通过user名.object名的形式,采用了Oracle同义词之后就可以隐蔽掉user名, 当然这里要注意的是 ...
 - 576D Flights for Regular Customers
			
分析 https://www.cnblogs.com/onioncyc/p/8037056.html 写的好像有点问题 但是大致就是这个意思 代码很好理解 代码 #include<bits/st ...
 - spring boot官方配置
			
#BANNER banner.charset = UTF-8 #横幅文件编码.banner.location = classpath:banner.txt #横幅文件位置.banner.image.l ...
 - await Vue.nextTick() 的含义分析
			
概述 今天看别人的单元测试代码的时候碰到了一段代码 await Vue.nextTick(),初看起来不是很懂,后来通过查资料弄懂了,记录下来,供以后开发时参考,相信对其他人也有用. await Vu ...
 - 【Linux开发】为qt-embedded添加jpeg库的交叉编译方法for arm
			
看了一个文章: =====================================谢论坛内各位的帮助,我的qt/e2.3.10和qtopia2.1.1终于全部编译通过. 下面是jpeg和uui ...
 - python+selenium模拟键盘输入
			
from selenium.webdriver.common.keys import Keys #键盘导入类 --------------------------------------------- ...
 - Spring源码深度解析
			
Spring源码分析 Spring Web入门及源码学习 [Spring源码分析]Bean加载流程概览 Spring Framework 实现原理与源码解析系统 Spring源码分析--水门 Spri ...
 - 指定pom文件jdk版本
			
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> ...