synchronized 关键字存在的意义
线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照既定的代码一步一步地执行,直到终止。但是,每个运行中的线程,如果仅仅是孤立地运行,那么没有一点儿价值,或者说价值很少,如果多个线程能够相互配合完成工作,包括数据之间的共享,协同处理事情。这将会带来巨大的价值。
Java支持多个线程同时访问一个对象或者对象的成员变量,关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它的目的是确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。
对象锁和类锁
java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。
我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的
这个特性也是解决在分布式场景下,synchronized的适用方法
synchronized 关键字的三种使用方式
- 对于同步代码块,就得指定锁对象。
- 对于修饰方法的synchronized,默认的锁对象就是当前方法的对象。
- 对于修饰静态方法的synchronized,其锁对象就是此方法所对应的类Class对象。(分布式)
实战解析
No1.同步代码块
/**
* synchronized 关键字测试
* <p>
* 同步代码块
* 结果是 所有打印语句中 startDate 是同时进入的 在多线程情况下 只允许一个线程进入 同步代码块
* 当new 出两个 对象的时候 同步代码块 将互不干扰
* 结论 针对同一对象 同步代码块可以起到多线程访问不会冲突的情况
*/
private static class SynchronizedDemo1 implements Runnable {
/**
* 全局变量
* 创建一个计数器
*/
private static int counter = 1;
public void run() {
long startDate = System.currentTimeMillis();
synchronized (this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
System.out.println("开始时间 :" + startDate + " 当前时间 :" + System.currentTimeMillis());
System.out.println();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// 测试代码1
// SynchronizedDemo1 syncThread = new SynchronizedDemo1();
// Thread thread1 = new Thread(syncThread, "sync-thread-1");
// Thread thread2 = new Thread(syncThread, "sync-thread-2");
// thread1.start();
// thread2.start();
// 测试代码2
// SynchronizedDemo1 syncThread1 = new SynchronizedDemo1();
// SynchronizedDemo1 syncThread2 = new SynchronizedDemo1();
// Thread thread1 = new Thread(syncThread1, "sync-thread-1");
// Thread thread2 = new Thread(syncThread2, "sync-thread-2");
// thread1.start();
// thread2.start();
}
}
测试结果
测试代码1
发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全
测试代码2
发现线程1执行期间线程2会穿插执行 也就是不能保证在不同对象下,线程安全
No2.普通方法测试
/**
* synchronized 普通方法测试
* <p>
* 在同一对象中 即使多个线程访问同一方法 只允许一个线程进入 加锁方法
* <p>
* 当new 出两个 对象的时候 同步代码块 将互不干扰
*/
private static class SynchronizedDemo2 implements Runnable {
/**
* 全局变量
* 创建一个计数器
*/
private static int counter = 1;
public synchronized void run() {
long startDate = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
System.out.println("开始时间 :" + startDate + " 当前时间 :" + System.currentTimeMillis());
System.out.println();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 测试代码1
// SynchronizedDemo2 syncThread = new SynchronizedDemo2();
// Thread thread1 = new Thread(syncThread, "sync-thread-1");
// Thread thread2 = new Thread(syncThread, "sync-thread-2");
// thread1.start();
// thread2.start();
// 测试代码2
// SynchronizedDemo2 syncThread1 = new SynchronizedDemo2();
// SynchronizedDemo2 syncThread2 = new SynchronizedDemo2();
// Thread thread1 = new Thread(syncThread1, "sync-thread-1");
// Thread thread2 = new Thread(syncThread2, "sync-thread-2");
// thread1.start();
// thread2.start();
}
}
测试结果
测试代码1
发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全
测试代码2
发现线程1执行期间线程2会穿插执行 也就是不能保证在不同对象下,线程安全
No3.静态方法测试
/**
* synchronized 静态方法测试
* 会将这个类锁 也就是对于的class 锁起来 这个类的实例都会被锁
*/
public static class SynchronizedDemo3 implements Runnable {
private static int counter = 1;
/**
* 静态的同步方法
*/
public synchronized static void method() {
long startDate = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
System.out.println("开始时间 :" + startDate + " 当前时间 :" + System.currentTimeMillis());
System.out.println();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void run() {
method();
}
public static void main(String[] args) {
// 测试方法1
// SynchronizedDemo3 syncThread1 = new SynchronizedDemo3();
// Thread thread1 = new Thread(syncThread1, "sync-thread-1");
// Thread thread2 = new Thread(syncThread1, "sync-thread-2");
// thread1.start();
// thread2.start();
// 测试方法2
// SynchronizedDemo3 syncThread1 = new SynchronizedDemo3();
// SynchronizedDemo3 syncThread2 = new SynchronizedDemo3();
// Thread thread1 = new Thread(syncThread1, "sync-thread-1");
// Thread thread2 = new Thread(syncThread1, "sync-thread-2");
// thread1.start();
// thread2.start();
}
}
测试结果
测试代码1
发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全
测试代码2
发现线程1执行完之后线程2才会执行 也就是可以保证在同一对象下,线程安全
测试结论
对象 | 同步代码块 | 同步普通方法 | 同步静态方法 |
---|---|---|---|
单个实例 | 线程安全 | 线程安全 | 线程安全 |
多个实例 | 线程之间对方法互不干扰,但是对同一变量不安全 | 线程之间对方法互不干扰,但是对同一变量不安全 | 线程安全 |