`

Java多线程基础总结一: synchronized

阅读更多

    最近写关于并发的小应用,才发现真的该好好的正视java的多线程了。之前没有深入的掌握,用起来也是那么的吃力。作为J2SE里面为 数不多的重要难点之一,多线程应用一直是我以敬畏的心态去尽量避开的,只是通过一些实例掌握一些简单的应用。这段时间会多用点时间 去掌握,有需要写下来的我也会通过这种方式既分享又加深理解。
    首先这篇只涉及基础的知识整理,对于并发包java.util.concurrent内的线程池和锁我会看情况在之后的总结中写点东西。对于进程的 概念我们都很熟悉,它是应用程序级的隔离,不同的应用程序之间的进程几乎不共享任何资源。而线程则可以说是应用程序内的隔离,一种 相对低级别的隔离。一个进程可以有多个线程,它们之间隔离的内容大致包括:a.自身的堆栈,b.程序计数器,c.局部变量;共享应用的内 容大致包括:a.内存,b.文件句柄,c.进程状态等。线程不是Java自身的概念,它是操作系统底层的概念。Java作为一种应用语言把线程的 操作通过API提升到应用开发的支持,但是在并发性的支持上并不是那么美好。
Java在设计时,每个对象都有一个隐式的锁,这个锁的使用则是通过synchronized关键字来显式的使用。在JDK5.0以后引用了 java.util.concurrent.ReentrantLock作为synchronized之外的选择,配和Condition可以以一种条件锁的机制来管理并发的线程,之后的 总结再介绍。提到synchronized,多数的初学者都知道Object的 wait(),notify(),notifyAll()是配和其使用的,但是为什么要在同步内 才能用对象的这些方法呢(不然抛 IllegalMonitorStateException)?
   我想因为没有synchronized让对象的隐式锁发挥作用,那么方法或者方法块内的线程在同一时间可能存在多个,假设wait()可用,它会 把这些线程统统的加到wait set中等待被唤醒,这样永远没有多余的线程去唤醒它们。每个对象管理调用其wait(),notify()的线程,使得 别的对象即使想帮忙也帮不上忙。这样的结果就是多线程永远完成不了多任务,基于此Java在设计时使其必须与synchronized一起使用,这 样获得隐式锁的线程同一时间只有一个,当此线程被对象的wait()扔到wait set中时,线程会释放这个对象的隐式锁等待被唤醒的机会,这 样的设计会大大降低死锁。另外同一个对象隐式锁作用下的多个方法或者方法块在没有锁的限制下可以同时允许多个线程在不同的方法内 wait和notify,严重的竞争条件使得死锁轻而易举。所以Java设计者试图通过Monitor Object模式解决这些问题,每个对象都是Monitor用 于监视拥有其使用权的线程。
    但是synchronized这种获得隐式锁的方式本身也是有隐患问题的:a.不能中断正在试图获得锁的线程,b.试图获得锁时不能设定超时, c.每个锁只有一个条件太少。对于最后一项的设计前面提到的JDK5的方案是可以弥补的,一个ReentrantLock可以有多个Condition,每个条 件管理获得对象锁满足条件的线程,通过await(),signalAll()使只关于Condition自己放倒的线程继续运行,或者放倒一些线程,而不是全 部唤醒等等。但对于前两者的极端情况会出现死锁。下面的这个例子:

class DeadLockSample{
   public final Object lock1 = new Object();
   public final Object lock2 = new Object();

   public void methodOne(){
     synchronized(lock1){
      ...
      synchronized(lock2){...}
     }
   }

   public void methodTwo(){
     synchronized(lock2){
   ...
      synchronized(lock1){...}
     }
   }
}

假设场景:线程A调用methodOne(),获得lock1的隐式锁后,在获得lock2的隐式锁之前线程B进入运行,调用 methodTwo(),抢先获得了 lock2的隐式锁,此时线程A等着线程B交出lock2,线程B等着lock1进入方法块,死锁就这样被创造出来了。
以上的例子不直观的话,再看一个实例顺便看看wait()的缺陷:

package thread;

import java.util.LinkedList;
import java.util.List;

public class DeadLockSample {
	public static void main(String[] args) {
		final WaitAndNotify wan = new WaitAndNotify();

		Thread t1 = new Thread(new Runnable() {
			public void run() {
				wan.pop();
			}
		});

		Thread t2 = new Thread(new Runnable() {
			public void run() {
				wan.push("a");
			}
		});

		t1.start();
		t2.start();
	}
}

class WaitAndNotify {

	final List<String> list = new LinkedList<String>();

	public synchronized void push(String x) {
		synchronized (list) {
			list.add(x);
			notify();
		}
	}

	public synchronized Object pop() {
		synchronized (list) {
			if (list.size() <= 0) {
				try {
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			return list.size();
		}
	}

}


   上面的这个例子也会出现死锁,为什么呢?首先看WaitAndNotify这个类,在push和pop方法上有synchronized关键字,方法内部也有 synchronized,那么当WaitAndNotify实例化时会有两个对象的隐式锁,一个是WaitAndNotify对象自身的,作用在方法上;另一个就是方法 内部同步用到的list的。主线程开启两个线程t1和t2,t1进入pop方法此时list为空,它先后获得了wan和 list的隐式锁,接着就被wait扔 进wait set等待去了。注意这个wait()方法是谁的?答案是wan的,所以它释放了wan的隐式锁,但是把list的死死的抓着不放。此时t2终于 得到了 wan的隐式锁进入push方法,但是不幸的是list的隐式锁它这辈子也得不到了。。。
就是由于wait的设计是针对对象管理线程的,而又没有其他的可以类似栈的方式层层释放锁,导致死锁的杯具了。关于synchronized和 wait(),notify(),notifyAll()的故事,我想我能瞎编的暂时这么多了,不然滔滔江水就又杯具了,哈哈。下面简单的讲讲 java.lang.Thread的特色小故事。(待续...)


ref:http://www.bianceng.cn/Programming/Java/201206/34145_2.htm
分享到:
评论

相关推荐

    Java多线程知识点总结

    Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...

    Java多线程编程总结

    ### Java多线程编程总结 #### 一、Java线程:概念与原理 1. **操作系统中线程和进程的概念** - 当前的操作系统通常为多任务操作系统,多线程是实现多任务的一种手段。 - **进程**:指内存中运行的应用程序,每个...

    JAVAJAVA多线程教学演示系统论文

    《JAVA多线程教学演示系统》是一篇深入探讨JAVA多线程编程的论文,它针对教育领域中的教学需求,提供了一种生动、直观的演示方式,帮助学生更好地理解和掌握多线程技术。这篇论文的核心内容可能包括以下几个方面: ...

    Java 多线程学习总结归纳(附代码)

    以上内容涵盖了Java多线程的基础知识,包括创建、同步、终止、线程安全和并发控制等方面。通过实际的代码实践,可以深入理解并掌握这些概念,提升多线程编程的能力。同时,文档中的代码示例能帮助读者更好地理解和...

    java 多线程synchronized互斥锁demo

    标题中的"java 多线程synchronized互斥锁demo"指的是一个示例,展示了如何在多线程环境下使用`synchronized`关键字创建互斥锁,确保同一时间只有一个线程可以访问特定的代码块或方法。 描述中的"一个多线程访问的同...

    Java多线程的总结

    Java多线程是Java编程中的一个核心概念,它在现代软件开发中扮演着至关重要的角色。多线程允许程序同时执行多个任务,提高了系统资源的利用率,提升了应用程序的响应速度和并发性能。对于大型分布式系统、Web应用...

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...

    深入学习:Java多线程编程

    《深入学习:Java多线程编程》是一本专注于Java并发技术的专业书籍,旨在帮助开发者深入理解和熟练运用Java中的多线程编程。Java多线程是Java编程中的核心部分,尤其在现代高性能应用和分布式系统中不可或缺。理解并...

    java多线程Demo

    Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当我们创建一个新...

    JAVA单线程多线程

    通过以上分析,我们可以看到,在Java中合理利用单线程或多线程以及`synchronized`关键字能够有效地提高程序的性能和响应能力,同时也需要注意其可能带来的复杂性和资源消耗问题。开发者应根据具体的应用场景和需求来...

    java多线程经典案例

    通过分析并实践`threadTest`案例,我们可以深入理解Java多线程的原理和使用技巧,为编写高效并发程序打下坚实基础。同时,也要注意多线程编程中的死锁、活锁和饥饿等问题,合理设计线程间的交互,避免出现不可预期的...

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

    Java多线程机制是Java编程中至关重要的一部分,它允许程序同时执行多个任务,提升应用程序的效率和响应性。以下是对各个知识点的详细说明: 9.1 Java中的线程: Java程序中的线程是在操作系统级别的线程基础上进行...

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

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

    《Java程序设计基础》章:多线程处理(与“线程”有关文档共45张).pptx

    Java程序设计基础中的多线程处理是编程领域中至关重要的一部分,尤其对于Java开发者而言。多线程允许程序同时执行多个任务,从而提高了程序的效率和响应性。在讲解这一主题时,我们首先需要理解线程的基本概念。 1....

    Java多线程结构_Java多线程结构_

    Java多线程结构是Java编程中的重要组成部分,它允许程序同时执行多个任务,提升系统效率。在Java中,实现多线程主要有两种方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当自定义类继承Thread类时,...

Global site tag (gtag.js) - Google Analytics