多线程(二)生命周期和同步

接上文继续学习。


四:线程的生命周期:

由上图可以看出,一个线程由出生到死亡分为五个阶段:

1).创建状态 

?当用new操作符创建一个新的线程对象时,该线程处于创建状态。

?处于创建状态的线程只是一个空的线程对象,系统不为它分配资源

2). 可运行状态

?执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体—run()方法,这样就使得该线程处于可运行( Runnable )状态。

?这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。

3).不可运行状态

.当发生下列事件时,处于运行状态的线程会转入到不可运行状态。

调用了sleep()方法;

?线程调用wait方法等待特定条件的满足

?线程输入/输出阻塞

4)返回可运行状态: 

?处于睡眠状态的线程在指定的时间过去后

?如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变

?如果线程是因为输入/输出阻塞,等待输入/输出完成

5). 消亡状态 

当线程的run方法执行结束后,该线程自然消亡。

注意:

1.停止线程的方式:不能使用Thread类的stop方法来终止线程的执行。一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。

2.不能依靠线程的优先级来决定线程的执行顺序。

五:多线程并发

多线程并发是线程同步中比较常见的现象,java多线程为了避免多线程并发解决多线程共享数据同步问题提供了synchronized关键字

synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。

1.Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。

2. 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。

3.如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。

4. synchronized块,写法:

synchronized(object) 

{ 

} 

表示线程在执行的时候会对object对象上锁。

5.synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。

同步的线程状态图:

代码实现

public static void main(String[] args) {

		new TraditionalThreadSynchronized().init();//
	}

	private void init() {
		final Outputer outputer = new Outputer();//
		new Thread(new Runnable() {

			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {

						e.printStackTrace();
					}
					outputer.output("1");//
				}

			}
		}).start();

		new Thread(new Runnable() {

			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {

						e.printStackTrace();
					}
					outputer.output3("2");
				}

			}
		}).start();
	}

	static class Outputer {

		public void output(String name) {
			int len = name.length();
			synchronized (Outputer.class) {

				for (int i = 0; i < len; i++) {
					System.out.println(name.charAt(i));

				}
				System.out.println();
			}

		}

		public synchronized void output2(String name) {
			int len = name.length();
			synchronized (this) {

				for (int i = 0; i < len; i++) {
					System.out.println(name.charAt(i));

				}
				System.out.println();
			}

		}

		public static synchronized  void output3(String name) {
			int len = name.length();

				for (int i = 0; i < len; i++) {
					System.out.println(name.charAt(i));

				}
				System.out.println();
			}

	}

Synchronized是java语言级别内置的同步机制。Synchronized的背后机制是Java在语言级上的锁机制:

  • 类锁:对于每一个类,自动配套一个类锁,该锁临界量为1
  • 实例锁:对于每一个类实例化后的一个对象,自动配套一个对象锁,该锁临界量为1

对于类锁,目前注意到,只能应用到类得某个方法上:

  • 静态方法锁:static synchronized method();

对于实例锁,有以下几种方式:

  • 非静态方法锁:synchronized method();
  • 代码块锁:synchronized{ }或synchronized(this){ }
  • 指定类实例锁:synchronized(Object ob){ }

非静态方法锁与代码块锁实质都一样,是对当前所属类得实例加锁。而指定类实例锁,是对括号里的对象ob加锁,这一点很有意思,灵活运用指定类锁可以构建一些稍微负责的并行程序模型。

其中类锁与实例锁各自独立,不相互冲突,一个synchronized 指定锁,要么是类锁,要么是实例锁,是互斥的。

在多线程中,依然是引用日本作者结成浩的《java多线程设计模式》中的例子:

问题:

[java] view plaincopy

  1. public class Something(){
  2. public synchronized void isSyncA(){}
  3. public synchronized void isSyncB(){}
  4. public static synchronized void cSyncA(){}
  5. public static synchronized void cSyncB(){}
  6. }

那么,对于Something类的两个实例a与b,那么下列组方法何以被1个以上线程同时访问呢

  1. x.isSyncA()与x.isSyncB()
  2. x.isSyncA()与y.isSyncA()
  3. x.cSyncA()与y.cSyncB()
  4. x.isSyncA()与Something.cSyncA()

  1. 都是对同一个实例的synchronized域访问,因此不能被同时访问
  2. 是针对不同实例的,因此可以同时被访问
  3. 因为是static synchronized,所以不同实例之间仍然会被限制,相当于Something.isSyncA()与   Something.isSyncB()了,因此不能被同时访问。
  4. 能够同时访问,因为一个是实例锁,一个是类锁。

总结

