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