java多线程:synchronized

本文介绍了Java中的synchronized关键字的使用方法,包括同步方法和同步代码块,以及如何避免死锁的发生。通过示例代码详细解释了synchronized的原理。

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

一、synchronized的使用
java的synchronized关键字能够修饰方法和语句块,也就是同步方法和同步语句块。synchronized()并不是保护数据不被访问,只是保证同一时刻只有一个线程在运行。

  1. 数据私有
  2. 所有的访问都同步化
  3. 用数据本身做钥匙
    synchronized (object) { // } 钥匙在对象中,而不在代码中。 每个对象有一个钥匙
    为了执行synchronized()块,线程需要得到对象中的钥匙。一旦获得了钥匙,对象就不再拥有钥匙。 如果当线程要执行synchronized()时,钥匙不在对象中,线程就stall了。一直到钥匙还到了对象中,才被这个线程拿到。 当线程离开synchorized()块,钥匙就还给了对象。
    对象锁标志:
    1、每个对象都有一个标志,它可以被认为是“锁标志”。
    2、synchronized允许和锁标志交互。
    释放锁标志:
    1、线程执行到synchronized()代码块末尾时释放
    2、synchronized()代码块抛出中断或异常时自动释放
public class TestSyc {
	public static void main(String[] args) {
		Shop p=new Shop();
		ThreadTest t1=new ThreadTest(p);
		ThreadTest t2=new ThreadTest(p);
		t1.start();
		t2.start();
	}
	static class ThreadTest extends Thread{
		Shop p;
		public ThreadTest(Shop p){
			this.p=p;
		}
		
		@Override
		public void run(){
			p.count();
		}
	}
}

//同步方法/同步代码块:解决多个线程对象操作同一个数据时发生数据紊乱的情况
//能够确保当前只有一个线程对象操作该同步方法/ 同步代码块中的内容
class Shop {
	int num;
	public /*synchronized*/ void count(){//同步方法  默认拿到当前对象的锁
		synchronized (this) {//同步代码块  synchronized(同步锁(对象)){代码}
			num++;
			
			Thread t = Thread.currentThread();
			try {
				t.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(t.getName()+"你是第"+num+"个人来访问的");
		}
	}
}

输出结果:

Thread-1你是第1个人来访问的
Thread-0你是第2个人来访问的

二、死锁
两个线程相互等待来自对方的锁,它不能被监测到或避免。
它可以通过以下方法来避免:
1、决定获取锁的次序
2、始终遵照这个次序
3、按照相反的次序释放锁
下面看一个死锁的例子:

//死锁:两个线程相互等待来自对方的锁
//同步代码块的相互嵌套并不一定会造成死锁
//避免死锁:1、尽量不使用同步代码块的嵌套  2、改变锁的顺序
public class TestDead {
	public static void main(String[] args) {
		Object o1=new Object();
		Object o2=new Object();
		Thread1 t1=new Thread1(o1,o2);
		t1.setName("线程1");
		Thread2 t2=new Thread2(o1,o2);
		t2.setName("线程2");
		
		t1.start();
		t2.start();
	}
	static class Thread1 extends Thread{
		Object o1;
		Object o2;
		
		public Thread1(Object o1,Object o2){
			this.o1=o1;
			this.o2=o2;
		}
		
		@Override
		public void run() {
			synchronized (o1) {
				System.out.println(getName()+"我有我的o1,等待拿你的o2");
				try {
					sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (o2) {
					System.out.println(getName()+"我拿到你的o2");
				}
			}
		}
	}
	static class Thread2 extends Thread{

		Object o1;
		Object o2;
		
		public Thread2(Object o1,Object o2){
			this.o1=o1;
			this.o2=o2;
		}
		@Override
		public void run() {
			synchronized (o2) {
				System.out.println(getName()+"我有我的o2,等待拿你的o1");
				
				synchronized (o1) {
					System.out.println(getName()+"我拿到了o1");
				}
			}
		}
	}
}

三、synchronized的原理
每个对象有个线程池,对象可以调用wait方法让对该对象同步的线程加入到该线程池中,或者调用notify方法让等待的一个线程离开此线程池。如果是notifyall方法,则让该对象的等待池中的对象都离开。
每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1。同样任务是可重入的,每次重入也是加1,离开减1。synchronized是独占式的,拿到对象锁才能继续,没有获取到锁就会阻塞。调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。

参考资料:

synchronized、锁、多线程同步的原理是咋样的
深入JVM锁机制之一:synchronized

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值