并发编程专题(二)---线程安全解决办法

内置的锁

Java提供了一种内置的锁机制来支持原子性
每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁
内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁
内置锁使用synchronized关键字实现,synchronized关键字有两种用法:
1.修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象
2.同步代码块和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活

同步代码块synchronized

在这里插入图片描述
对象如同锁,持有锁的线程可以在同步中执行
没持有锁的线程即使获取CPU的执行权,也进不去
同步的前提:
1,必须要有两个或者两个以上的线程
2,必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。

public void sale() {
		synchronized (this) {
			if (trainCount > 0) {
				System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
				trainCount--;
			}
		}
	}

同步方法

在方法上修饰synchronized 称为同步方法

public synchronized void sale() {
		if (trainCount > 0) {
			System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
			trainCount--;
		}
	}

同步函数使用this锁。
证明方式: 一个线程使用同步代码块(this明锁),另一个线程使用同步函数。如果两个线程抢票不能实现同步,那么会出现数据错误。

class Thread009 implements Runnable {
	private int trainCount = 100;
	private Object oj = new Object();
	public boolean flag = true;

	public void run() {
		if (flag) {
			while (trainCount > 0) {
				synchronized (this) {
					try {
						Thread.sleep(10);
					} catch (Exception e) {
						// TODO: handle exception
					}
					if (trainCount > 0) {
						System.out
								.println(Thread.currentThread().getName() + "," + "出售第" + (100 - trainCount + 1) + "票");
						trainCount--;
					}
				}
			}
		} else {
			while (trainCount > 0) {
				sale();
			}
		}
	}

	public synchronized void sale() {
		try {
			Thread.sleep(10);
		} catch (Exception e) {
			// TODO: handle exception
		}
		if (trainCount > 0) {
			System.out.println(Thread.currentThread().getName() + "," + "出售第" + (100 - trainCount + 1) + "票");
			trainCount--;
		}
	}
}

public class Test009 {
	public static void main(String[] args) throws InterruptedException {
		Thread009 threadTrain = new Thread009();
		Thread t1 = new Thread(threadTrain, "窗口1");
		Thread t2 = new Thread(threadTrain, "窗口2");
		t1.start();
		Thread.sleep(40);
		threadTrain.flag = false;
		t2.start();
	}
}

静态同步函数

方法上加上static关键字,使用synchronized 关键字修饰 或者使用类.class文件。
静态的同步函数使用的锁是 该函数所属字节码文件对象
可以用 getClass方法获取,也可以用当前 类名.class 表示。

public static void sale() {
		synchronized (ThreadTrain3.class) {
			if (trainCount > 0) {
				System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
				trainCount--;
			}
		}
}

总结:
synchronized 修饰方法使用锁是当前this锁。
synchronized 修饰静态方法使用锁是当前类的字节码文件

猜你喜欢

转载自blog.csdn.net/qq648299994/article/details/88548470