在多线程情况下,当多个线程操作同一个资源的时候,会出现安全问题,例如脏读(一个线程咋读取变量的时候,值已经被另一个线程改变)。
synchronized关键字:可用来同步方法或者代码块。有关synchronized,总结一下几条。
1 synchronized关键字锁的是对象,当多个对象会创建多个锁,而达不到同步的效果。
2 只有操作公共资源的时候才需要上锁,非公共资源没必要上锁。
3 synchronized关键字拥有可重入锁。
4 异常出现的时候,会自动释放锁。
5 同步不具备继承性。
6 sleep()方法不会释放锁。
7wait()方法会释放锁。
8 synchronized可同步方法,也可以同步代码块。
下面对其中几条进行验证;
方法类:
public class MyMethod { synchronized public void methodA(String username) throws InterruptedException{ System.out.println(username); if(username.equals("a")){ System.out.println(Thread.currentThread().getName()+" into methodA"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+" out methodA"); } else { System.out.println(Thread.currentThread().getName()+" into methodB"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" out methodB"); } /*while(true){ }*/ } //synchronized 锁代码块 public static void methodB(String lock) throws InterruptedException{ synchronized (lock) { System.out.println(Thread.currentThread().getName()+" into lock"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" out lock"); } } }
主线程:synchronized同步代码块。synchronized(this)锁的是当前对象。
public class Run { public static void main(String[] args) throws InterruptedException { MyMethod m1=new MyMethod(); MyMethod m2=new MyMethod(); String lock=""; Thread t1 =new Thread(new Runnable() { @Override public void run() { try { m1.methodB(lock); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } },"t1"); Thread t2 =new Thread(new Runnable() { @Override public void run() { try { m1.methodB(lock); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } },"t2"); t1.start(); t2.start(); } }
控制台:
t2 into lock t2 out lock t1 into lock t1 out lock
synchronized同步方法,修改主线程如下
public class Run { public static void main(String[] args) throws InterruptedException { MyMethod m1=new MyMethod(); MyMethod m2=new MyMethod(); String lock=""; Thread t1 =new Thread(new Runnable() { @Override public void run() { try { m1.methodA("a"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } },"t1"); Thread t2 =new Thread(new Runnable() { @Override public void run() { try { m1.methodA("b"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } },"t2"); t1.start(); t2.start(); } }
控制台输出:
b t2 into methodB t2 out methodB a t1 into methodA t1 out methodA
可以发现达到同步的效果。
再次修改主线程代码如下:
public class Run { public static void main(String[] args) throws InterruptedException { MyMethod m1=new MyMethod(); MyMethod m2=new MyMethod(); String lock=""; Thread t1 =new Thread(new Runnable() { @Override public void run() { try { m1.methodA("a"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } },"t1"); Thread t2 =new Thread(new Runnable() { @Override public void run() { try { m2.methodA("b"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } },"t2"); t1.start(); t2.start(); } }
控制台如下
b a t1 into methodA t2 into methodB t2 out methodB t1 out methodA
并且多次执行main方法,发现控制台打印顺序不同。此处调用的是两个对象,所以jvm会创建两个锁,互不影响,所以在锁,只能锁一个对象中的方法。证明synchronized锁的是对象。此过程中,我还测试了静态方法,当把methodA()方法改为静态的时候
两个对象一样会有同步的效果。
可重入锁概念:自己可以再次获取自己的内部锁。如有一个线程获得了某个对象的锁,此时这个对象还没有释放,当其再次想获取这个对象的锁的时候,还是可以获取的,否则会造成死锁。
每一个优秀的人,都有一段沉默的时光。不抱怨,不诉苦,最后度过那段感动自己的日子。