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

Java线程(二)

    博客分类:
  • JAVA
阅读更多

浅谈synchronized应用于类方法和类字面量之上

       当调用一个synchronized static函数时,获得的lock将与定义该方法的class的Class对象相关联,而不是与调用方法的那个实例对象相关联。当你对一个class literal调用其synchronized代码块时,获得的也是同样那个lock,与特定Class对象相关联的lock,类的所有实例共享 。

public class TestSyncLock {
	public synchronized static void methodOne() { /** 修饰方法 */
			/**
			 * ......
			 */
	}

	public void methodTwo() {		/** 修饰object reference */
		synchronized (TestSyncLock.class) { 
			/**
			 *  ...... 
			 */
		}
	}

	public void methodThree() { 	/** 修饰class literal*/
		synchronized (syncObj) { 	/** syncObj应该为static字面量*/
			/**
			 * ...... 
			 */
		} 
	}
}

       methodOne() 和methodTwo()争取的是同一个lock,也就是TestSyncLock的Class object lock。methodOne()通过synchronized的修饰符取得该lock,methodTwo()则通过class literal TestSyncLock.class得到它。如果synchronized施行于instance方法和object references,得到的lock就与前面的不一样了。对于instance函数,取得的lock隶属于其调用者(某个对象),至于同步控制一个对象(synchronized(syncObj)),取得的当然是该对象(syncObj)的lock。

同步控制(1)instance 方法(2)static 方法(3)对象(object) (4)class literals时得到的lock不同

       举例1.

public class MyRunnable implements Runnable {
	public MyRunnable() {
	}

	private synchronized void printVal() {
		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();
			}
		}
	}

	@Override
	public void run() {
		printVal();
	}

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

       说明:上述代码由于是synchronized instance方法,故使用的是instance对象锁,不同的对象具有不同的对象锁。线程tone和线程ttwo使用的是目标对象run和run2的 run()方法,在run()方法中调用了printVal()方法,由于线程访问的是不同对象(run和run2)的synchronized方法,故两个线程可以并行执行,无需等待对象锁。

      如果我们将上面的代码略加修改,即:

private synchronized void printVal() ===> private synchronized static void printVal()

       则其输出就不同了,分别输出,只有一个输出完毕后另一个才会输出。此处我们还假设tone先执行(尽管两个线程tone和ttwo的运行顺序是不定的 ),当tone执行synchronized static void printVal()方法的时候,将会获得类TestSyncLock的Class object lock。当后运行的线程ttwo也执行run()方法时,尽管它们两个访问的对象不同,但是线程ttwo执行到方法printVal()的时候,发现其修饰符为synchronized static,此时它也要求类TestSyncLock的Class object lock,但该锁已经被线程tone锁持有,故ttwo只能等待tone执行完毕后释放该锁。

     举例2.

public class MyRunnable implements Runnable {
	private static byte[] bytes = new byte[0];

	@Override
	public void run() {
		synchronized (bytes) {		//注意这里的bytes为static
			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");
		tone.start();
		MyRunnable run2 = new MyRunnable();
		Thread ttwo = new Thread(run2, "threadTwo");
		ttwo.start();
	}
}

       说明:经修改后实际和synchronized static void 方法的效果是一样的。

       举例3.

public class TestStaticInstanceRunnable implements Runnable {
	public synchronized void printMOne() {
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " printMOne .");
		}
	}

	public synchronized static void printMTwo() {
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " printMTwo .");
		}
	}

	public void run() {
		printMOne();
	}

	@SuppressWarnings("static-access")
	public static void main(String[] args) {
		TestStaticInstanceRunnable run = new TestStaticInstanceRunnable();
		Thread thread = new Thread(run, "thread");
		thread.start();
		run.printMTwo();
	}
}

       说明:在输出时线程main和线程thread交替输出。 线程thread仅仅调用synchronized instance(此处的对象为run)方法printMOne(),在无限循环中打印字符串"printMOne"。主线程main则调用了(此处也是对象run)synchronized static方法printMTwo(),它也进入自身的无限循环中,打印字符串"printMTwo"。

       你或许会以为:由于这两个线程(main线程和thread线程)的执行过程中访问的是同一个对象(run)的两个不同synchronized方法,故无法在上述两方法之间交替。这段代码的输出不是字符串"printMOne"就是字符串"printMTwo"一一取决于哪个线程率先进入其synchronized方法并获得lock。

       然而,当上述代码执行起来,两个字符串都打印在屏幕上,也就是说,两个方法并发执行。

       解析:尽管上述两个方法都声明为synchronized,但它们并非线程安全(thread safe)的,其原因在于一个是synchronized static方法,另一个是synchronized instance方法,因此它们争取的是不同的locks。instance方法printMOne()取得的是TestStaticInstanceRunnable object lock,
static方法printMTwo()取得的是TestStaticInstanceRunnable的Class object lock,是不同的两个locks,彼此互不
影响。

       如果希望上述代码在多线程情况下只有一个线程可以得到锁的话,必须为synchronized锁定的是相同的(相同的instance object或Class object),将方法printOne修改成如下形式,则两个方法是用相同的Class object lock。

