synchronized

java多线程

Posted by zhenghao on 2019-04-26

synchronized

同步锁,它可以把任何非null的对象当作锁。它是java解决并发问题的一种手段,它保证了线程互斥的访问同步代码。

synchronized作用范围


作用于方法:锁住的是当前对象(this);

作用于静态方法:该类的字节码,是class实例

作用于代码块:锁的是调用这个代码块的对象。

作用于代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class SynchronizedBlock {

public static void main(String[] args) {
SynchronizedBlockT synchronizedBlock=new SynchronizedBlockT();
Thread thread1=new Thread(synchronizedBlock,"synchronized1");
Thread thread2=new Thread(synchronizedBlock,"synchronized2");
thread1.start();
thread2.start();
}
}
class SynchronizedBlockT implements Runnable{
private static int count=10;
@Override
public void run() {
synchronized (this){
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+count++);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

}

执行结果

1
2
3
4
5
6
7
8
9
10
synchronized1-10
synchronized1-11
synchronized1-12
synchronized1-13
synchronized1-14
synchronized2-15
synchronized2-16
synchronized2-17
synchronized2-18
synchronized2-19

Synchronized 作用在代码块上,它的作用范围是代码块包起来的部分,作用的对象是调用该代码块的对象。

现在两个线程访问同一个对象进行并发执行,因为代码块上加了锁,所以在并发执行的时候,锁定了该对象,只有执行完才会释放锁,另一个线程才会进来。

如果我们把main方法中的代码改为下面部分,会出现什么情况。

1
2
3
4
5
6
public static void main(String[] args) {
Thread thread1=new Thread(new SynchronizedBlockT(),"synchronized1-");
Thread thread2=new Thread(new SynchronizedBlockT(),"synchronized2-");
thread1.start();
thread2.start();
}
1
2
3
4
5
6
7
8
9
10
11
运行结果为
synchronized1-11
synchronized2-10
synchronized1-12
synchronized2-13
synchronized1-14
synchronized2-15
synchronized1-16
synchronized2-17
synchronized1-18
synchronized2-19

为什么会出现上面的情况呢,我不是已经加入锁了吗,原因是开启了2个线程,创建了2个对象,线程1拿到的是一个对象,线程2拿到的也是一个新对象。我门知道synchronized 锁的是对象,这时候会有两把锁分别锁定在这2个对象上,而且这2把锁是互不干扰的,所以2个线程同时执行。

我门在看同步代码块的一段代码,还是创建2个新对象,执行结果确又变了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class SynchronizedBlock {

public static void main(String[] args) {
byte [] lock=new byte[0];
Thread thread1=new Thread(new SynchronizedBlockT(lock),"synchronized1-");
Thread thread2=new Thread(new SynchronizedBlockT(lock),"synchronized2-");
thread1.start();
thread2.start();
}
}

class SynchronizedBlockT implements Runnable{
private static int count=10;
private Object object;
public SynchronizedBlockT(Object o){
this.object=o;
}
@Override
public void run() {
synchronized (object){
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+count++);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

}
1
2
3
4
5
6
7
8
9
10
11
执行结果为:
synchronized1-10
synchronized1-11
synchronized1-12
synchronized1-13
synchronized1-14
synchronized2-15
synchronized2-16
synchronized2-17
synchronized2-18
synchronized2-19

这是为什么呢,原因是同步代码块作用的对象是调用这个代码块的对象,现在作用在这个代码块的对象是object,而object是传递过来的参数,虽然创建了2个SynchronizedBlockT对象,可是传递过来的参数是同一个参数对象,也就是byte数组。相当于他们持有的是这个byte数组对象的锁,所以他们是互斥的。

作用于静态代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class SynchronizedStatic {
public static void main(String[] args) {
Thread thread1=new Thread(new SynchronizedStaticT(),"synchronized1-");
Thread thread2=new Thread(new SynchronizedStaticT(),"synchronized2-");
thread1.start();
thread2.start();
}
}
class SynchronizedStaticT implements Runnable{
private static int count=10;

@Override
public void run() {
method();
}
public synchronized static void method() {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
执行结果
synchronized1-:10
synchronized1-:11
synchronized1-:12
synchronized1-:13
synchronized1-:14
synchronized2-:15
synchronized2-:16
synchronized2-:17
synchronized2-:18
synchronized2-:19

我门知道静态方法属于类的而不是属于对象,所以synchronized 修饰静态方法时候,也是锁定的是这个类,也就是这个类的所有对象。

因为 run中调用了静态方法,而静态方法是属于类的,所以两个线程相当于用了同一把锁。

作用于方法

作用于方法和作用代码块的是一样的,都是锁定了整个方法的内容。

总结

synchronized 无论是作用于方法还是对象上,只要它作用的对象是非静态的,那么它取的锁就是对象,每个对象只有一个锁,谁拿到这个锁就可以进行执行代码。如果作用的是静态的方法或者类,则它取到的锁是该类,该类的所有对象拿同一把锁。