首页 > 技术文章 > 类锁与实例锁

yangyongjie 2017-09-13 21:18 原文

Synchronized(实例锁)和 static Synchronized(类锁)区别

  实例锁:锁在某个对象上,如果该类是单例的,那么该锁也有全局锁的概念

  类锁:该锁针对的类,无论实例多少个对象,那么线程都共享该锁。

 

Java的锁分为对象锁和类锁。

  1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内针对该对象的操作只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

  2. 然而,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

  3. 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对该object中所有其它synchronized(this)同步代码块的访问将被阻塞。

  4. 同步加锁的是对象,而不是代码。因此,如果你的类中有一个同步方法,这个方法可以被两个不同的线程同时执行,只要每个线程自己创建一个的该类的实例即可。

  5. 不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。

  6. synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。

  7.对一个全局对象或者类加锁时,对该类的所有对象都起作用。

 

 

对象锁:

当多个线程同属于一个对象时,线程是安全的

如:

 1 public class ThreadTest implements Runnable {
 2     public synchronized void get() {
 3         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 4         set();
 5     }
 6 
 7     private synchronized void set() {
 8         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 9     }
10 
11     @Override
12     public void run() {
13         get();
14     }
15 
16     public static void main(String[] args) {
17         ThreadTest test = new ThreadTest();
18         new Thread(test, "test").start();
19         new Thread(test, "test").start();
20         new Thread(test, "test").start();
21     }
22 }

多次试验后的运行结果:

1 test:11
2 test:11
3 test:12
4 test:12
5 test:10
6 test:10

 

当多个线程不属于同一个对象时:

 1 public class ThreadTest implements Runnable {
 2     public synchronized void get() {
 3         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 4         set();
 5     }
 6 
 7     private synchronized void set() {
 8         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 9     }
10 
11     @Override
12     public void run() {
13         get();
14     }
15 
16     public static void main(String[] args) {
17         ThreadTest test = new ThreadTest();
18         ThreadTest test2 = new ThreadTest();
19         new Thread(test, "test").start();
20         new Thread(test, "test").start();
21         new Thread(test, "test").start();
22         new Thread(test2, "test2").start();
23         new Thread(test2, "test2").start();
24         new Thread(test2, "test2").start();
25     }
26 }

结果:

 1 test:10
 2 test:10
 3 test:12
 4 test2:13
 5 test:12
 6 test2:13
 7 test:11
 8 test:11
 9 test2:14
10 test2:14
11 test2:15
12 test2:15

 

可见,当对象1的线程在执行get方法时,还没执行完毕;对象2的线程也在执行get方法。

优化:

加入类锁 static synchronized之后:

代码:

 1 public class ThreadTest implements Runnable {
 2     public static synchronized void get() {
 3         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 4         set();
 5     }
 6 
 7     private static synchronized void set() {
 8         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 9     }
10 
11     @Override
12     public void run() {
13         get();
14     }
15 
16     public static void main(String[] args) {
17         ThreadTest test = new ThreadTest();
18         ThreadTest test2 = new ThreadTest();
19         new Thread(test, "test").start();
20         new Thread(test, "test").start();
21         new Thread(test, "test").start();
22         new Thread(test2, "test2").start();
23         new Thread(test2, "test2").start();
24         new Thread(test2, "test2").start();
25     }
26 }

运行结果:

 1 test:11
 2 test:11
 3 test2:13
 4 test2:13
 5 test:10
 6 test:10
 7 test2:14
 8 test2:14
 9 test:12
10 test:12
11 test2:15
12 test2:15

此时,同一个类的对象的所有线程在同一时刻只有一个线程执行get方法。

 

重入锁 ReentrantLock:

示例

 1 public class ThreadTest2 implements Runnable {
 2 
 3     ReentrantLock lock = new ReentrantLock();
 4 
 5     public void get() {
 6         lock.lock();
 7         System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
 8         set();
 9         lock.unlock();
10     }
11 
12     public void set() {
13         lock.lock();
14         System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
15         lock.unlock();
16     }
17 
18     @Override
19     public void run() {
20         get();
21     }
22 
23     public static void main(String[] args) {
24         ThreadTest2 test = new ThreadTest2();
25         new Thread(test).start();
26         new Thread(test).start();
27         new Thread(test).start();
28     }
29 
30 }

多次运行结果:

1 Thread-0:10
2 Thread-0:10
3 Thread-1:11
4 Thread-1:11
5 Thread-2:12
6 Thread-2:12

多个线程不属于一个对象的情况下:

 1 public class ThreadTest2 implements Runnable {
 2 
 3     ReentrantLock lock = new ReentrantLock();
 4 
 5     public void get() {
 6         lock.lock();
 7         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 8         set();
 9         lock.unlock();
10     }
11 
12     public void set() {
13         lock.lock();
14         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
15         lock.unlock();
16     }
17 
18     @Override
19     public void run() {
20         get();
21     }
22 
23     public static void main(String[] args) {
24         ThreadTest2 test = new ThreadTest2();
25         ThreadTest2 test2 = new ThreadTest2();
26         new Thread(test, "test").start();
27         new Thread(test, "test").start();
28         new Thread(test, "test").start();
29         new Thread(test2, "test2").start();
30         new Thread(test2, "test2").start();
31         new Thread(test2, "test2").start();
32     }
33 
34 }

结果:

 1 test:10
 2 test2:14
 3 test:10
 4 test:11
 5 test2:14
 6 test:11
 7 test2:13
 8 test2:13
 9 test:12
10 test:12
11 test2:15
12 test2:15

线程不安全的原因:多个对象都有自己的  ReentrantLock lock = new ReentrantLock();

因此不属于一个对象的多个线程之间用的不是同一把锁,解决方案,将ReentrantLock 对象声明为静态的,使其成为类变量。多个对象共享。

 1 public class ThreadTest2 implements Runnable {
 2 
 3     static ReentrantLock lock = new ReentrantLock();
 4 
 5     public void get() {
 6         lock.lock();
 7         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
 8         set();
 9         lock.unlock();
10     }
11 
12     public void set() {
13         lock.lock();
14         System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getId());
15         lock.unlock();
16     }
17 
18     @Override
19     public void run() {
20         get();
21     }
22 
23     public static void main(String[] args) {
24         ThreadTest2 test = new ThreadTest2();
25         ThreadTest2 test2 = new ThreadTest2();
26         new Thread(test, "test").start();
27         new Thread(test, "test").start();
28         new Thread(test, "test").start();
29         new Thread(test2, "test2").start();
30         new Thread(test2, "test2").start();
31         new Thread(test2, "test2").start();
32     }
33 
34 }

 

 

END.

推荐阅读