【Java虚拟机】线程安全与锁优化

版权声明:转载请注明出处: https://blog.csdn.net/qq_21687635/article/details/84106728

绝对线程安全

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替工作,也不需要进行额外的同步,或者在调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象时绝对线程安全的。

在Java API中标注自己是线程安全的类,大多数都不是绝对的线程安全。

java.util.Vector是一个线程安全的容器,它的add()、get()和size()这类方法都是被synchronized修饰的。

看一下下面的代码:

	private static Vector<Integer> vector = new Vector<>();

	public static void main(String[] args) {
		while (true) {
			for (int i = 0; i < 10; i++) {
				vector.add(i);
			}

			Thread removeThread = new Thread(new Runnable() {
				@Override
				public void run() {
					for (int i = 0; i < vector.size(); i++) {
						vector.remove(i);
					}
				}
			});

			Thread printThread = new Thread(new Runnable() {
				@Override
				public void run() {
					for (int i = 0; i < vector.size(); i++) {
						System.out.println(vector.get(i));
					}
				}
			});

			removeThread.start();
			printThread.start();

			while (Thread.activeCount() > 20);
		}
	}

运行期间会抛ArrayIndexOutOfBoundsException这个错误。

相对线程安全

相对的线程安全就是我们通常意义上所讲的线程安全,它需要保证对这个对象单独的操作是线程安全的。对于一些特定顺序的连续调用,就可能需要在调用段使用额外的同步手段来保证调用的正确性。

线程安全的实现方式

互斥同步

同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个或一些线程使用。

互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式。

因此互斥是因,同步是果;互斥是方法,同步是目的。

非阻塞同步

互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也成为阻塞同步。

基于冲突检测的乐观并发策略,就是先进行操作,如果没有其他线程竞争使用共享数据,那操作就成功了;如果共享数据有竞争,产生了冲突,就采取其他的补偿措施(最初的措施就是不断地重试,直到成功为止)。

这种乐观的并发策略的许多实现都不要把线程挂起,因此这种同步操作称为非阻塞同步。

锁优化

自旋锁:让请求锁的那个线程“稍等一下”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。为了让线程等待,只需要让线程执行一个忙循环,这项技术就是所谓的自旋锁。

锁消除:对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

锁粗化:如果虚拟机检测到有一连串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展到整个操作序列的外部。

轻量级锁:使用操作系统互斥量来实现的传统锁是“重量级的”。在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

偏向锁:消除数据在无竞争情况下的同步,进一步提高性能。

参考

  1. 深入理解Java虚拟机[书籍]

猜你喜欢

转载自blog.csdn.net/qq_21687635/article/details/84106728