synchronized
synchronized为Java中的关键字,其加锁和释放锁都是由虚拟机实现的。
当多个线程访问同一个对象时,只有对实例变量的访问是非线程安全的,局域变量是线程安全的。
本节内容:
1.非线程安全的例子
2.锁的作用域
2.1锁作用于对象上
2.1.1 线程共享相同的对象
2.1.2线程使用不同的对象
2.1.3使用同步代码块实现同步
2.1.4自定义锁对象,两个线程共享同一个对象
2.1.5两个线程使用不同的对象,但是共享同一把锁
2.1.6 两个线程使用不同的锁
2.1.7 两个线程调用的是不同的方法
2.2 锁作用于类上
2.2.1 静态方法使用synchronized修饰
2.2.2 用同步代码块实现class对象加锁
2.2.3 对象锁和类锁区别
3. 锁的重入
4. 出现异常自动释放锁
5. 同步不具有继承性
5.1 调用方法属于父类
5.2 调用方法属于子类
1.我们先看一下非线程安全的例子:
输出如下:
很明显,线程不同步,且运算结果有误。
2.锁的作用域
锁可以作用于对象上,也可以作用于类上。
2.1锁作用于对象上
2.1.1 在方法上加上synchronized来实现同步,两个线程共享同一个对象
运算结果如下:
两个线程共享同一个对象,在调用同一个同步方法时,只有一个线程先获得锁,另一个线程等待,在锁释放后,等待线程再获取锁。
2.1.2当线程使用不同的对象时,不同的对象持有不同的锁,线程间互不干扰。
执行结果如下:两个对象分别拥有自己的实例变量,线程并发执行。
2.1.3除了可以用synchronized修饰方法,还可以用同步代码块,好处是可以自由控制需要同步的代码,可以将不需要同步的代码放到同步代码块外面。
2.1.4其中this表示的加锁的对象为当前对象,还可以自己指定对象。
自定义锁:
锁为对象中传入的参数obj.
两个线程共享一个对象,使用同一把锁,线程同步执行
运算结果如下
2.1.5当两个线程使用不同的对象,但是共享同一把锁时
执行结果如下:
两个对象分别拥有自己的实例变量,由于两个线程拥有同一把锁,线程同步执行。
2.1.6 当两个线程使用不同的锁时
运算结果如下:两个线程并发执行
2.1.7 当两个线程调用的是不同的同步方法时,线程依然是同步执行,因为锁是加在对象上的,而不是方法上。若一个是同步方法,另一个不是同步方法,由于不需要获取锁,两个方法可以并发执行。
2.2 锁作用于类上
2.2.1 锁是加在class对象上,所有静态方法的访问共享同一把锁
用static修饰同步方法,两个线程同时调用两个静态方法,执行结果如下:
同步执行
2.2.2 用同步代码块实现class对象加锁也是一样的。
2.2.3 对象锁和类锁是不同的锁,若一个是静态方法,一个是非静态方法,两个线程可以并发执行
输出结果如下:
3. 锁的重入
当一个已经获取锁的线程,需要再次获取锁时,不需要等待释放锁后再获取,这样会造成死锁,而是在锁计数器上加1,释放锁时再减1,直到计数器为0时释放锁。
执行结果如下:在调用test1方法时,不需要再次竞争锁。
4. 出现异常自动释放锁
执行结果如下:
5. 同步不具有继承性
5.1 调用方法属于父类,子类直接使用父类方法,锁是父类的锁
5.2 调用方法属于子类
输出如下: