`

(新手)java多线程基础知识——调度与同步

阅读更多
以前写过一篇关于多线程的总结:
http://kyfxbl.iteye.com/blog/1370377

很久没用到,忘记了。最近又遇到了一些多线程的问题,重新查了些资料,再提炼一下。本文不涉及java.util.concurrent包里的API,后面再专门介绍

1、线程的状态

线程的运行状态主要有runnable、running、waiting、timed_waiting、blocked等,主要有2类API,可以控制线程的运行状态

第一类是调度类,不涉及object monitor和synchronized方法,这类API包括yield()、sleep()、join()等

第二类是同步类,涉及到object monitor和synchronized方法,这类API包括wait()、notify()、notifyAll()

总的来说,第一类API比第二类要简单

2、调度类API

这类API不涉及同步方法,是直接控制线程的运行状态,都是Thread类定义的方法

yield()会使线程从running状态切换到runnable状态,但是也有可能JVM立刻又把该线程切换回running状态(优先级高、无其他线程等原因),可以说,这个方法只是“建议”jvm先去执行其他线程,运行结果是不能保证的

sleep()需要跟一个参数,单位是毫秒,会使线程进入timed_waiting状态,sleep时间结束以后,会回到runnable状态

join()使当前线程进入waiting状态,等join的线程执行完毕之后,才回到runnable状态。举例来说,有2个线程t1和t2,在t1里执行了t2.join(),则t1进入waiting状态,等t2执行完毕之后,t1才回到runnable状态
public static void main(String[] args) throws InterruptedException {

		final Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					Thread.sleep(10000);// 此行代码使该线程进入timed_waiting状态
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		});

		thread.start();

		new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					thread.join();// 此行代码使该线程进入waiting状态,等待thread执行完毕
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}).start();

	}

用jstack看到这2个线程的状态:



Thread-0由于执行了sleep()方法,而进入timed_waiting状态;Thread-1由于执行了join()方法,而进入waiting状态

调度类的API不涉及synchronized方法,所以也不涉及到blocked状态

3、synchronized关键字与object monitor

同步的关键字是synchronized,其实我觉得叫“锁定”会比较好,不知道怎么理解“同步”这个词

synchronized方法包括2种,一种是标注了synchronized关键字的方法,一种是synchronized代码块

本质上差不多,只要是同步方法,都有一个object monitor:对于synchronized关键字方法来说,monitor就是该方法所属的类的实例;对于synchronized代码块来说,monitor是在synchronized关键字后面直接指定的

当线程执行到同步方法或者同步代码块的时候,就会获取object monitor,并继续执行;但是,同一时刻,只有唯一一个线程可以获取object monitor,其他的线程就会阻塞,进入blocked状态,等待持有object monitor的那个线程释放object monitor

这就是同步代码的基本状况,如果没有同步类的API的话,多线程执行就无法控制了,只能线程A获取->线程A释放->线程B获取->线程B释放->线程C获取……,这样顺序执行下去,所以就有了下面的同步类API

4、同步类API

前面说到了,同步的代码(方法或代码块),都有一个object monitor,可以通过在object monitor上执行wait()、notify()、notifyAll()方法,来进行多线程之间的通信

在object monitor上执行wait()方法,会使当前的线程释放锁,并进入waiting状态;并将该线程加入object monitor内部的“等待线程列表”中

在object monitor上执行notify()方法,会从“等待线程列表”中随机挑选一个线程唤醒,进入runnable状态(如果没有获取object monitor,则会立刻进入blocked状态)

在object monitor上执行notifyAll()方法,会将“等待线程列表”中所有的线程都唤醒,全部进入runnable状态(但是随后没有获取object monitor的线程,会立刻进入blocked状态)

注意,当一个线程调用了object.wait()方法进入waiting状态之后,如果没有其他线程帮它调用object.notify()方法,则该线程将永远停留在waiting状态,此线程永不结束

贴一段示例代码进行说明:
public static void main(String[] args) {

		final Object o = new Object();

		new Thread(new Runnable() {

			@Override
			public void run() {
				synchronized (o) {
					try {
						System.out.println("me first");// 这个线程先执行,是期望的结果,但是不能保证
						Thread.sleep(15000);
						o.wait();
						System.out.println("wake up and done");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

		}).start();

		new Thread(new Runnable() {

			@Override
			public void run() {
				synchronized (o) {
					try {
						System.out.println("it's my time");// 如果这个线程先执行,则另一个线程将永远waiting
						Thread.sleep(15000);
						o.notify();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

		}).start();

	}

阶段1:thread-0进入timed_waiting状态,thread-1没有获取到object monitor,进入blocked状态



控制台输出:me first

阶段2:thread-0调用了o.wait()方法,释放锁并进入waiting状态;thread-1执行sleep()方法,进入timed_waiting状态



控制台输出:it's my time

阶段3:thread-1执行o.notify()方法,唤醒thread-0,自己运行结束,释放锁(并不是notify方法释放了锁);thread-0执行最后一行代码,然后也结束;两个线程都执行完毕

控制台输出:wake up and done

但是,这里有很强的随机性,每次执行的效果是不同的!如果是thread-1先执行的话,那么当thread-0调用o.wait()之后,就会永远停留在waiting状态,该线程永远不会结束

5、同步类API的限制

上述3个方法,都是只能在synchronized代码里调用的,否则就会报错。准确的说,虽然这3个方法都是Object类定义的方法,但是只有成为object monitor的实例才能调用

比如以下这段代码是不能执行的,o是object monitor,而o1不是,所以不能调用o1.wait():
public static void main(String[] args) {

		final Object o = new Object();

		final Object o1 = new Object();

		new Thread(new Runnable() {

			@Override
			public void run() {

				synchronized (o) {
					try {
						o1.wait();// 错误
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			}

		}).start();

	}


6、总结

yield()、sleep()、join()都是Thread类定义的方法,不涉及同步方法,因此也不会使线程切换到blocked状态,也不涉及object monitor的释放

wait()、notify()、notifyAll()是Object类定义的方法,但是只能在object monitor实例上调用

wait()方法会立刻释放object monitor,并使当前线程进入waiting状态

notify()和notifyAll()方法只是唤醒此前调用了wait()方法的线程,但是自己并不会释放锁



本文也参考了:
http://dylanxu.iteye.com/blog/1322066
http://www.cnblogs.com/adamzuocy/archive/2010/03/08/1680851.html
  • 大小: 12.6 KB
  • 大小: 10.8 KB
  • 大小: 13.1 KB
  • 大小: 20.5 KB
分享到:
评论
1 楼 轻指飞扬 2013-03-25  
不错的文章!最近工作中频繁用到了多线程的知识,看了楼主的文章等于又帮我梳理了一遍,非常感谢!

相关推荐

    java多线程教程——一个课件彻底搞清多线程

    本教程将深入讲解Java线程的相关知识,包括进程与线程的基本概念、线程的创建和启动、多线程的互斥与同步、线程状态和线程控制以及死锁的概念。 首先,我们要理解进程与线程的区别。进程是操作系统资源分配的基本...

    Java多线程知识点总结

    总之,掌握Java多线程的生命周期、创建、启动、同步以及线程池的使用是编写高效、稳定并发程序的基础。理解这些知识点对于解决并发编程中的问题,比如资源竞争、死锁、线程安全性等问题,至关重要。在实际开发中,...

    java多线程案例——未完成

    下面是对Java多线程基础知识的详细解释: 1. **线程的创建方式**: - 继承`Thread`类:自定义一个新的类,继承Java的`Thread`类,并重写`run()`方法。创建实例后调用`start()`方法启动线程。 - 实现`Runnable`...

    Java多线程之定时任务 以及 SpringBoot多线程实现定时任务——异步任务

    1. SpringBoot 自定义线程池以及多线程间的异步调用(@Async、@EnableAsync) 2.Java多线程之定时任务 以及 SpringBoot多线程实现定时任务 3.@EnableScheduling 与 @Scheduled

    基于java swing的多线程电梯调度模拟

    在本项目"基于Java Swing的多线程电梯调度模拟"中,我们主要探讨的是如何利用Java的多线程特性来实现一个复杂的系统——电梯调度。这个任务是在操作系统课程中的一个典型作业,它要求开发者模拟真实世界中的电梯运行...

    Java多线程的小例子——吃包子

    这个名为"Java多线程的小例子——吃包子"的示例,旨在帮助开发者直观地理解多线程的工作原理。下面我们将深入探讨该示例所涉及的核心知识点。 首先,多线程通常涉及到以下几个关键概念: 1. **线程(Thread)**:...

    java多线程基础知识

    Java多线程基础知识 Java多线程基础知识是Java编程语言中的一项重要技术,用于提高程序的执行效率和响应速度。在这里,我们将详细介绍Java多线程基础知识的相关概念和技术。 一、程序、进程和线程 程序(Program...

    多线程编程——线程的同步

    在“多线程编程之四——线程的同步”这个文件中,可能包含了上述各种同步机制的具体实现示例和详细说明,这对于初学者来说是一份非常宝贵的参考资料。通过学习和理解这些例子,开发者可以更好地掌握如何在实际项目中...

    多线程编程之四——线程的同步-VC知识库文章[归纳].pdf

    多线程编程之四——线程的同步-VC知识库文章[归纳].pdf

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要...通过对这些知识点的学习和实践,读者可以深入理解Java多线程的运用,提升编程技能。

    Java多线程编程总结

    Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 Java线程:线程的调度-让步 Java线程:线程的调度-合并 Java线程:线程的调度-守护线程 Java线程:线程的...

    java多线程设计

    本知识点将深入探讨Java多线程设计以及如何利用“不可变对象”(immutable objects)来避免多线程环境中的非安全问题。 一、Java多线程基础 1. 线程的创建:Java提供了两种创建线程的方式——继承Thread类和实现...

    Java练手小项目——多线程聊天室.zip

    【Java练手小项目——多线程聊天室】 在Java编程世界中,多线程是不可或缺的一部分,尤其在开发实时性、交互性强的应用时,如我们的主题“多线程聊天室”。这个实战项目旨在帮助开发者深入理解Java多线程的概念,并...

    头歌java多线程基础-Java多线程基础详解与实战指南

    内容概要:本文详细介绍了Java多线程的基础概念和关键技术点。首先解释了线程的基本概念、线程与进程的区别及其不同状态。接着,通过三种方式创建线程(继承Thread类、实现Runnable接口、使用Callable和Future接口)...

    Java多线程机制(讲述java里面与多线程有关的函数)

    掌握Java的多线程机制对于编写高效、并发友好的应用程序至关重要,它涉及到线程的创建、管理、同步以及线程间的通信等多个方面。理解并熟练运用这些概念和方法,可以帮助开发者构建出更稳定、高效的系统。

    多线程编程之四——线程的同步

    互斥量与临界区类似,也是用于同步多个线程访问同一资源,但它可以跨进程使用。互斥量在MFC中通过CMutex类实现,它允许多个进程中的线程同步,而临界区仅限于同一进程。互斥量在解锁后,会唤醒等待的线程,就像临界...

    java 多线程同步

    在使用`java.util.concurrent`包时,了解并发基础知识是必要的,包括线程的创建、同步、中断和异常处理。线程的创建可以通过Thread类或Runnable接口实现,同步可以通过`synchronized`关键字、Lock接口(如...

    java多线程设计模式详解(PDF及源码)

    (注意,本资源附带书中源代码可供参考) 多线程与并发处理是程序设计好坏优劣的重要课题,本书通过浅显易懂的文字与实例来介绍Java线程相关的设计模式概念,并且通过实际的Java程序范例和 UML图示来一一解说,书中...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

Global site tag (gtag.js) - Google Analytics