为什么要使用Synchronized关键字
在并发的过程中,存在着多个线程共享数据或者数据集的时候,Synchronized可以保证在同一时刻,只有一条线程可以访问某个方法或者某个代码块。
Synchronized加锁的方式
-
对象锁
锁的是类的实例对象。 -
类锁
锁的是每个类的class对象。每个类的class对象在虚拟机中只有一个,所以类锁也只有一个。
关键字可修饰的地方
- 代码块
synchronized (Object.class){
}
- 普通方法
public synchronized void myFunction(){
}
静态方法
public static synchronized void myFunction(){
}
对象锁
在线程非安全的情况下
public class TmpDemo {
public static void main(String[] args) throws InterruptedException {
A a = new A();
new Thread(()->{
for (int i = 0;i<100000;i++){
a.getIncrement();
}
},"线程A ").start();
new Thread(()->{
for (int i = 0;i<100000;i++){
a.getIncrement();
}
},"线程B ").start();
}
}
class A {
int index = 0;
public void getIncrement(){
index ++ ;
System.out.println(Thread.currentThread().getName() + index);
}
}
在上面代码中A ,B两条线程中,分别对 class A 的静态变量进行10w次的自增1,理想的结果输出应该是20w。经过几次测试,数据并不是每次都一样
想要达到预期的结果就必须是线程安全的情况下。
那么 加锁的方式可以使用对象锁
public class TmpDemo {
public static void main(String[] args) throws InterruptedException {
A a = new A();
new Thread(()->{
synchronized (a){ //给当前对象 a 进行加锁
for (int i = 0;i<100000;i++){
a.getIncrement();
}
}
},"线程A ").start();
new Thread(()->{
synchronized (a){
for (int i = 0;i<100000;i++){
a.getIncrement();
}
}
},"线程B ").start();
}
}
class A {
int index = 0;
public void getIncrement(){
index ++ ;
System.out.println(Thread.currentThread().getName() + index);
}
}
达到预期的效果
也可以使用类锁
public class TmpDemo {
public static void main(String[] args) throws InterruptedException {
A a = new A();
new Thread(()->{
synchronized (A.class){ //锁住 A.class
for (int i = 0;i<100000;i++){
a.getIncrement();
}
}
},"线程A ").start();
new Thread(()->{
synchronized (A.class){
for (int i = 0;i<100000;i++){
a.getIncrement();
}
}
},"线程B ").start();
}
}
class A {
int index = 0;
public void getIncrement(){
index ++ ;
System.out.println(Thread.currentThread().getName() + index);
}
}
或者 对A类的getIncreement()方法进行加锁
public synchronized void getIncrement(){
index ++ ;
System.out.println(Thread.currentThread().getName() + index);
}
这样都能够达到线程安全。
但是在使用对象锁的时候有需要注意的地方,对象锁 锁住的是当前对象的实例。当如果锁住的不是两个相同的实例,这把锁是不生效的。
public class TmpDemo {
public static void main(String[] args) throws InterruptedException {
A a = new A();
A a1 = new A(); //第二个实例
new Thread(()->{
synchronized (a){ //锁住 a
for (int i = 0;i<100000;i++){
a.getIncrement();
}
}
},"线程A ").start();
new Thread(()->{
synchronized (a1){ //锁住 a1
for (int i = 0;i<100000;i++){
a.getIncrement();
}
}
},"线程B ").start();
}
}
class A {
int index = 0;
public void getIncrement(){
index ++ ;
System.out.println(Thread.currentThread().getName() + index);
}
}