转载请注明出处:http://blog.csdn.net/xingjiarong/article/details/47907237
在之前的博客中我们介绍了条件对象和锁对象,两者结合使用才能起到比较好的互斥与同步效果,大家可能觉得有些麻烦,有没有将两者结合起来的工具呢,有!java提供了synchronized关键字来实现线程的互斥和同步,其达到的效果相当于条件对象和锁对象结合起来的效果。synchronized关键字有两类用法,一类是修饰方法,一类是修饰代码块,这篇博客主要介绍一下synchronized关键字修饰方法时的用法。
先来为大家介绍一下java中锁的概念。java中的每个对象和每个类都有锁,而且是互斥锁,也就是说只能有一方占有,另一方只能等到对方释放后才能再占有锁。synchronized关键字就是基于java的对象和类的锁的。
下面来看一下下面这个例子,Trans这个类是在一行中打印当前的线程和0-24这25个数。MyThread这个类接收一个Trans类的对象,在run方法中不停的调用printNum方法。在main方法中,创建了一个对象和两个线程,这两个线程都使用同一个Trans对象。
public class Trans { public void printNum(int num){ System.out.print(Thread.currentThread());//获取当前运行这个方法的类 for(int i=0;i<25;i++){ System.out.print(i+" "); } System.out.println(); } }
class MyThread implements Runnable { private Trans trans; private int num; public MyThread(Trans trans, int num) { this.trans = trans; this.num = num; } public void run() { while (true) { trans.printNum(num); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test { public static void main(String[] args) { Trans t = new Trans(); Thread a = new Thread(new MyThread(t, 1)); Thread b = new Thread(new MyThread(t, 2)); a.start(); b.start(); } }
可以看到,打印结果确实是不正确的。那么怎么用synchronized关键字避免这种情况呢?可以将printNum方法的声明改为下面这种:
public synchronized void printNum(int num){ System.out.print(Thread.currentThread()); for(int i=0;i<25;i++){ System.out.print(i+" "); } System.out.println(); }
每个对象都有自己的锁,所以这里synchronized关键字起作用的原因就是因为两个线程用的是同一个对象,如果每个线程都有自己的Trans对象,那么上边的方法将不再适用,例如我们把Main方法改为下面这样:
public static void main(String[] args) { Trans t = new Trans(); Trans t1 = new Trans(); Thread a = new Thread(new MyThread(t, 1)); Thread b = new Thread(new MyThread(t1, 2)); a.start(); b.start(); }
既然每个对象都只有一把互斥锁,那么同一个对象的多个synchronized关键字修饰的方法之间也是无法同时访问的。例如下面的例子:
public class Trans { public synchronized void printNum(int num) { while (true) { System.out.print(Thread.currentThread()); for (int i = 0; i < 25; i++) { System.out.print(i + " "); } System.out.println(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void printNum1(int num) { System.out.print(Thread.currentThread()); for (int i = 25; i > 0; i--) { System.out.print(i + " "); } System.out.println(); } public void printNum2(int num) { for (int i = 25; i > 0; i--) { System.out.print(num); } System.out.println(); } }
class MyThread implements Runnable { private Trans trans; private int num; public MyThread(Trans trans, int num) { this.trans = trans; this.num = num; } public void run() { trans.printNum(num); } } class MyThread1 implements Runnable { private Trans trans; private int num; public MyThread1(Trans trans, int num) { this.trans = trans; this.num = num; } public void run() { while (true) { trans.printNum1(num); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyThread2 implements Runnable { private Trans trans; private int num; public MyThread2(Trans trans, int num) { this.trans = trans; this.num = num; } public void run() { while (true) { trans.printNum2(num); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test { public static void main(String[] args) { Trans t = new Trans(); Thread a = new Thread(new MyThread(t, 1)); Thread b = new Thread(new MyThread1(t, 2)); Thread c = new Thread(new MyThread2(t, 2)); a.start(); b.start(); c.start(); } }
首先,Trans类增加了两个方法,一个方法其中一个方法也是用synchronized关键字修饰的方法,另一个方法是不用synchronized关键字修饰的,在Main方法中创建了三个线程,这个三个线程都是同一个Trans对象,分别调用三个不同的打印方法。第一个打印方法是一个死循环,一直在打印信息,又是synchronized修饰的方法,一旦获得了对象的锁就不会释放了,第二个方法也是synchronized关键字修饰的方法,这两个方法不能同时执行,如果第一个方法获得了锁,那么第二个方法将永远不能执行,第三个打印方法不是synchronized的,所以锁对它没有任何的影响,可以随时的执行。
下面是我的执行结果:
和我们想的一样,方法一和方法三交替执行,方法二不会执行。
public class Trans { public static synchronized void printNum(int num){ System.out.print(Thread.currentThread()); for(int i=0;i<25;i++){ System.out.print(i+" "); } System.out.println(); } }
class MyThread implements Runnable { private Trans trans; private int num; public MyThread(Trans trans, int num) { this.trans = trans; this.num = num; } public void run() { while (true) { trans.printNum(num); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test { public static void main(String[] args) { Trans t = new Trans(); Trans t1 = new Trans(); Thread a = new Thread(new MyThread(t, 1)); Thread b = new Thread(new MyThread(t1, 2)); a.start(); b.start(); } }
public class Trans { public static synchronized void printNum(int num){ System.out.print(Thread.currentThread()); for(int i=0;i<25;i++){ System.out.print(i+" "); } System.out.println(); } public synchronized void printNum1(int num){ System.out.print(Thread.currentThread()); for(int i=0;i<25;i++){ System.out.print(2+" "); } System.out.println(); } }
class MyThread implements Runnable { private Trans trans; private int num; public MyThread(Trans trans, int num) { this.trans = trans; this.num = num; } public void run() { while (true) { trans.printNum(num); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyThread1 implements Runnable { private Trans trans; private int num; public MyThread1(Trans trans, int num) { this.trans = trans; this.num = num; } public void run() { while (true) { trans.printNum1(num); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test { public static void main(String[] args) { Trans t = new Trans(); Thread a = new Thread(new MyThread(t, 1)); Thread b = new Thread(new MyThread1(t, 2)); a.start(); b.start(); } }
了解了锁的概念和synchronized修饰方法的用法之后我们可以总结出,两个方法是不是互斥的关键是看两个方法取得的锁是不是互斥的,如果锁是互斥的,那么方法也是互斥访问的,如果锁不是互斥的,那么不同的锁之间是不会有什么影响的,所以这时方法是可以同时访问的。