本文主要是介绍Java中 synchronized 关键字学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前言
线程安全是我们并发编程中很重要的,为什么会造成线程安全,主要原因有两点:
修饰一个代码块
/*** 同步线程* Created by leiqi on 2018/4/27.*/public class SynThread implements Runnable {public SynThread(){}@Overridepublic void run() {synchronized (this){for (int i= 0; i< 5;i++){try {System.out.println(Thread.currentThread().getName()+":"+i);Thread.sleep(100);}catch (InterruptedException e){e.printStackTrace();}}}}
}
/*** 访问同一对象的synchronized()this代码块*/private static void FirstSynMethond() {SynThread synThread = new SynThread();Thread thread1 = new Thread(synThread,"SynThread1");Thread thread2 = new Thread(synThread,"SynThread2");thread1.start();thread2.start();}
我们看到的结果如下: SynThread1:0
SynThread1:1
SynThread1:2
SynThread1:3
SynThread1:4
SynThread2:0
SynThread2:1
SynThread2:2
SynThread2:3
SynThread2:4Process finished with exit code 0
我们可以看到同时调用两个并发线程对象,同时访问同一对象(SynThread)的synchronized(this)代码块时,只有一个线程能够执行,另一个线程受到阻塞,必须等前面的线程执行完之后后面的线程才能执行。这就相当于给对象加了把锁,当一个线程访问该对象时就把所关闭,不让其他线程访问,当前面线程执行完成之后,再把锁打开。这就解决了我们的并发时的安全问题。
/*** 访问不同对象的synchronized()this代码块*/private static void FirstSynMethond2() {SynThread synThread1 = new SynThread();SynThread synThread2 = new SynThread();Thread thread1 = new Thread(synThread1,"SynThread1");Thread thread2 = new Thread(synThread2,"SynThread2");thread1.start();thread2.start();}
得到结果如下: SynThread1:0
SynThread2:0
SynThread1:1
SynThread2:1
SynThread1:2
SynThread2:2
SynThread2:3
SynThread1:3
SynThread2:4
SynThread1:4Process finished with exit code 0
/*** 访问同一对象的synchronized(this)代码块和非synchronized(this)代码块* Created by leiqi on 2018/4/28.*/
public class SynthreadDemo2 implements Runnable {private int count;public SynthreadDemo2(){count = 0;}public void countAdd(){synchronized (this){for (int i=0;i<5;i++){try {System.out.println("Add count"+":"+(count++));Thread.sleep(100);}catch (InterruptedException e){e.printStackTrace();}}}}public void printcount(){for (int i =0; i<5;i++){try{System.out.println("print count"+":"+(count++));Thread.sleep(100);}catch (InterruptedException e){e.printStackTrace();}}}@Overridepublic void run() {String threadName = Thread.currentThread().getName();if (threadName.equals("SynThreadDemo1")){countAdd();}else if (threadName.equals("SynThreadDemo2")){printcount();}}
}
/*** 同时访问同一对象的synchronized(this)代码块和非synchronized(this)代码块*/private static void SecondSynDemoMehond(){SynthreadDemo2 synthreadDemo2 = new SynthreadDemo2();Thread thread1= new Thread(synthreadDemo2,"SynThreadDemo1");Thread thread2= new Thread(synthreadDemo2,"SynThreadDemo2");thread1.start();thread2.start();}
我们得到结果如下所示:
print count:1
Add count:0
print count:2
Add count:3
print count:4
Add count:5
print count:6
Add count:7
print count:8
Add count:9Process finished with exit code 0
上面的两个代码printcount是非synchronized(this)代码块,countAdd是synchronized(this)代码块,根据运行结果我们可以看到当一个线程访问一个对象的synchronized的代码块时其他线程可以访问该对象的非synchronized代码块。 修饰一个方法
/*** 写法一*/public synchronized void method(){//todo}/*** 写法二*/public void method(){synchronized (this){//todo}}
这两种写法,写法一修饰一个方法 写法二修饰后整个方法的代码块,两者都是修饰了整个方法的代码,所以两种写法是等价的。在用synchronized修饰一个方法要注意以下几点:
我们可以使用synchronized关键字来修饰方法,但是synchronized是不属于方法的一部分,所以synchronized是不能被继承的。当我们用synchronized修饰一个方法后,在子类覆盖来这个方法,在子类中这个方法并不是同步的,必须显示的在子类调用方法上加上synchronized关键字才可以。也可以在子类调用父类的方法,两种写法demo如下:
class Parent {public synchronized void method() { }}class Child extends Parent {public synchronized void method() { }}
在子类方法中调用父类方法:
class Parent {public synchronized void method() { }}class Child extends Parent {public void method() { super.method(); }}
2、定义接口方法时不能使用synchronized关键字
3、构造方法不能使用synchronized关键字,但是可以使用synchronized (this)来同步代码块。
修饰一个静态方法
synchronized修饰一个静态方法写法如下:
public synchronized static void method() {// todo
}
由于静态方法是属于类的而不是属于对象的,所以synchronized修饰静态方法是给这个类的对象加上锁,我们将第一个demo稍加改动:
/*** 同步线程* Created by leiqi on 2018/4/27.*/public class SynThread implements Runnable {public SynThread(){}@Overridepublic void run() {method();}private synchronized static void method(){for (int i= 0; i< 5;i++){try {System.out.println(Thread.currentThread().getName()+":"+i);Thread.sleep(100);}catch (InterruptedException e){e.printStackTrace();}}}
}
/*** 访问不同对象的synchronized()this代码块*/private static void FirstSynMethond2() {SynThread synThread1 = new SynThread();SynThread synThread2 = new SynThread();Thread thread1 = new Thread(synThread1,"SynThread1");Thread thread2 = new Thread(synThread2,"SynThread2");thread1.start();thread2.start();}
而这次我们得到的结果如下:
SynThread1:0
SynThread1:1
SynThread1:2
SynThread1:3
SynThread1:4
SynThread2:0
SynThread2:1
SynThread2:2
SynThread2:3
SynThread2:4Process finished with exit code 0
为啥这次的结果和上面执行的结果不一样昵?这是因为 synchronized这次修饰的是静态方法,静态方法属于类,所以这次synchronized是给累的对象加锁,故而synThread1和synThread2是同一把锁。
修饰一个类
synchronized还可以用来修饰一个类,写法如下:
class ClassName{public void method(){synchronized (ClassName.class){//todo}}
}
我们把上面的demo再做个修改
/*** 同步线程* Created by leiqi on 2018/4/27.*/public class SynThread implements Runnable {public SynThread(){}@Overridepublic void run() {method();}private void method(){synchronized (SynThread.class){for (int i= 0; i< 5;i++){try {System.out.println(Thread.currentThread().getName()+":"+i);Thread.sleep(100);}catch (InterruptedException e){e.printStackTrace();}}}}
}
它的运行效果和上面的结果是一样的,最终效果都是给类的对象加锁,synchronized用于修饰一个类时,是给这个类的所有对象加锁,所有对象都是同同一把锁。
总结
通过上面的学习我们简单总结一下:
这篇关于Java中 synchronized 关键字学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!