Android开发经验谈Android开发半栈工程师

java synchronized关键字

2017-11-13  本文已影响281人  选一个昵称这么难

synchronized关键字的作用是保证多线程操作的安全
简单说:多个线程公用一把锁时,就要一个一个来执行

1.不使用synchronized

class Fun{
      public void run(){
          for (int i = 0; i < 10; ++i)
          {
              System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
          }
      }
}
class MyThread extends Thread{
    Fun fun;
    public MyThread(Fun fun){
        this.fun = fun;
    }
    @Override
    public void run() {
        fun.run();
    }
}
测试运行
       Fun fun = new Fun();
        MyThread thread1 = new MyThread(fun);
        MyThread thread2 = new MyThread(fun);
        thread1.start();
        thread2.start();

结果:

11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-2
11-13 15:43:53.238 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-3
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-2
11-13 15:43:53.238 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-3
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-2
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-3

从中可以看出,到底是哪个线程先运行时没有章法的,也就是不同步,哪个线程抢到了运行权哪个线程就运行.
如果使用了synchronized呢?

2.synchronized修饰普通方法

只需稍微修改下:
class Fun{
      public synchronized void run(){
          for (int i = 0; i < 10; ++i)
          {
              System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
          }
      }
}

结果:

11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-2
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-3
11-13 15:45:26.289 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-3
11-13 15:45:26.289 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-3

从中可以看出,thread2执行完fun.run()方法之后,thread3才会执行,这说明thread2在执行的时候获取了锁,直到执行完之后才把锁释放,这就保证了线程安全

但是,如果我定义两个Fun对象呢,比如这样

Fun fun1 = new Fun();
        Fun fun2 = new Fun();
        MyThread thread1 = new MyThread(fun1);
        MyThread thread2 = new MyThread(fun2);
        thread1.start();
        thread2.start();

结果如下:

11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-2
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-3
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-3

结果说明,线程又不同步了
为什么呢?
因为synchronized修饰的普通方法的的锁是这个类的对象,在示例中我们定义了两个对象,也就是每个thread用的锁不是一个,每个thread都有自己的锁,这跟上面例子不同的地方就是,上面例子只有一把锁,这个有两把锁
接着往下看:

3.synchronized修饰static方法

class Fun{
      public static synchronized void run(){
          for (int i = 0; i < 10; ++i)
          {
              System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
          }
      }
}
class MyThread extends Thread{
    Fun fun;
    public MyThread(Fun fun){
        this.fun = fun;
    }
    @Override
    public void run() {
        Fun.run();
    }
}
调用情况:
 Fun fun1 = new Fun();
        Fun fun2 = new Fun();
        MyThread thread1 = new MyThread(fun1);
        MyThread thread2 = new MyThread(fun2);
        thread1.start();
        thread2.start();

结果:

11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 0____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 1____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 2____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 3____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 4____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 5____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 6____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 7____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 8____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 9____Thread-2
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 0____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 1____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 2____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 3____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 4____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 5____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 6____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 7____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 8____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 9____Thread-3

结果是,又同步了.
为什么呢?
因为静态方法属于这个类,synchronized修饰的静态方法的锁是这个类,因为class只有一个,所以锁只有一把,因此就会同步.
既然synchronized修饰的静态方法的锁是这个class,也就是说当这个锁被占有的时候,这个类中的其他需要这个class当锁的方法一样得等到锁才能执行,下面看下例子来说明:

class Fun{
      public static synchronized void run(){
          for (int i = 0; i < 10; ++i)
          {
              System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
          }
      }
    public  static synchronized void run2(){
        for (int i = 0; i < 10; ++i)
        {
            System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
        }
    }
}
class MyThread extends Thread{
    Fun fun;
    public MyThread(Fun fun){
        this.fun = fun;
    }
    @Override
    public void run() {
        Fun.run();
    }
}
class MyThread2 extends Thread{
    Fun fun;
    public MyThread2(Fun fun){
        this.fun = fun;
    }
    @Override
    public void run() {
        Fun.run2();
    }
}
执行情况:
Fun fun1 = new Fun();
        Fun fun2 = new Fun();
        MyThread thread1 = new MyThread(fun1);
        MyThread2 thread2 = new MyThread2(fun2);
        thread1.start();
        thread2.start();

结果:

11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 0____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 1____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 2____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 3____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 4____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 5____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 6____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 7____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 8____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 9____Thread-2
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 0____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 1____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 2____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 3____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 4____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 5____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 6____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 7____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 8____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 9____Thread-3

这两个thread运行的静态方法都需要这个Fun.class当作锁,因此执行是一个接着一个执行的
下面对这个例子稍微修改下

class Fun{
      public static synchronized void run(){
          for (int i = 0; i < 10; ++i)
          {
              System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
          }
      }
    public  synchronized void run2(){
        for (int i = 0; i < 10; ++i)
        {
            System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
        }
    }
}
class MyThread extends Thread{
    Fun fun;
    public MyThread(Fun fun){
        this.fun = fun;
    }
    @Override
    public void run() {
        Fun.run();
    }
}
class MyThread2 extends Thread{
    Fun fun;
    public MyThread2(Fun fun){
        this.fun = fun;
    }
    @Override
    public void run() {
        fun.run2();
    }
}

修改的关键一点:Fun中的run2()方法不是静态的
结果:

11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 0____Thread-2
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 0____Thread-3
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 1____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 2____Thread-2
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 1____Thread-3
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 3____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 4____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 5____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 6____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 7____Thread-2
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 2____Thread-3
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 3____Thread-3
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 4____Thread-3
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 5____Thread-3
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 6____Thread-3
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 7____Thread-3
11-13 16:22:10.411 31450-31465/? I/System.out: Hello: 8____Thread-2
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 8____Thread-3
11-13 16:22:10.411 31450-31465/? I/System.out: Hello: 9____Thread-2
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 9____Thread-3

可以看出又不同步了
为什么?
因为run()是静态的,run2()方法不是,这两个方法虽然都是用synchronized修饰,但是锁不是一把,run()的锁是class,而run2()的锁是class对象,不同的锁当然不能同步.

4.synchronized修饰代码块

这个和修饰普通方法差不多,只不过同步的地方是被synchronized包起来的的地方,它的锁取决于这个方法是否是静态的,我们常用的地方就是单例,举个例子:

 public static PushProcess getInstance() {
        if (instance == null) {
            synchronized (PushProcess.class) {
                if (instance == null) {
                    instance = new PushProcess();
                }
            }
        }
        return instance;
    }

可能一个方法中只有几行代码会涉及到线程同步问题,所以synchronized块比synchronized方法更加细粒度地控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程所访问,方法中的其他语句仍然可以同时被多个线程所访问(包括synchronized块之前的和之后的)。

上一篇下一篇

猜你喜欢

热点阅读