synchronized的语言级别锁机制,让并发项目时,无需增加锁控制类,直接本身原生态支持,十分方便,但注意到,synchronized锁只有两个粒度,一个是类锁,一个是实例锁,而且临界区互斥量为1,对于有些需要临界区大于1的,锁粒度更大,动态调控,那synchronized就不够用了。得需要专门的信号量等机制了。

最后一点是synchronized关键词不能被直接继承,子类在继承负责,覆盖父类的synchronized方法时候,必须同样显示用synchronized关键词。而子类如果不覆盖父类synchronized关键词的非静态方法,直接继承并使用父类的非静态方法时,父类的synchronized字段在子类对象仍然是有效的。

时间: 2024-10-18 23:40:05

多线程(二)生命周期和同步的相关文章

Java 多线程系列2——多线程的生命周期及生产消费者模型

一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了.主要包括: Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态.处于就绪状态的线程,只是说

day11(多线程,唤醒机制,生产消费者模式,多线程的生命周期)

A:进程: 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. B:线程: 线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程.一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序. C:简而言之: 一个程序运行后至少有一个进程,一个进程中可以包含多个线程 线程实现 实现的两种方式 继承Thread public class MyThread extends Thread{ @Ove

Android apk动态加载机制的研究(二):资源加载和activity生命周期管理

出处:http://blog.csdn.net/singwhatiwanna/article/details/23387079 (来自singwhatiwanna的csdn博客) 前言 为了更好地阅读本文,你需要先阅读Android apk动态加载机制的研究这篇文章,在此文中,博主分析了Android中apk的动态加载机制,并在文章的最后指出需要解决的两个复杂问题:资源的访问和activity生命周期的管理,而本文将会分析这两个复杂问题的解决方法.需要说明的一点是,我们不可能调起任何一个未安装的

简述Activity生命周期

copy from : http://gityuan.com/2016/03/18/start-activity-cycle/ 基于Android 6.0的源码剖析, 分析android Activity启动流程中ActivityManagerService所扮演的角色 一.概述 上一篇文章startActivity启动过程分析,介绍了startActivity是如何一步步创建的,再来看看生命周期的控制.先来一张官方的Activity状态转换图: Activity的生命周期中只有在以下3种状态之

5.PHP内核探索:多进程/线程的SAPI生命周期

多进程的SAPI生命周期 通常PHP是编译为apache的一个模块来处理PHP请求.Apache一般会采用多进程模式, Apache启动后会fork出多个子进程,每个进程的内存空间独立,每个子进程都会经过开始和结束环节, 不过每个进程的开始阶段只在进程fork出来以来后进行,在整个进程的生命周期内可能会处理多个请求. 只有在Apache关闭或者进程被结束之后才会进行关闭阶段,在这两个阶段之间会随着每个请求重复请求开始-请求关闭的环节. 多进程SAPI生命周期 多线程的SAPI生命周期 多线程模式

PHP内核的学习--PHP生命周期

一切的开始: SAPI接口 SAPI(Server Application Programming Interface)指的是PHP具体应用的编程接口, 就像PC一样,无论安装哪些操作系统,只要满足了PC的接口规范都可以在PC上正常运行, PHP脚本要执行有很多种方式,通过Web服务器,或者直接在命令行下,也可以嵌入在其他程序中. 通常,我们使用Apache或者Nginx这类Web服务器来测试PHP脚本,或者在命令行下通过PHP解释器程序来执行. 脚本执行完后,Web服务器应答,浏览器显示应答信

第一节 生命周期和Zend引擎

一切的开始: SAPI接口 SAPI(Server Application Programming Interface)指的是PHP具体应用的编程接口, 就像PC一样,无论安装哪些操作系统,只要满足了PC的接口规范都可以在PC上正常运行, PHP脚本要执行有很多种方式,通过Web服务器,或者直接在命令行下,也可以嵌入在其他程序中. 通常,我们使用Apache或者Nginx这类Web服务器来测试PHP脚本,或者在命令行下通过PHP解释器程序来执行. 脚本执行完后,Web服务器应答,浏览器显示应答信

Vue的过滤器,生命周期的钩子函数和使用Vue-router

一.过滤器 1.局部过滤器 在当前组件内部使用过滤器 给某些数据 添油加醋 //声明 filters:{ '过滤器的名字':function(val,a,b){ //a 就是alax ,val就是当前的数据 } } //使用 管道符 数据 | 过滤器的名字('alex','wusir') <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8">

Java多线程(二)、线程的生命周期和状态控制(转)

Java多线程(二).线程的生命周期和状态控制 分类: javaSE综合知识点 2012-09-10 16:11 15937人阅读 评论(3) 收藏 举报 一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable). 注意:不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadSt