线程同步技术

一、线程同步的基本概念

1.线程同步是指在访问共享资源时多个线程相互间的协调和控制。

2.线程同步的目的是实现多线程对共享资源有序可控访问,保障共享资源数据安全、避免死锁使整个系统正常运作。

3.实现线程同步的方式是互斥和协作。

**注意:并行程序设计中,共享资源越多,程序设计将会越复杂,所以应尽可能的减少共享资源的使用。

二、线程的互斥同步

1.当有多个并行线程执行时,由于线程占用和放弃CPU在微观上无法预知,所以对于共享数据的插入、删除、更新等操作,若不采取一定措施的话,所取得的数据很有可能不正确。

2.为了避免这种情况的发生,Java提供了一种同步机制的办法即互斥,是用synchronized关键字即加锁的方式来实现的。

3.互斥指临界资源同时只允许一个线程对其进行访问,具有唯一性和排它性,保证了线程对临界资源访问的原子性。

4.synchronized关键字的作用是:确保在同时只有一个线程执行特定的代码块,即实现该代码块的原子性。

5.使用synchronized的三种使用方式:

①同步代码块。

②同步实例方法。

③同步类方法(静态方法)。

6.同步代码块

①被synchronized包围的代码块称为同步代码块;

②synchronized代码块需要接收一个参数,该参数是一个对象,可以称为该代码块的锁对象(又称同步锁);

③任何线程持有锁对象时,方可执行该锁对象锁定的代码块;

④如果某线程一旦持有锁对象,其它线程即不能同时持有该锁对象,从而实现线程基于特定锁对象的互斥。

**注意:线程持有占锁对象并不影响其它线程访问该锁对象的方法和属性。 多个线程以竞争的方式争夺锁对象的监视器,线程执行完该代码块,即释放锁对象的监视器 。

7.同步实例方法

①public synchronized void show(){//功能代码}。

②就是在方法前面加上关键字synchronized,即实现了方法的同步,相当于整个方法是一个同步代码块,其锁对象是this。

8.同步静态方法

①写法:public synchronized static void fun(){//…}。

②就是在静态方法前面加上关键字synchronized,也是将整个方法看做一个同步代码块,其锁对象较特殊,相当于以所在类为锁对象。

三、死锁

1.当两个线程循环依赖于一对同步对象时将发生死锁。

2.例如:一个线程进入对象obj1上的监视器,并在等待对象obj2上的监视器;而同时另一个线程进入对象obj2上的监视器,并在等待对象obj1上的监视器,即发生死锁。

3.死锁很少发生,但一旦发生就很难调试。

四、线程协作

1.线程协作是多线程互斥同步的基础上,使线程之间依照一定条件,有目的、有计划地交互协同工作,这是一种较高级的线程同步方式。

2.Java提供了一个精心设计的线程间通信机制即wait-notify机制,通过该机制可以实现线程协作。

3.wait-notify机制是通过使用wait()、notify()和notifyAll()三个方法来实现的。

4.这三个方法均定义在Object类中,是final修饰的实例方法。

5.这三个方法必须在synchronized代码中调用,而且只有锁对象才能调用这三个方法。即持有锁对象监视器的线程才能调用锁对象的这三个方法.

6.wait()方法

调用该方法的线程退出监视器并进入等待状态,直到其他线程进入相同的监视器并调用notify( )方法。

7.notify( )

通知等待(该方法所属对象)监视器的(多个)线程中的一个结束等待。 即唤醒一个等待(当前线程所持有)监视器的线程。

8.notifyAll()

通知等待(该方法所属对象)监视器的所有线程结束等待。 即唤醒所有等待(当前线程所持有)监视器的线程。

五、线程协作示例-生产者消费者问题(面包店)

1.看一看面包店的场景

①店面柜橱用来存放面包;

②面包师傅负责将烤好面包放到柜橱中;

③店面伙计负责柜橱中的面包售出;

2.面包店会遇到两种问题

①柜橱放满了,新面包没处放;

②柜橱空了,但是面包仍在销售中。

3.解决问题的办法

①师傅检查橱柜,若橱柜未满放入面包,同时通知伙计有面包卖,若橱柜满师傅暂停工作,直到收到伙计通知可以放面包了。

②伙计检查橱柜,若橱柜未空销售面包,同时通知师傅可以放面包,若橱柜空伙计暂停工作,直到收到师傅通知有面包卖。

4.代码示例

//面包柜
public class Cupboard {

	private int cap;// 柜子容量
	private int size;// 柜子实际存储量

	public Cupboard(int cap) {
		this.cap = cap;
	}

	public synchronized void add() {// 向柜子里面放面包
		while (isFull()) {// 面包师要判断柜子是否满了,满了的话就要等待。对于有多个面包师,不可用if。
			System.out.println(Thread.currentThread().getName() + "柜子已经放满了,有" + size + "个面包,等待中...");
			try {
				this.wait();// this为锁对象,通过其方法让他等待
				System.out.println("销售员被唤醒");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		size++;
		System.out.println(Thread.currentThread().getName() + "向柜子里面放入面包:目前柜子有:(" + size + ")个面包");
		this.notifyAll();// 唤醒所有的销售员,通知卖货。
		Thread.yield();// 给其他线程机会

	}

	public synchronized void remove() {// 从柜子里面取面包
		while (isEmpty()) {// 面包师要判断柜子是否满了,满了的话就要通知销售员售卖,等柜子不空的时候放面包
			System.out.println(Thread.currentThread().getName() + "柜子已经没有面包了");
			try {
				this.wait();
				System.out.println("面包师被唤醒");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		size--;
		System.out.println(Thread.currentThread().getName() + "向柜子里面取走面包:目前柜子有:(" + size + ")个面包");
		this.notifyAll();// 唤醒所有的销售员,通知卖货。
		Thread.yield();// 给其他线程机会
	}

	public boolean isFull() {// 柜子是否满了
		return cap == size;
	}

	public boolean isEmpty() {// 柜子是否空了
		return size == 0;
	}

}
//面包师和销售员
public class MakeAndSale {
	public static void main(String[] args) {
		Cupboard cb = new Cupboard(5);
		for (int i = 1; i < 10; i++) { // 有九个面包师和销售员
			new Thread("面包师" + i + ">") {// 一个线程是面包师进行不断制作面包的操作
				public void run() {
					for (int i = 1; i < 100; i++) {
						cb.add();
					}
				};
			}.start();
			new Thread("销售员" + i + ">") {// 一个线程是销售员师进行不断销售面包的操作
				public void run() {
					for (int i = 0; i < 100; i++) {
						cb.remove();
					}
				};
			}.start();
		}
	}
}

5.运行结果

发布了74 篇原创文章 · 获赞 32 · 访问量 9985

猜你喜欢

转载自blog.csdn.net/qq_41629684/article/details/104311587