【Java笔记】synchronized使用方法

当多个线程并行访问共享的数据时,通常会发生访问冲突,产生脏数据。可以使用synchronized关键字来避免这种情况出现。

Java中任何对象上都有一把锁,synchronized的使用会让一个对象的锁在一段时间内只能被一个线程占用,让其他线程等待,当获得锁的线程释放锁之后,所有的线程才开始竞争这个锁。

synchronized的用法有三种:

第一种:同步代码块(对象锁)

synchronized作用于代码块,指定锁定的对象,在进入代码块之前需要获得指定对象的锁。

下面代码中的this指的是类的当前实例对象

//售票问题
public class Test3 {
	public static void main(String[] args) {
		T mythread = new T();
		Thread t1 = new Thread(mythread, "一");
		Thread t2 = new Thread(mythread, "二");
		Thread t3 = new Thread(mythread, "三");
		Thread t4 = new Thread(mythread, "四");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
class T implements Runnable{
	static int num = 100;
	@Override
	public void run() {
			while(num>0) {
				synchronized (this) {
					if(num<=0) {
						System.out.println("售票结束");
						break;
					}	
					System.out.println(Thread.currentThread().getName()+
"号窗口售卖出第"+num+"张票");
					num--;
					try {
						Thread.sleep(150);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
		}
	}
}

上述代码运行结果为:

运行结果正确,没有出现脏数据。

第二种:同步方法(对象锁)

synchronized作用于实例方法,向当前实例加锁,在执行同步方法之前,需要获得当前实例的锁。当前实例指调用这个方法的对象。

//售票问题
//同步方法实现
public class Test3 {
	public static void main(String[] args) {
		T mythread = new T();
		Thread t1 = new Thread(mythread, "一");
		Thread t2 = new Thread(mythread, "二");
		Thread t3 = new Thread(mythread, "三");
		Thread t4 = new Thread(mythread, "四");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
class T implements Runnable{
	static int num = 100;
	@Override
	public void run() {
		while(num>0) {
				synMethon();
			}
		}
	private synchronized void synMethon() {
		if(num<=0) {
			System.out.println("售票结束");
			return;
		}
		System.out.println(Thread.currentThread().getName()+"号窗口售卖出第"+num+"张票");
		num--;
		try {
			Thread.sleep(150);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

运行结果是:

结果是正确的。每一次只有一个线程执行synMethon()方法。

第三种:同步静态方法(类锁)

synchronized作用于静态方法,会向当前类加锁,即所有被synchronized修饰的静态方法获得同一个锁。当一个线程执行其中一个被synchronized修饰的静态方法时,这个线程就获得了所有被synchronized修饰的静态方法的锁,其他线程必须等待该线程释放锁之后,才能参与锁的竞争。但是非静态方法不在这个锁中。

//当i为奇数时,输出奇数,并且退出程序
public class Test2 {
	public static void main(String[] args) {
		R1 r1 = new R1();	//创建第一个对象
		Thread t = new Thread(r1);
		t.start();	//死循环执行 r1.add()
		while(true) {
			int i = R1.get();
			if(i%2!=0) {
				System.out.println(i);
				System.exit(0);
			}
		}
	}
	static class R1 implements Runnable{
		static int i;
		public static synchronized void add() {
			i++;
			i++;
		}
		public static synchronized int get() {
//          测试输出i,看锁的占用情况
			System.out.println(i);
			return i;
		}
		@Override
		public void run() {
			while(true) {
				add();
			}
		}
	}
}

测试结果是:

结果中连续打印相同数字的原因:main线程多次抢占类锁的锁,所以多次输出同样的值;打印的值跨度大的原因:t 线程多次抢占类锁,多次执行add()方法,所以跨度较大。

第三种用法还有一种写法,类似于第一种用法

//这是第三种用法的另一种写法,作用是一样的
	public void A() {
		synchronized (类名.class) {
			
		}
	}

总结:

  1. 第一种和第二种用法都是作用于对象,称对象锁,第二种是专门指向类的实例对象,第一种可以指向其他的引用对象,比如数组。
  2. 第三种用法作用于类,因为静态方法属于类的成员,一个线程获得类锁之后,锁中的静态方法无法被其他线程调用。需要等到该线程释放类锁之后,再去竞争类锁。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值