java并发原理(二)

Synchronized理解

上一篇已经对线程的创建方式进行了介绍

这次LK来聊一聊synchronized

首先synchronized是什么?

百度百科

  • 代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行
  • ava语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。

总结:synchronized是每个对象的内置锁,万物皆对象,一切对象都有自己的锁,内置锁,synchronized是它的具体使用方式。同一时刻只有一个线程访问或者代码块,说明synchronized是互斥锁

接着聊一聊synchronized是干什么的?

public class SynchronizedDemo implements Runnable {

	private static int count = 0;

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread thread = new Thread(new SynchronizedDemo());
			thread.start();
		}

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("result:" + count);
	}

	@Override
	public void run() {
		for (int i = 0; i < 10000; i++) {
			count++;
		}
	}

}
运行结果:
第一次:result:71020
第二次:result:89861
第三次:result:88757
...........

可以看到每次运行的结果都不同,为什么呢?
猜想:一个线程在对count++时,另一个线程在前一个线程执行的过程中获得了此时的count,在它基础上进行++操作,以此类推。

从java内存模型来看看产生此问题的原因
在这里插入图片描述
主内存中存在变量a,线程A获取到主内存中的变量a对变量a执行写操作,此时主内存没有及时更新线程a对变量a的改变,线程b获取到变量a的值也就没有改变。

先来理解一下两个概念

  • 主内存

    所有的线程所共享的

  • 工作内存

    每个线程自己有一个

总结:从上面可以看出问题出现在如何保证读写操作时数据的一致性。

来看看synchronized是如何解决这个问题的

public class SynchronizedDemo implements Runnable {

	private static int count = 0;

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread thread = new Thread(new SynchronizedDemo());
			thread.start();
		}

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("result:" + count);
	}

	// 对象锁
	@Override
	public synchronized void run() {
		for (int i = 0; i < 10000; i++) {
			count++;
		}
	}

}

运行结果:
result:82889
result:68751
result:70303
...........
public class SynchronizedDemo implements Runnable {

	private static int count = 0;

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread thread = new Thread(new SynchronizedDemo());
			thread.start();
		}

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("result:" + count);
	}

	public void run() {
		String a = "1";
		// 类锁
		synchronized (a) {
			for (int i = 0; i < 10000; i++) {
				count++;
			}
		}

	}

}
运行结果:
result:100000
result:100000
result:100000
........
public class SynchronizedDemo implements Runnable {

	private static int count = 0;

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread thread = new Thread(new SynchronizedDemo());
			thread.start();
		}

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("result:" + count);
	}

	// 类锁
	@Override
	public void run() {
		synchronized (SynchronizedDemo.class) {
			for (int i = 0; i < 10000; i++) {
				count++;
			}
		}
	}

}
运行结果:
result:100000
result:100000
result:100000
........
public class SynchronizedDemo implements Runnable {

	private static int count = 0;

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread thread = new Thread(new SynchronizedDemo());
			thread.start();
		}

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("result:" + count);
	}

	// 对象锁
	@Override
	public void run() {
		synchronized (this) {
			for (int i = 0; i < 10000; i++) {
				count++;
			}
		}
	}

}
运行结果:
result:54202
result:54024
result:63947
...
public class SynchronizedDemo implements Runnable {

	private static int count = 0;

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread thread = new Thread(new SynchronizedDemo());
			thread.start();
		}

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("result:" + count);
	}

	@Override
	public void run() {
		count();
	}

	//类锁
	public static synchronized void count() {
		for (int i = 0; i < 10000; i++) {
			count++;
		}
	}

}
运行结果:
result:100000
result:100000
result:100000
...

总结:以上是synchronized 加锁方式,synchronized 给方法加锁,给静态代码块加锁。作为对象锁,每一个对象都有锁,对象改变锁也跟着改变,所以作为对象锁在上面例子中不能保证数据一致性。作为类锁,包括锁住静态方法,锁住字节码类对象,变量作为锁。
这些锁的对象一旦初始化,在内存中只会有一份,所以类锁可以保证数据一致性。

问题又来了,给对象加锁,那么对象锁又是什么,又在哪?
存在对象头中

对象头中的信息

  • Mark Word
    在这里插入图片描述
    存放的对象的Hashcode,分代年龄和锁标记位

  • Class Metadata Address(指向类的指针)

    Java对象的类数据保存在方法区

  • Array Length(数组长度)

    只有数组对象保存了这部分数据
    jvm层面来理解锁竞争状态

  • 偏向锁

    每次线程进入时,markword会检查是不是同一个线程,是的话会让它直接进入,不是的话,其它线程开始抢锁,抢锁成功改变markword中线程id为当前线程id。抢锁失败,多次尝试后会膨胀为轻量级锁

    至于抢锁过程涉及(CAS操作)以后会接着说。

  • 轻量级锁

    多个线程可以同时竞争锁,竞争不到就会产生自旋状态,一直尝试抢锁。

  • 重量级锁

    自旋多次如果仍未抢到锁,那么所有线程就会阻塞,等待当前线程释放锁,其它线程再去竞争。

总结:
synchronized就是重量级锁,其它线程只能等待当前线程释放锁,它才能去竞争获得锁。
synchronized常用来解决共享变量。

发布了47 篇原创文章 · 获赞 18 · 访问量 5729

猜你喜欢

转载自blog.csdn.net/yuruizai110/article/details/86528307
今日推荐