`
lclcr
  • 浏览: 125597 次
  • 性别: Icon_minigender_1
  • 来自: 山东
社区版块
存档分类
最新评论

Java线程(一)

    博客分类:
  • JAVA
阅读更多

        线程同步:synchronized方法和synchronized代码块
        一个synchronized方法在它执行前得到一个监视器锁。

        (1).对于类方法(static方法),使用与该方法的类的类对象相关联的锁。

        (2).对于实例方法,使用与this(为之调用该方法的对象)相关联的锁。

        (3).synchronized代码块对各对象使用同样的锁。

1.同步方法
	public synchronized void accessMethod(){}

2.同步代码块
	synchronized(syncObject) {
		//允许访问控制的代码
	}

       无论是同步方法还是同步代码块,两者之间并没有本质的区别,关键是我们能否正确的理解当前所使用的锁。  

       浅谈synchronized应用于实例方法和对象之上

       对于实例方法,synchronized 锁定的是对象(objects)而非方法(methods)或代码(code),每个对象只有一个lock(锁)与之相关联。

       当synchronized 被当作方法修饰符的时候,它所取得的lock 将被交给函数调用者(某对象)。如果synchronized 用于object reference,则取得的lock 将被交给该reference所指对象 

public class TestSyncLock {
	public synchronized void methodOne () {	//修饰方法
		//......
	}
	
	public void methodTwo () {			//修饰object reference
		synchronized (this) {
			//......
		}
	}
	
	public void methodThree () {			//修饰object reference		
		synchronized (syncObj) {		//任意的对象(Integer num、String str等,建议是byte [] bytes = new byte[0];)
			//......
		}
	}
}

       对于方法methodOne()和方法methodTwo()在对象锁定方面功能一致,二者都对this进行同步控制,即获得的lock将给予调用此方法的对象(也就是this所指代的对象)。 由于方法methodOne()和方法methodTwo()都隶属于class TestSyncLock,所以lock由TestSyncLock的某个对象获得。但是方法methodThree()则同步控制syncObj所指代的对象。

       如果我们实例化了class TestSyncLock的两个对象,即: 

TestSyncLock lockOne = new TestSyncLock();
TestSyncLock lockTwo = new TestSyncLock();

      对一个对象进行同步控制到底意味着什么呢?它意味着调用类(TestSyncLock )的对象(lockOne )的synchronized方法/块的线程(比如是Trhead threadA )将会取得对象(lockOne )的lock,当另外的一个线程(比如说Trhead threadB )调用相同的对象(lockOne )的synchronized方法/块的时候,由于该对象(lockOne )的lock已经被前一个线程(threadA )所持有,在该lock被释放之前后来的线程(threadB )将无法获得满足。然而,如果另一个线程(threadB )调用的是相同类(TestSyncLock )的其他对象(lockTwo )的synchronized方法/块的话,获得的是对象(lockTwo)的lock,此时就可以执行。

       结论:对同一个对象的synchronized方法/块只能有一个线程调用并执行,若有其他线程则需要等待;但是其他的线程可以访问该对象的其他非synchronized方法/块。对于不同对象的synchronzied方法/块同一时刻可以由多个线程执行。

       举例1.  

public class MyRunnable implements Runnable {
	@Override
	//public void run() {		//输出结果为格式一
	public synchronized void run() {	//输出结果为格式二
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		MyRunnable run = new MyRunnable();
		Thread tone = new Thread(run, "ThreadOne");
		Thread ttwo = new Thread(run, "ThreadTwo");
		tone.start();
		ttwo.start();
	}
}

 

格式一								格式二
ThreadOne synchronized loop 0		ThreadOne synchronized loop 0
ThreadTwo synchronized loop 0		ThreadOne synchronized loop 1
ThreadTwo synchronized loop 1		ThreadOne synchronized loop 2
ThreadOne synchronized loop 1		ThreadOne synchronized loop 3
ThreadOne synchronized loop 2		ThreadOne synchronized loop 4
ThreadTwo synchronized loop 2		ThreadTwo synchronized loop 0
ThreadOne synchronized loop 3		ThreadTwo synchronized loop 1
ThreadTwo synchronized loop 3		ThreadTwo synchronized loop 2
ThreadOne synchronized loop 4		ThreadTwo synchronized loop 3
ThreadTwo synchronized loop 4		ThreadTwo synchronized loop 4

       说明:我们创建了两个线程tone和ttwo,虽说它们两个是不同的对象,但是真正执行的目标对象却是MyRunnable的对象run。也就是说,线程tone和ttwo在启动执行时调用的是相同的对象(run)的synchronized run()方法。当tone.start()后等待执行(这里假设是tone先执行的,因为线程到底哪个先执行是随机的,由CPU分配时间片决定的,尽管我们可以设置优先级 ),当其获得了时间片后开始执行,获得了run对象的lock;当tone线程在执行sleep(1000)后,实际上CPU会将时间片分配给其他线程(我们就假定此时碰巧分配给了线程ttwo)。当ttwo线程去执行run对象的synchronized run()方法时,发现run对象的lock已经被线程tone获得,自己只有等到,在线程tone释放该lock之前,自己无法执行。当线程tone执行完毕,释放了对象run的lock之后,ttwo才可以执行。

       举例2.  

import java.util.Map;
import java.util.HashMap;

public class MapUtil {
	private static Map<String, String> map = new HashMap<String, String>();
	
	public synchronized void putData() {
		for (int j = 0 ; j < 5 ; j++) {
			System.out.println("put data " + j + " into map");
			map.put("key" + j, "" + j);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public synchronized void getData () {
		for (int j = 0 ; j < 5 ; j++) {
			System.out.println(map.get("key" + j));
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void sayHello() {
		for(int j = 0 ; j < 5 ; j++) {
			System.out.println("Hello World");
		}
	}
}

 

public class MapUtilRunnable {
	public static void main(String[] args) {
		final MapUtil mapUtil = new MapUtil();
		Thread tone = new Thread(){
			@Override
			public void run() {
				mapUtil.putData();
			}
		};
		Thread ttwo = new Thread(new Runnable() {
			public void run() {
				mapUtil.getData();
			}
		}, "threadTwo");
		tone.start();
		ttwo.start();
	}
}

 

put data 0 into map
put data 1 into map
put data 2 into map
put data 3 into map
put data 4 into map
0 1 2 3 4 

       说明:对于线程tone和ttwo到底哪个先执行是不确定的,只不过我这里执行时碰巧先执行了putData罢了。但是不管是这两个线程那个先执行,比如咱们这里和举例1 一样先假定tone先执行。虽说是两个不同的线程,但是其执行的仍然是同一对象mapUtil的不同synchronized方法,故只能由一个线程(先执行的threadOne)获得其对象锁。当线程threadOne访问mapUtil的getData()方法时,由于该方法是由synchronized修饰的,故当前线程(threadTwo)也会要求对象mapUtil的lock,而其lock已经被线程threadOne获得。不论怎样,对于synchronized实例方法,线程调用时总是获得调用该方法的对象的lock,只要是同一对象,其所有synchronized方法只有一个对象lock

      若线程tone和ttwo一个调用synchronized方法,一个调用sayHello()方法的话,则数据为交叉输出。

      举例1和举例2的对象锁都是synchronized(this)中this,即当前对象。

      举例3.  

public class TestSyncNeverPrint extends Thread{
	private int val;
	public TestSyncNeverPrint (int val) {
		this.val = val;
	}
	
	public synchronized void printVal (int val) {
		while (true) {
			System.out.println(val);
		}
	}
	/**
	 * 
	 * 在该程序中1和3会交替输出,而2永远都不会输出
	 */
	public static void main(String[] args) {
		TestSyncNeverPrint test = new TestSyncNeverPrint(1);
		test.start();
		Bar bar = new Bar(test);
		bar.start();
		TestSyncNeverPrint other = new TestSyncNeverPrint(3);
		other.start();
	}
}

class Bar extends Thread {
	private TestSyncNeverPrint test ;
	public Bar (TestSyncNeverPrint test) {
		this.test = test;
	}
	public void run () {
		test.printVal(2);
	}
}

       记住,同步机制(synchronization)锁定的是对象,而不是函数或代码。函数或代码区段被声明为synchronization 并非意味它在同一时刻只能由一个线程执行。

       Java 语言不允许你将构造方法声明为synchronized(那么做会发生编译错误)。原因是当两个线程并发调用同一个构造函数时,它们各自操控的是同一个class 的两个不同实体(对象)的内存

      举例4.

public class MyRunnable implements Runnable {
	byte [] bytes;
	public MyRunnable (byte [] bytes) {
		this.bytes = bytes;
	}
	@Override
	public void run() {	
		synchronized(bytes) {	//此处为methodThree中提到到object reference
			for (int i = 0; i < 10; i++) {
				System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		byte [] bytes = new byte[0];
		MyRunnable run = new MyRunnable(bytes);
		Thread tone = new Thread(run, "threadOne");
		tone.start();
		MyRunnable run2 = new MyRunnable(bytes);
		Thread ttwo = new Thread(run2, "threadTwo");
		ttwo.start();
	}
}

       说明:两个不同的线程tone和ttwo执行run()方法时,虽说目标对象不同了(一个为对象run、一个为对象run2),但是在run()方法中同步代码块synchronized(bytes)锁定的对象是bytes,对于两个线程来说,该bytes对象是完全相同的对象,故只能有一个线程获得该对象的lock。

<<To Be Continued>>

分享到:
评论

相关推荐

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition.Sep.2004.eBook-DDU Java Thread Programming (Sams) java线程第二版中英文 java线程第二版中英文 ...

    java 线程工具类 java 线程工具类

    java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具...

    java线程.pdf

    下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java应用程序至少会有一个线程,即主线程,用于执行程序的主要逻辑。通过创建多个...

    Java线程状态流转图

    Java线程状态流转图是一种用于描述Java线程生命周期中不同的状态和状态转换的图形表示方式。该图形展示了Java线程从创建到终止的整个生命周期,并详细介绍了每种状态的特点和转换规则。 NEW(初始化状态) 在Java...

    Java线程详解大全

    Java线程是并发编程的核心部分,它允许程序在同一时间执行多个独立的任务,从而提高系统效率和响应速度。本文将深入探讨Java线程的概念、生命周期、实现方式以及相关的同步机制。 首先,理解线程的基本概念至关重要...

    Java线程.ppt

    Java线程是Java编程中的重要概念,特别是在多核处理器和并发处理中不可或缺。Java线程允许程序在同一时间执行多个不同的任务,从而提高了程序的效率和响应性。在燕山大学信息学院计算机系的课程中,李峰教授讲解了...

    java 线程 dump 分析工具 2.3.3

    Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的统计信息,提供关于锁定监视器...

    java多线程Demo

    Java线程有10个优先级(MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY),默认优先级是NORM_PRIORITY。但是,线程优先级并不保证绝对的执行顺序,操作系统调度策略可能影响实际执行顺序。 7. join()方法: 一个线程...

    Java线程(第三版)

    《Java线程(第三版)》是一本深入探讨Java线程技术的专业书籍,旨在帮助开发者理解和掌握Java平台上的多线程编程。Java线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而充分利用系统资源,提高程序的...

    java线程分析工具TDA

    TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的阻塞情况、等待资源、死锁等问题。下面将详细介绍TDA的使用方法、功能以及它如何帮助我们...

    Java线程使用教程

    Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的并发性和效率。本教程将深入探讨Java线程的使用,帮助开发者掌握这一关键技术。 一、线程基础 1. **线程的概念**:线程...

    java线程实例 各种小Demo

    Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而提高系统效率和响应速度。在Java中,线程可以分为用户线程和守护线程,前者是程序运行的基础,而后者是在所有用户线程结束时才终止的后台...

    JAVA线程dump的分析

    JAVA线程dump的分析 JAVA线程dump是指在JAVA程序中,当前线程的状态和调用堆栈的快照,能够帮助开发者了解当前程序的执行情况,诊断问题和性能瓶颈。生成JAVA线程dump的方法在不同的操作系统下是不同的,在Windows...

    java线程深入解析

    Java线程是Java编程语言中的核心概念,尤其在多任务处理和并发编程中扮演着重要角色。线程允许一个程序内部同时执行多个独立的控制流,使得程序能够更高效地利用处理器资源。本文将深入解析Java线程的相关知识点,...

    JAVA单线程多线程

    在Java编程环境中,单线程指的是程序执行过程中只有一个线程在运行。这意味着任何时刻只能执行一个任务,上一个任务完成后才会进行下一个任务。单线程模型简化了程序设计,降低了程序复杂度,使得开发者可以更专注于...

    java线程入门级书籍

    #### 一、Java线程基础知识概述 **1.1 什么是线程?** 线程是程序执行流的最小单元,是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。在Java中,线程是一种轻量级的进程,...

    java 线程相关工具类

    java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类....

    java线程文档大全

    Java线程是多任务编程中的核心概念,它允许程序同时执行多个不同的任务,极大地提高了程序的效率和响应性。在Java中,线程是通过Java.lang.Thread类或实现Runnable接口来创建和管理的。这份“java线程文档大全”包含...

    JAVA线程学习(源代码)

    本资源"JAVA线程学习(源代码)"提供了关于Java线程的源代码示例,帮助我们深入理解和实践线程的使用。 首先,我们要理解Java中的线程模型。Java线程由`java.lang.Thread`类或`java.util.concurrent.Executor`框架来...

    Java线程培训资料

    #### 一、Java线程基本概念 1. **如何编写与启动线程** - **方式一:继承Thread类** ```java class MyThread extends Thread { @Override public void run() { // 业务逻辑 } } new MyThread().start(); ...

Global site tag (gtag.js) - Google Analytics