多线程基础回顾(二)-同步锁

线程同步方式

启动3个线程,目的打印出“abcabcabc”字样。

 1 class Add implements Runnable{
 2     private static char[] arr={'a','b','c'};
 3     @Override
 4     public void run() {
 5         for(int i=0;i<arr.length;i++){
 6             System.out.print(arr[i]);
 7         }
 8     }
 9 }
10 public class ThreadTest{
11     public static void main(String[] args) {
12         Add add = new Add();
13         new Thread(add).start();
14         new Thread(add).start();
15         new Thread(add).start();
16     }
17 }
View Code

上边的代码是无法实现想要的结果,需要加入线程同步才行。

一、使用synchronized关键字

将上边的runable稍作修改

 1 class Add implements Runnable{
 2     private static char[] arr={'a','b','c'};
 3     @Override
 4     public void run() {
 5         synchronized (this) {
 6             for(int i=0;i<arr.length;i++){
 7                 System.out.print(arr[i]);
 8             }
 9         }
10     }
11 }
View Code

 

这里this指实例本身,相当于实例方法同步。

1.实例方法同步:synchronized void method(){}

指同步在拥有该方法的对象上(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。

2. 静态方法同步:synchronized static void method(){}

指同步在该方法所在的类对象上,它可以对类的所有对象实例起作用。

3. 实例方法中的同步块:synchronized(this){/区块/}

表示只对这个区块的资源实行互斥访问,括号内为监视器,(这里this为调用该方法的实例作为监视器,执行时和同步方法一样。)最简单的监听器:byte by = new byte[0];

4. synchronized关键字是不能继承的,

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


二、使用Lock

ReentrantLock 重入锁:

  • 可重入、互斥、实现了Lock接口的锁,功能同synchronized,更加灵活。
  • 需手动释放锁,一般使用try...finally,防止异常死锁。
 1 class Add implements Runnable{
 2     private Lock lock = new ReentrantLock();
 3     private static char[] arr={'a','b','c'};
 4     @Override
 5     public void run() {
 6         lock.lock();//获取锁
 7         try{
 8             for(int i=0;i<arr.length;i++){
 9                 System.out.print(arr[i]);
10             }
11         }catch(Expetion e){
12             //捕获操作
13         }finally{
14             lock.unlock();//手动释放锁
15         }
16 
17     }
18 }
View Code

 

ReentrantReadWriteLock 读写锁,

  • 读锁与写锁互斥。
  • 读锁共享,可被多条线程同时占有。
  • 写锁与读锁,写锁互斥,只能被一条线程占有。
  • 锁降级:获取写锁,再获取读锁,再释放写锁,则降级为读锁。
 1 class readAndWrite{
 2     private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 3     private final Lock rl = lock.readLock();
 4     private final Lock wl = lock.writeLock();
 5     StringBuilder sb = new StringBuilder("start:");
 6     public void read(){
 7         String name = Thread.currentThread().getName();
 8         if(lock.isWriteLocked()){
 9             System.out.println(name+":写锁被占有,无法获取读锁");
10         }else{
11             try{
12                 rl.lock();
13                 System.out.println(name+":得到读锁");
14                 Thread.sleep(1000);
15             } catch (InterruptedException e) {
16                 e.printStackTrace();
17             }finally{
18                 System.out.println(name+":释放读锁");
19                 rl.unlock();
20             }
21         }
22     }
23     public void write(){
24         String name = Thread.currentThread().getName();
25         try{
26             wl.lock();
27             System.out.println(name+":获取到写锁");
28             sb.append(name+",");
29         }finally{
30             wl.unlock();
31             System.out.println(name+":释放写锁");
32         }
33     }
34 }
35 //测试
36 public class ThreadTest{
37     public static void main(String[] args) {
38         readAndWrite wr = new readAndWrite();
39         ExecutorService ser1 = Executors.newFixedThreadPool(3);
40         ExecutorService ser2 = Executors.newFixedThreadPool(3);
41         for(int i=0;i<5;i++){
42             ser1.execute(new Runnable() {
43                 @Override
44                 public void run() {
45                     wr.write();
46                 }
47             });
48             ser2.execute(new Runnable() {
49                 @Override
50                 public void run() {
51                     wr.read();
52                 }
53             });
54         }
55         ser1.shutdown();
56         ser2.shutdown();
57         System.out.println("执行关闭线程池,sb="+wr.sb.toString());
58         try {
59                 while(!ser1.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
60                 System.out.println("等待线程池关闭");
61                 }
62                 System.out.println("线程池关闭,sb="+wr.sb.toString());
63         } catch (InterruptedException e) {
64             e.printStackTrace();
65         }
66         
67     }
68 }
View Code
上边测试代码执行结果
pool-1-thread-1:获取到写锁
pool-1-thread-1:释放写锁
pool-2-thread-1:得到读锁
执行关闭线程池,sb=start:pool-1-thread-1,
pool-2-thread-1:释放读锁
pool-1-thread-2:获取到写锁
pool-1-thread-2:释放写锁
pool-2-thread-2:得到读锁
等待线程池关闭
pool-2-thread-2:释放读锁
pool-1-thread-3:获取到写锁
pool-1-thread-3:释放写锁
pool-2-thread-3:得到读锁
pool-2-thread-2:写锁被占有,无法获取读锁
等待线程池关闭
pool-2-thread-3:释放读锁
pool-1-thread-1:获取到写锁
pool-1-thread-1:释放写锁
pool-2-thread-1:得到读锁
等待线程池关闭
pool-2-thread-1:释放读锁
pool-1-thread-2:获取到写锁
pool-1-thread-2:释放写锁
等待线程池关闭
线程池关闭,sb=start:pool-1-thread-1,pool-1-thread-2,pool-1-thread-3,pool-1-thread-1,pool-1-thread-2,

三、使用volatile修饰符

posted @ 2019-01-23 16:13  哈比Sport  阅读(121)  评论(0编辑  收藏  举报