1. 基本概念:
在Java中当多线程访问一个方法或者代码块时,可能造成因为抢夺资源而造成进程死锁
使用synchronized关键字可以防止进程死锁
2. 主要用法
可以修饰在方法上
比如 public void synchronized void method(){ //代码块 }
可以修饰在对象上
比如 synchronized(某个对象){ //代码块 }
可以修饰在类上
比如 synchronized(类名.class){ //代码块 }
3. 修饰在方法上
public class SynchronizedTest1 {
public synchronized void methodA(){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodA()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public void methodB(){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodB()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedTest1 test = new SynchronizedTest1();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
test.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
test.methodB();
}
});
t2.start();
}
}
结果显示:作用在同一个实例对象一个线程调用synchronized方法修饰的方法,另一个线程调用没有被
synchronized修饰的方法,是异步的,并行执行
public class SynchronizedTest1_1 {
public synchronized void methodA(){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodA()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public synchronized void methodB(){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodB()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
public static void main(String[] args) {
SynchronizedTest1_1 test1 = new SynchronizedTest1_1();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodB();
}
});
t2.start();
}
}
结果显示:作用在同一个实例对象,两个线程都调用synchronized修饰的方法,是同步的,串行执行
总结:
1.当一类中的方法有被synchronized修饰时,一个线程被调用了被synchronized修饰的方法,那么其他线程就不能调用synchronized修饰的方法,原因:一个线程获得了对象的锁之后,其他线程就不能再获取这个对象的锁,只能等获取到锁的线程执行完后,才能够获取到对象的锁
2.当一类中的方法有被synchronized修饰时,一个线程被调用了被synchronized修饰的方法,但是其他线程可以调用没有被synchronized修饰的方法,原因:调用没有被synchronized修饰的方法,就不需要获取当前对象的锁,可以直接调用方法
3.如果实例化两个上面类的对象,那么test1和test2,即使同时调用methodA()方法,也是并行执行,是同步的,因为所调用的对象不是一个对象
4. 修饰在对象上
public class SynchronizedTest2 {
public void methodA(){
//同步代码块
synchronized(this){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodA()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public void methodB(){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodB()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedTest2 test1 = new SynchronizedTest2();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodB();
}
});
t2.start();
}
}
结果显示:作用在同一个实例对象上,一个线程调用synchronized代码块,另一个线程可以调用非
synchronized代码块,是异步的,并行执行
public class SynchronizedTest2_1 {
public void methodA(){
//同步代码块
synchronized(this){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodA()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public void methodB(){
synchronized(this){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodB()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedTest2_1 test1 = new SynchronizedTest2_1();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodB();
}
});
t2.start();
}
}
结果显示:作用在同一个实例对象,两个线程都调用synchronized修饰的代码块,是同步的,串行执行
总结:
修饰在代码块上的synchronized可以使代码实用性更强,达到只同步方法中的部分代码,而不是同步所有的代码
5. 同时修饰在方法和对象上
public class SynchronizedTest3 {
//给方法加同步
public synchronized void methodA(){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodA()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
//给代码块加同步
public void methodB(){
synchronized(this){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodB()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedTest3 test1 = new SynchronizedTest3();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodB();
}
});
t2.start();
}
}
结果显示:作用在同一个实例对象上,synchronized和synchronized(this)二者没有区别,都作用在this对象上, 给this对象添加锁,所以会同步,串行执行
总结:
1.synchronized和synchronized(this)二者没有区别,都作用在this对象上,当都需要调用对象的锁的时候,就要等待前者执行完后,才能获取对象的锁
2. 如果把methodB()方法中的this换成别的对象,那么调用此方法所需要的对象的锁就不是当前test1对象的锁,而是该方法所修饰对象的锁,那么线程t1和t2就是同步的,并行执行
6. 修饰在类上:
public class SynchronizedTest4 {
public synchronized static void methodA(){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodA()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public void methodB(){
synchronized(SynchronizedTest4.class){
try {
for (int i = 0; i < 5; i++) {
System.out.println("methodB()-"+i);
Thread.sleep(1000);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedTest4 test1 = new SynchronizedTest4();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodA();
}
});
t1.start();
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
test1.methodB();
}
});
t2.start();
}
}
结果显示:修饰在类上所以是同一对象的锁,所以是同步的串行执行
总结:
1.static synchronized 和 synchronized(类名.class)都是作用在同一个类锁上,所以会同步
2.如果把synchronized(类名.class)中的类名换成Object(指的是与static synchronized修饰的不同的类),那么t1和t2就是异步的可以并行执行
7. 总结:
1.方法上只有synchronized关键字锁的是对象,方法上有static synchronized两个关键字修饰锁的是类
2.在分析synchronized关键字时要注意不是修饰的方法锁的就是方法,可能时对象或者类
3.有多个synchronized关键字的方法,执行时需要等待对象的锁,先占有锁的线程先执行,其他线程要等待对象的锁,拿到锁之后才能执行
4.注意不带synchronized关键字的方法,线程调用的时候不需要等待对象的锁可以直接执行
- 个人感悟:
通过今天的学习多少对synchronized关键字有了一点自己的了解,一面的一些结论是自己撰写的,可能措辞有些不恰当,大家可以提出来,我会尽快改正,另外,如果大家觉得我写的有问题,也可以和我来探讨,走向java大佬的路还很,漫长呀!!