Java-----多线程同步和锁

本文深入探讨了线程同步的重要性及其实现方式,包括锁的概念、同步代码块与同步方法的应用等,并通过具体示例展示了如何避免竞态条件,确保线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程同步小节:

1、线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有且仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。


锁的原理

Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步,当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。 一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,
直到该锁被释放。释放锁是指持锁线程退出了synchronized同步方法或代码块。

关于锁和同步,有一下几个要点:
1、只能同步方法,而不能同步变量和类;
2、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6、线程睡眠时,它所持的任何锁都不会释放。
7、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。

9、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。



比如说对于火车站买票系统,有下面的代码:

public class lzwCode implements Runnable {
	
	private int count = 5;
	
	public void run() {
		for (int i = 0; i < 10; ++i) {
			if (count > 0) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(count--);
			}
		}
	}

	public static void main(String[] args) {
		lzwCode he = new lzwCode();
		Thread h1 = new Thread(he);
		Thread h2 = new Thread(he);
		Thread h3 = new Thread(he);
		h1.start();
		h2.start();
		h3.start();
	}
}

控制台结果:

5

4

3

2

1

0

-1

这里出现了-1,显然这个是错的。应该票数不能为负值。

如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,

其他的线程必须等到这个线程结束之后才能继续执行。


【使用线程同步解决问题】

采用同步的话,可以使用同步代码块和同步方法两种来完成。

 

【同步代码块】:

语法格式:

synchronized(同步对象){

 //需要同步的代码

}

但是一般都把当前对象this作为同步对象。

比如对于上面的买票的问题,如下:

public class lzwCode implements Runnable {
	
	private int count = 5;
	
	public void run() {
		synchronized(this) {
			for (int i = 0; i < 10; ++i) {
				if (count > 0) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(count--);
				}
			}
		}
	}

	public static void main(String[] args) {
		lzwCode he = new lzwCode();
		Thread h1 = new Thread(he);
		Thread h2 = new Thread(he);
		Thread h3 = new Thread(he);
		h1.start();
		h2.start();
		h3.start();
	}
}

控制台结果:

5

4

3

2

1


【同步方法】

也可以采用同步方法。

语法格式为synchronized 方法返回类型 方法名(参数列表){

    // 其他代码

}

public class lzwCode implements Runnable {
	
	private int count = 5;
	
	public void run() {
		for (int i = 0; i < 10; ++i) {
			sale();
		}
	}

	public synchronized void sale() {	
		if (count > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(count--);
		}
	}

	public static void main(String[] args) {
		lzwCode he = new lzwCode();
		Thread h1 = new Thread(he);
		Thread h2 = new Thread(he);
		Thread h3 = new Thread(he);
		h1.start();
		h2.start();
		h3.start();
	}
}

控制台结果:

5

4

3

2

1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值