public void printMOne() {
	synchronized(TestStaticInstance.class) {
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + " printMOne .");
		}
	}
}	

       如果需要同步控制这段代码,又该怎么办呢?两种选择可以解决这个问题:

       1.同步控制(synchronize)公用资源。       
       2.同步控制(synchronize)—个特殊的instance变量。

       对于1涉及在函数中添加synchronized语句,以便同步控制共享资源。假设两个方法要更新同一个对象,它们就对其进行同步控制。

public class TestSyncLock {
	private SyncObj syncObj;
	public void methodOne () {			
		synchronized (syncObj) {
			/**
			 * ......
			 */
		}
	}
	public static void methodTwo (TestSyncLock syncLock) {
		synchronized (syncLock.syncObj) {
			/**
			 * ......
			 */
		}
	}
}

       对于2涉及声明一个local instance 变量,惟一的目的就是对它进行同步控制。

public class TestSyncLock {
	private byte [] lock = new byte[0];
	public void methodOne () {			
		synchronized (lock) {
			/**
			 * ......
			 */
		}
	}
	public static void methodTwo (TestSyncLock syncLock) {
		synchronized (syncLock.lock) {
			/**
			 * ......
			 */
		}
	}
}

       由于你只能锁定对象,你使用的local instance 变量必须是个对象。至于为什么使用一个元素个数为0的字节数组,其原因是字节数组比其他任何对象都经济。

       上述两种做法中的任何一种都使代码成为线程安全(thread safe)的。同步控制通过instance方法或object reference所取得的lock,完全不同于同步控制通过static方法或class literal 所取得的lock。两个方法被声明为synchronized并不就意味它们具备线程安全性。

public class TestStaticInstanceRunnable implements Runnable {
	private byte [] bytes = new byte[0];          //关键在于多线程中锁一定是相同的
	public void printMOne() {
		synchronized(bytes) {
			while (true) {
				/** ...... */
			}
		}
	}

	public static void printMTwo(TestStaticInstanceRunnable run) {
		synchronized(run.bytes) {
			while (true) {
				/** ...... */
			}
		}
	}

	public void run() {
		printMOne();
	}

	@SuppressWarnings("static-access")
	public static void main(String[] args) {
		TestStaticInstanceRunnable run = new TestStaticInstanceRunnable();
		Thread thread = new Thread(run, "thread");
		thread.start();
		run.printMTwo(run);
	}
}

  <<To Be Continued>>

分享到:
评论

相关推荐

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

    java线程第二版中英文 java线程第二版中英文 线程并不是新的概念:许多操作系统和语言都支持它们。在Java出现以前,似乎人人都在谈论线程,却很少有人使用它。用线程编程是技巧性很强的且不可移植。 而在Java中却...

    Java线程讲解Java线程讲解

    Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解

    Java线程详解大全

    Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程

    Java线程Java线程Java线程Java线程

    Java线程Java线程Java线程Java线程Java线程Java线程

    java多线程编程总结

    Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 ...

    java 线程java 线程

    java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程

    Java多线程编程总结

    Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-...

    Java线程高清晰中文第二版

    在Java出现以前,似乎人人都在谈论线程,却很少有人使用它。用线程编程是技巧性很强的且不可移植。 而在Java中却完全不同。Java的线程工具易于使用,并且像Java中的其他东西一样可以在不同的平台之间移植。这是一件...

    Java线程模块Java线程之秒表

    Java线程模块Java线程之秒表新手学习Java线程模块时,利用Java中设置线程的暂停间隔,做的简易秒表

    java多线程笔记

    Java线程:概念与原理 2 一、操作系统中线程和进程的概念 2 二、Java中的线程 3 三、Java中关于线程的名词解释 3 四、线程的状态转换和生命周期 4 Java线程:创建与启动 7 Java线程:线程名称的设定及获取 10 Java...

    java线程同步java线程同步

    java线程同步java线程同步java线程同步

    java 线程 dump 分析工具 2.3.3

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

    java线程 线程学习资料 java线程教程

    java线程 线程 教程 java线程教程 java线程学习资料 本教程有什么内容? 本教程研究了线程的基础知识— 线程是什么、线程为什么有用以及怎么开始编写使用线程的简单 程序。 我们还将研究更复杂的、使用线程的应用...

    Java的线程和Java AppletJava的线程和Java AppletJava的线程和Java Applet

    Java的线程和Java AppletJava的线程和Java AppletJava的线程和Java AppletJava的线程和Java AppletJava的线程和Java Applet

    java线程.pdf

    java线程.pdf java 学习java

    Java线程Java线程

    java 线程 新手java 线程 新手java 线程 新手java 线程 新手

    java线程分析工具TDA

    分析java线程日志的工具,使用jstack把java线程日志dump下来,然后上传到该工具,就可以查看线程阻塞情况等信息。

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    Java 模拟线程并发

    Java 模拟线程并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发

    java线程入门,java线程入门

    java线程入门java线程入门java线程入门java线程入门java线程入门java线程入门java线程入门java线程入门

Global site tag (gtag.js) - Google Analytics