关于这个synchronized锁,过去是一直避免使用的,说他是重量级锁,锁的性能差,远不如ReentrantLock
但是后来看了java并发编程实战,里面提到随着java版本的升级,内置锁synchronized的性能已经得到了很大
的提升,而且他作为内置锁,对内存的消耗更小一点(看大佬这么说,其实我也不知道为啥内存更小额,等我看完jvm和java核心技术再回来解释)。
用法很简单,可以分三种情况:
synchronized加在普通方法上:对对象实例加内置锁
synchronized加在静态方法上:对Class对象加内置锁
synchronized加在代码块上:对参数加内置锁
下面针对1,2两种情况做一个区分的验证。
public static void main(String[] args) throws InterruptedException { //开启四个线程 new Thread(() -> new Load().test()).start(); new Thread(() -> new Load().test()).start(); new Thread(() -> new Load().test()).start(); new Thread(() -> new Load().test()).start(); //休眠一会,主线程等等其他线程,这个值看情况调整哈。 Thread.sleep(1000); System.out.println(Count.i); } public void test() { for (int t = 0; t < 5000; t++) { Count.i++; } }
public class Count { public static int i=0; }
执行结果为17565,这个结果是随机的,不是20000肯定是说明它线程不安全
接下来对test方法synchronized字段
public synchronized void test() { for (int t = 0; t < 5000; t++) { Count.i++; } }
结果是13996,还是线程不安全的,这是为啥呢,就是因为四个线程里执行方法时new 了一个对象实例去执行的
而对于普通方法synchronized字段锁的是实例,不同实例意味着他们之间没有冲突,同一个实例才冲突
下面将线程里的方法改为同一个对象测试
public static void main(String[] args) throws InterruptedException { //开启四个线程 Load load = new Load(); new Thread(() ->load.test()).start(); new Thread(() -> load.test()).start(); new Thread(() -> load.test()).start(); new Thread(() -> load.test()).start(); //休眠一会,主线程等等其他线程。 Thread.sleep(1000); System.out.println(Count.i); }
这时答案就稳定了,只有20000,这种用法就是线程安全的了,如果在方法上加入static成为静态方法,那么只要是Load类的实例都会冲突
我把main里的方法改回去,把test方法上加上static,再次测试
new Thread(() -> new Load().test()).start(); new Thread(() -> new Load().test()).start(); new Thread(() -> new Load().test()).start(); new Thread(() -> new Load().test()).start();
public static synchronized void test() { for (int t = 0; t < 5000; t++) { Count.i++; } }
结果也是稳定的20000,也是线程安全的