`

初探多线程之线程同步

    博客分类:
  • java
阅读更多

1.synchronized同步代码块

   synchronized(obj){

          .....

         //同步代码块的代码

   }

直接对obj对象同步监听,任何线程在修改指定的资源之前,首先对该资源加锁,在加锁期间其他线程无法修改该资源。

2.synchronized同步方法

  1)类里的对象可以被多线程安全访问

 

public synchronized void  draw(double drawAmount){
		if (this.balance >= drawAmount) {
			System.out.println(Thread.currentThread().getName() + "取钱成功!吐出现金:" + drawAmount);

			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			this.balance = this.balance - drawAmount;
			System.out.println("\t余额为:" + balance);
		} else {
			System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足!");
		}
	}

 总结:

 

    1)synchronized可修饰方法,可修饰代码块,但不能修饰构造器,属性。

    2)不要对线程安全的所有方法都进行同步。

3.同步锁(Lock)

   显式加锁,显式解锁

   ReentrantLock(可重入锁):一个线程可以对已被加锁的ReentrantLock锁再次加锁,ReentrantLock对象会维持一个计数器来追踪lock()方法的嵌套调用。

  

public class Account {
	
	private final ReentrantLock lock = new ReentrantLock();

	public void draw(double drawAmount) {
		//上锁
		lock.lock();
		try {
			......//操作代码
		} finally {
			//解锁
			lock.unlock();
		}
	}

}

 

   4.死锁问题

     当两个线程互相等待对方释放同步监视器时就会发生死锁,当陷入死锁,所有线程处于阻塞状态,无法继续。

    四个必要条件:

    1)互斥条件:进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    2)请求和保持条件:进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
    3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
    4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
    传统的线程通信:
       wait():导致当前线程等待
       notify():唤醒等待线程中的单个线程
       notifyAll():唤醒等待线程中的所有线程
public class Account {
	private String accountNo;
	private double balance;
	private boolean flag = false;

	public Account(String accountNo, double balance) {
		this.accountNo = accountNo;
		this.balance = balance;
	}

	public int hashCode() {
		return accountNo.hashCode();
	}

	public boolean equals(Object object) {
		if (this == object) {
			return true;
		}

		if (object != null && object.getClass() == this.getClass()) {
			Account account = (Account) object;
			return account.getAccountNo().equals(account);
		}

		return false;
	}

	public String getAccountNo() {
		return accountNo;
	}

	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}

	public double getBalance() {
		return balance;
	}

	public synchronized void draw(double drawAmount) {
		try {
			if (!flag) {
				wait();
			} else {
				if (this.balance >= drawAmount) {
					System.out.println(Thread.currentThread().getName()
							+ "取钱成功!吐出现金:" + drawAmount);

					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					this.balance = this.balance - drawAmount;
					System.out.println("\t余额为:" + balance);
					
					flag = false;
					notifyAll();
				} else {
					System.out.println(Thread.currentThread().getName()
							+ "取钱失败,余额不足!");
				}
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void deposit(double depositAmount) {
		try {
			if (flag) {
				wait();
			} else {
				System.out.println(Thread.currentThread().getName() + "存款:"
						+ depositAmount);

				balance += depositAmount;
				System.out.println("\t余额为:" + balance);
				flag = true;
				notifyAll();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
 
    使用Condition类控制线程通信
    Condition实例的方法:
       await():类似于隐式同步监视器上的wait()方法
       signal():类似于notify()方法
       signalAll():类似于notifyAll()方法
public class Account {
	private String accountNo;
	private double balance;
	private boolean flag = false;

	private final Lock lock = new ReentrantLock();
	private final Condition condition = lock.newCondition();

	public Account(String accountNo, double balance) {
		this.accountNo = accountNo;
		this.balance = balance;
	}

	public int hashCode() {
		return accountNo.hashCode();
	}

	public boolean equals(Object object) {
		if (this == object) {
			return true;
		}

		if (object != null && object.getClass() == this.getClass()) {
			Account account = (Account) object;
			return account.getAccountNo().equals(account);
		}

		return false;
	}

	public String getAccountNo() {
		return accountNo;
	}

	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}

	public double getBalance() {
		return balance;
	}

	public void draw(double drawAmount) {
		lock.lock();
		try {
			if (!flag) {
				condition.await();
			} else {
				if (this.balance >= drawAmount) {
					System.out.println(Thread.currentThread().getName() + "取钱成功!吐出现金:" + drawAmount);
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					this.balance = this.balance - drawAmount;
					System.out.println("\t余额为:" + balance);

					flag = false;
					condition.signalAll();
				} else {
					System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足!");
				}
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public synchronized void deposit(double depositAmount) {
		lock.lock();
		try {
			if (flag) {
				condition.await();
			} else {
				System.out.println(Thread.currentThread().getName() + "存款:" + depositAmount);

				balance += depositAmount;
				System.out.println("\t余额为:" + balance);
				flag = true;
				condition.signalAll();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

}
    显式和隐式线程通信
分享到:
评论

相关推荐

    Vchome资料库--多线程技术篇(CHM)

    Win32 多线程的性能(1)... 1 <br/>Win32 多线程的性能(2)... 10 <br/>关于多线程的一些细节... 23 <br/>用VC++5.0 实 现 多 线 程 的 调 度 和 处 理... 25 <br/>一 多 任 务, 多 进 程 和 多 ...

    C语言文件操作、多线程编程和网络编程.md

    目录: ...多线程编程初探 3.1 线程的基本概念 3.2 创建与同步线程 网络编程基础 4.1 套接字的概念与创建 4.2 服务器与客户端通信 高级文件操作 5.1 顺序文件与随机文件 5.2 文件的读写位置与偏移

    C语言高级编程技巧与数据结构.md

    目录: 函数指针与回调函数 1.1 函数指针的定义与使用 1.2 回调函数的概念 枚举与位操作 2.1 枚举类型的定义与应用 2.2 位操作与位运算 ...多线程编程初探 6.1 线程的基本概念 6.2 创建与同步线程

    嵌入式Linux应用程序开发标准教程(第2版全)

    第9章 多线程编程 9.1 Linux线程概述 9.1.1 线程概述 9.1.2 线程机制的分类和特性 9.2 Linux线程编程 9.2.1 线程基本编程 9.2.2 线程之间的同步与互斥 9.2.3 线程属性 9.3 实验内容——“生产者消费者”实验 9.4 本...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    对Java语言的每个语法都提供了一个或多个例程讲解 大量使用流程图表示程序的执行过程,使用结构图表示程序的内部状态 每章最后都给出了典型的练习题,让读者及时练习,巩固提高,并提供了参考答案 目录 第1篇 ...

    asp.net知识库

    通过作业,定时同步两个数据库 SQLSERVER高级注入技巧 利用反射实现ASP.NET控件和数据实体之间的双向绑定,并且在客户端自动验证输入的内容是否合法 asp.net报表解决方法 SQLDMO类的使用 SQL过程自动C#封装,支持从表到...

    亮剑.NET深入体验与实战精要2

    4.10 进程与多线程的区别 190 4.11 创建多线程应用程序 191 4.12 WinForm开发常见问题 194 4.12.1 如何设置运行时窗体的起始位置 194 4.12.2 如何使一个窗体在屏幕的最顶端 194 4.12.3 实现窗体渐显效果 194 4.12.4 ...

    亮剑.NET深入体验与实战精要3

    4.10 进程与多线程的区别 190 4.11 创建多线程应用程序 191 4.12 WinForm开发常见问题 194 4.12.1 如何设置运行时窗体的起始位置 194 4.12.2 如何使一个窗体在屏幕的最顶端 194 4.12.3 实现窗体渐显效果 194 4.12.4 ...

    传智播客扫地僧视频讲义源码

    13_干活要知道在什么框架之下干活 14_结构体类型和变量定义及基本操作 15_结构体元素做函数参数pk结构指针做函数参数 16_结构体做函数基本操作 17_结构体做函数内存分配指针 18_结构中套一级指针 19_结构体中套二级...

Global site tag (gtag.js) - Google Analytics