设为首页 加入收藏

TOP

高并发Java(9):锁的优化和注意事项(三)
2017-09-30 13:36:59 】 浏览:9781
Tags:并发 Java 优化 注意事项
轻量级锁,轻量级锁会尝试使用CAS操作来获得锁,如果轻量级锁获得失败,说明存在竞争。但是也许很快就能获得锁,就会尝试自旋锁,将线程做几个空循环,每次循环时都不断尝试获得锁。如果自旋锁也失败,那么只能升级成重量级锁。

可见偏向锁,轻量级锁,自旋锁都是乐观锁。

3. 一个错误使用锁的案例

public class IntegerLock {
	static Integer i = 0;

	public static class AddThread extends Thread {
		public void run() {
			for (int k = 0; k < 100000; k++) {
				synchronized (i) {
					i++;
				}
			}
		}
	}

	public static void main(String[] args) throws InterruptedException {
		AddThread t1 = new AddThread();
		AddThread t2 = new AddThread();
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

一个很初级的错误在于,在 高并发Java(7):并发设计模式提到,Interger是final不变的,每次++后,会产生一个新的 Interger再赋给i,所以两个线程争夺的锁是不同的。所以并不是线程安全的。

4. ThreadLocal及其源码分析

这里来提ThreadLocal可能有点不合适,但是ThreadLocal是可以把锁代替的方式。所以还是有必要提一下。

基本的思想就是,在一个多线程当中需要把有数据冲突的数据加锁,使用ThreadLocal的话,为每一个线程都提供一个对象实例。不同的线程只访问自己的对象,而不访问其他的对象。这样锁就没有必要存在了。

package test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	private static final SimpleDateFormat sdf = new SimpleDateFormat(
			"yyyy-MM-dd HH:mm:ss");

	public static class ParseDate implements Runnable {
		int i = 0;

		public ParseDate(int i) {
			this.i = i;
		}

		public void run() {
			try {
				Date t = sdf.parse("2016-02-16 17:00:" + i % 60);
				System.out.println(i + ":" + t);
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		ExecutorService es = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 1000; i++) {
			es.execute(new ParseDate(i));
		}
	}

}

由于SimpleDateFormat并不线程安全的,所以上述代码是错误的使用。最简单的方式就是,自己定义一个类去用synchronized包装(类似于Collections.synchronizedMap)。这样做在高并发时会有问题,对 synchronized的争用导致每一次只能进去一个线程,并发量很低。

这里使用ThreadLocal去封装SimpleDateFormat就解决了这个问题

package test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	static ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>();

	public static class ParseDate implements Runnable {
		int i = 0;

		public ParseDate(int i) {
			this.i = i;
		}

		public void run() {
			try {
				if (tl.get() == null) {
					tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
				}
				Date t = tl.get().parse("2016-02-16 17:00:" + i % 60);
				System.out.println(i + ":" + t);
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		ExecutorService es = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 1000; i++) {
			es.execute(new ParseDate(i));
		}
	}

}

每个线程在运行时,会判断是否当前线程有SimpleDateFormat对象

if (tl.get() == null)

如果没有的话,就new个 SimpleDateFormat与当前线程绑定

tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

然后用当前线程的 SimpleDateFormat去解析

tl.get().parse("2016-02-16 17:00:" + i % 60);

一开始的代码中,只有一个 SimpleDat

首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇高并发Java(8):NIO和AIO 下一篇Tomcat5.5(6.0)配置-多域名绑定和..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目