【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();
					}
				}
		}
	}
}

上述代码运行结果为:

【Java笔记】synchronized使用方法_第1张图片

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

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

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();
		}
	}
}

运行结果是:

【Java笔记】synchronized使用方法_第2张图片

结果是正确的。每一次只有一个线程执行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();
			}
		}
	}
}

测试结果是:

【Java笔记】synchronized使用方法_第3张图片

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

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

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

总结:

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

你可能感兴趣的:(Java笔记)