zoukankan      html  css  js  c++  java
  • Java多线程之synchronized(一)

         在上节中已经说过了“非线程安全”是如何出现的,链接如下:http://www.cnblogs.com/chentong/p/5650137.html,那么怎么解决“非线程安全”问题呢,只需要在两个线程都需要同时访问的方法前面加上synchronized关键字即可,我只贴出需要修改的这个方法的代码,具体修改如下:

         public static class GetNum {
    
    		private int num = 0;
                    //两个线程访问同一个对象中的同步方法时一定是线程安全的
    		synchronized public void getNum(String name) {
    			try {
    
    				if ("a".equals(name)) {
    					num = 100;
    					System.out.println("a set over");
    					Thread.sleep(2000);
    
    				} else {
    					num = 200;
    					System.out.println("b set over");
    				}
    
    				System.out.println("线程" + name + "的num=" + num);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    

       结果如下:无论哪个线程先执行,一定不会出现交叉执行的情况,因为synchronized取得是对象锁,在main方法里只有一个GetNum对象num(不知道main方法如何调用的,可以看上一篇博客,链接上边已经附上了),因此只有一把锁,所以只有一个线程把run方法执行完了,才会释放锁,另一个线程才会执行。因此在只有一个对象锁的情况下,synchronized声明的方法一定是排队运行的。

         

          上面的这种情况是一个对象一把锁,下面说一下多个对象多个锁是怎么执行的。例子代码如下:

            public static void main(String[] args) {
    
    		GetNum num1 = new GetNum();
    		GetNum num2 = new GetNum();
    		ThreadA a = new ThreadA(num1);
    		a.start();
    		ThreadB b = new ThreadB(num2);
    		b.start();
    	}
    
    	public static class GetNum {
    		private int num = 0;
    		synchronized public void getNum(String name) {
    			try {
    				if ("a".equals(name)) {
    					num = 100;
    					System.out.println("a set over");
    					Thread.sleep(2000);
    				} else {
    					num = 200;
    					System.out.println("b set over");
    				}
    				System.out.println("线程" + name + "的num=" + num);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    public static class ThreadA extends Thread { private GetNum num; public ThreadA(GetNum num) { super(); this.num = num; } @Override public void run() { super.run(); num.getNum("a"); } } public static class ThreadB extends Thread { private GetNum num; public ThreadB(GetNum num) { super(); this.num = num; } @Override public void run() { super.run(); num.getNum("b"); } }

         运行结果如下:大家会发现打印的结果是交叉的,原因是由于synchronized取得是对象锁,而在这里两个线程传入的又不是同一个GetNum对象(num1和num2),所以synchronized取得是两个不一样的锁,大家互相不影响。只能哪个线程抢上CPU哪个线程就执行。

           

            上面的两个例子访问的都是synchronized关键字声明的方法,那么如果有其它的普通方法被调用的时候,会怎样执行呢,下面我写了一个例子来演示这种情况,如下:

    	public static void main(String[] args) {
    
    		MyObject object = new MyObject();
    		ThreadA a = new ThreadA(object);
    		a.setName("A");
    		ThreadB b = new ThreadB(object);
    		b.setName("B");
    		a.start();
    		b.start();
    	}
    
    	public static class MyObject {
                    //synchronized声明的方法
    		synchronized public void meathodA() {
    			try {
    				System.out.println("begin methodA threeName="
    						+ Thread.currentThread().getName());
    				Thread.sleep(5000);
    				System.out.println("meathodA endTime="
    						+ System.currentTimeMillis());
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
                    //普通方法
    		public void meathodB() {
    			try {
    				System.out.println("begin methodB threeName="
    						+ Thread.currentThread().getName());
    				System.out.println("meathodB beginTime="
    						+ System.currentTimeMillis());
    				Thread.sleep(5000);
    				System.out.println("end");
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public static class ThreadA extends Thread {
    
    		private MyObject object;
    		public ThreadA(MyObject object) {
    			super();
    			this.object = object;
    		}
    		@Override
    		public void run() {
    			super.run();
    //线程a调用的synchronized声明的方法 object.meathodA(); } } public static class ThreadB extends Thread { private MyObject object; public ThreadB(MyObject object) { super(); this.object = object; } @Override public void run() { super.run();
    //线程b调用的普通方法 object.meathodB(); } }

      输出结果如下:可见输出结果是交叉的,说明线程B可以以异步的形式调用MyObject类中的非synchronized类型的方法

     

          上面说过了synchronized方法和普通方法的调用,那么synchronized方法/块的内部调用其他的synchronized方法/块的时候,是怎么的情况呢,我写了一个小例子,如下:

           public static void main(String[] args) {
    
    		MyThread t = new MyThread();
    		t.start();
    	}
    
    	public static class Service {
    
    		synchronized public void service1() {
    			System.out.println("service1");
    			service2();
    		}
    
    		synchronized public void service2() {
    			System.out.println("service2");
    			service3();
    		}
    
    		synchronized public void service3() {
    			System.out.println("service3");
    		}
    	}
    
    	public static class MyThread extends Thread {
    
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    			super.run();
    			Service s = new Service();
    			s.service1();
    		}
    	}
    

       运行结果如下:从运行结果可以引出一个概念“可重入锁”:自己可以再次获得自己的内部锁。说明一个线程如果获得了某个对象锁,此时这个对象锁还没有释放,当想要再次获得这个对象锁的时候还是可以获取的。

      

           synchronized“可重入锁“还有一个特点,是什么呢?我写了一个例子,可以看一下,如下:

           public static void main(String[] args) {
    
    		MyThread t = new MyThread();
    		t.start();
    	}
    
    	public static class MyThread extends Thread {
    
    		@Override
    		public void run() {
    			super.run();
    			Sub sub = new Sub();
    			sub.openrateSubMenthod();
    		}
    	}
    
     }
          //Sub和Main两个外部类
           class Sub extends Main {
    
    	    synchronized public void openrateSubMenthod() {
                    //用的是父类定义的i=10
    		while (i > 0) {
    
    			i--;
    			try {
    				System.out.println("sub print i=" + i);
    				Thread.sleep(100);
    //调用父类的openrateMainMenthod()方法 this.openrateMainMenthod(); } catch (Exception e) { e.printStackTrace(); } } } } class Main { public int i = 10; synchronized public void openrateMainMenthod() { i--; try { System.out.println("main print i=" + i); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } }
      运行结果如下:在执行子线程MyThread的run方法中,调用了子类Sub重写父类Main中的operateMainMenthod()方法,而在子类Sub的operateMainMenthod()方法中又调用了父类的synchronized方法,从执行结果可以看出,“可重入锁”支持在父子类继承的环境中。

           

            上面说“可重入锁”支持父子类继承的环境中,但是同步是不具有继承性的,为了证明这一点,我写了一个小例子,如下:

     public class Test09 {
    
    	public static void main(String[] args) {
    
    		Child c = new Child();
    		MyThreasA a = new MyThreasA(c);
    		a.setName("A");
    		MyThreasB b = new MyThreasB(c);
    		b.setName("B");
    		a.start();
    		b.start();
    	}
    
    	public static class MyThreasA extends Thread {
    
    		private Child c;
    
    		public MyThreasA(Child c) {
    			super();
    			this.c = c;
    		}
    
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    			super.run();
    			c.serviceMethod();
    		}
    	}
    
    	public static class MyThreasB extends Thread {
    
    		private Child c;
    
    		public MyThreasB(Child c) {
    			super();
    			this.c = c;
    		}
    
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    			super.run();
    			c.serviceMethod();
    		}
    	}
    }
    
    //Child和Parent两个外部类,子类与父类 class Child extends Parent { @Override public void serviceMethod() { try { System.out.println("int child 下一步 sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int child 下一步 sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class Parent { synchronized public void serviceMethod() { try { System.out.println("int parent 下一步 sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int parent 下一步 sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

          运行结果如下:从运行结果可以看出,线程A和线程B交叉出现的,没有同步,说明子类的serviceMethod()方法并没有synchronized的特点,如果有synchronized的特点执行结果应该是同步的,也就是说一个线程执行完之后释放锁之后另一个线程才能执行,如果想让子类的方法也拥有synchronized的特点需要自己在这个方法前面收到添加synchronized关键字。

         

          在Child类的serviceMethod()方法前添加完synchronized字段之后,执行结果如下:

          

           以上说的都是synchronized对象监视器为Object时的使用,也就是说这里的锁是对象锁,但是在这里关键字synchronized都是用来声明方法的,这样写的弊端和如何改进我会在下节里说到的。

     

      

     

     

           

  • 相关阅读:
    SQL集合函数中case when then 使用技巧
    appium -- 页面出现弹窗,关闭后,无法识别页面元素
    SQLite3中dos命令下退出"...>"状态的方法
    android SharedPreferences 浅析
    BigDecimal简单说
    appium-手势密码实现-automationName 是Appium的情况
    Android color颜色-色号总结
    adb启动和关闭
    DesiredCapabilities的作用
    Android 使用intent传递返回值:startActivityForResult()与onActivityResult()与setResult()参数分析,activity带参数的返回
  • 原文地址:https://www.cnblogs.com/chentong/p/5650489.html
Copyright © 2011-2022 走看看