3分快3app二维码_Java多线程,对锁机制的进一步分析

  • 时间:
  • 浏览:0

1 可重入锁

    可重入锁,也叫递归锁。它有两层含义,第一,当另有十几个 多程序 在外层函数得到可重入锁后,能直接递归地调用该函数,第二,同一多程序 在外层函数获得可重入锁后,内层函数都不能 直接获取该锁对应其它代码的控制权。之前 当村里人 提到的synchronized和ReentrantLock一定会 可重入锁。

    通过ReEnterSyncDemo.java,当村里人 来演示下synchronized关键字的可重入性。    

1	class SyncReEnter implements Runnable{
2	   public synchronized void get(){
3	     System.out.print(Thread.currentThread().getId() + "\t");
4	      //在get方法里调用set
5	      set();
6	    }
7	    public synchronized void set()
8	    {System.out.print(Thread.currentThread().getId()+"\t"); }
9	    public void run() //run方法里调用了get方法
10	    { get();}
11	}
12	public class ReEnterSyncDemo {
13	    public static void main(String[] args) {
14	       	SyncReEnter demo=new SyncReEnter();
15	        new Thread(demo).start();
16	        new Thread(demo).start();
17	    }
18	}

    在第1行里,当村里人 是让syncReEnter类通过实现Runnable的方法来实现多多程序 ,在其中第2和第7行所定义的get和set方法均饱含synchronized关键字。在第9行定义的run方法里,当村里人 调用了get方法。在main函数的第15和16行里,当村里人 启动了2次多程序 ,这段代码的输出如下。

    8   8   9   9  

    在第15行第一次启动多程序 时,在run方法里,会调用饱含synchronized关键字的get方法,这时你一种生活 多程序 会得到get方法的锁,当执行到get里的set方法时,之前 set方法也饱含synchronized关键字,之前 set是饱含在get里的,统统 这里太大再次申请set的锁,能继续执行,统统 通过输出,当村里人 能看多get和set的打印励志的话 是连续输出的。同理当村里人 能理解第16行第二次启动多程序 的输出。

    通过ReEnterLock.java,当村里人 来演示下ReentrantLock的可重入性。      

1	import java.util.concurrent.locks.ReentrantLock;
2	class LockReEnter implements Runnable {
3		ReentrantLock lock = new ReentrantLock();
4		public void get() {
5		  lock.lock();
6	  	  System.out.print(Thread.currentThread().getId()+"\t");
7		  // 在get方法里调用set
8		  set();
9		  lock.unlock();
10	   }
11	   public void set() {
12		lock.lock();
13		System.out.print(Thread.currentThread().getId() + "\t");
14		lock.unlock();
15	   }
16	   public void run() 
17	   { get(); }
18	}
19	public class ReEnterLock {
20		public static void main(String[] args) {
21			LockReEnter demo = new LockReEnter();
22			new Thread(demo).start();
23			new Thread(demo).start();
24		}
25	}

    在第2行创建的LockReEnter类里,当村里人 同样饱含了get和set方法,并在get方法里调用了set方法,只不过在get和set方法里,当村里人 一定会 用synchronized,却说用第3行定义的ReentrantLock类型的lock对象来管理多多程序 的并发,在第16行的run方法里,当村里人 同样地调用了get方法。

    在main函数里,当村里人 同样地在第22和23行里启动了两次多程序 ,这段代码的运行结果如下。

    8   8   9   9

    当在第22行里第一次启动LockReEnter类型的多程序 后,在调用get方法时,能得到第5行的锁对象,get方法会调用set方法,人太好 set方法里的第12行会再次申请锁,但之前 LockReEnter多程序 在get方法里之前 得到了锁,统统 在set方法里不能得到锁,统统 第一次运行时,get和set方法会一块儿执行,同样地,在第23行第二次其中多程序 时,也会一块儿打印get和set方法里的输出。

    在项目的许多场景里,另有十几个 多程序 有之前 都要多次进入被锁关联的方法,比如某数据库的操作的多程序 都要多次调用被锁管理的“获取数据库连接”的方法,这时,之前 使用可重入锁就能避免死锁的间题,相反,之前 当村里人 一定会 用可重入锁,没有在第二次调用“获取数据库连接”方法时,一定会 之前 被锁住,从而导致 死锁间题。

2 公平锁和非公平锁

    在创建Semaphore对象时,当村里人 都不能 通过第另有十几个 参数,来指定该Semaphore对象是否以公平锁的方法来调度资源。

    公平锁会维护另有十几个 等待歌曲队列,多个在阻塞情况汇报等待歌曲的多程序 会被插入到你一种生活 等待歌曲队列,在调度时是按它们所发请求的时间顺序获取锁,而对于非公平锁,当另有十几个 多程序 请求非公平锁时,之前 此时该锁变成可用情况汇报,没有你一种生活 多程序 会跳过等待歌曲队列中所有的等待歌曲多程序 而获得锁。

    当村里人 在创建可重入锁时,也都不能 通过调用带布尔类型参数的构造函数来指定该锁是否公平锁。ReentrantLock(boolean fair)。

    在项目里,之前 请求锁的平均时间间隔较长,建议使用公平锁,反之建议使用非公平锁。

    比如有个服务窗口,之前 采用非公平锁的方法,当窗口空闲时,一定会 让下一号来,却说然之前 人就服务,曾经能缩短窗口的空闲等待歌曲时间,从而提升单位时间内的服务数量(也却说吞吐量)。相反,之前 这是个比较冷门的服务窗口,在统统 时间里来请求服务的频次不须高,比如一小时才来另两个人所有所有,没有就都不能 确定公平锁了。之前 ,之前 要缩短用户的平均等待歌曲时间,没有都不能 确定公平锁,曾经就能避免“早到的请求晚避免“的情况汇报。

3 读写锁

    之前 当村里人 通过synchronized和ReentrantLock来管理临界资源时,只却说另有十几个 多程序 得到锁,其它多程序 没有操作你一种生活 临界资源,你一种生活 锁都不能 叫做“互斥锁”。

    和你一种生活 管理方法相比,ReentrantReadWriteLock对象会使用两把锁来管理临界资源,另有十几个 是“读锁“,曾经是“写锁“。

    之前 另有十几个 多程序 获得了某资源上的“读锁“,没有其它对该资源执行“读操作“的多程序 还是都不能 继续获得该锁,也却说说,“读操作“都不能 并发执行,但执行“写操作“的多程序 会被阻塞。之前 另有十几个 多程序 获得了某资源的“写锁“,没有其它任何企图获得该资源“读锁“和“写锁“的多程序 都将被阻塞。

    和互斥锁相比,读写锁在保证并发时数据准确性的一块儿,允许多个多程序 一块儿“读“某资源,从而能提升强度。通过下面的ReadWriteLockDemo.java,当村里人 来观察下通过读写锁管理读写并发多程序 的方法。    

1	import java.util.concurrent.locks.Lock;
2	import java.util.concurrent.locks.ReentrantReadWriteLock;
3	class ReadWriteTool {
4		private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
5		private Lock readLock = lock.readLock();
6		private Lock writeLock = lock.writeLock();
7		private int num = 0;
8	  	public void read() {//读的方法 
9			int cnt = 0;
10			while (cnt++ < 3) {
11				try {
12					readLock.lock();				System.out.println(Thread.currentThread().getId()
13							+ " start to read");
14					Thread.sleep(50000);		
15		System.out.println(Thread.currentThread().getId() + " reading,"	+ num);
16				} catch (Exception e) 
17	            { e.printStackTrace();}
18	            finally { readLock.unlock(); 	}
19			}
20		}
21		public void write() {//写的方法
22			int cnt = 0;
23			while (cnt++ < 3) {
24				try {
25					writeLock.lock();		
26			System.out.println(Thread.currentThread().getId()
27							+ " start to write");
28					Thread.sleep(50000);
29					num = (int) (Math.random() * 10);
500				System.out.println(Thread.currentThread().getId() + " write," + num);
31				} catch (Exception e) 
32	            { e.printStackTrace();} 
33	            finally { writeLock.unlock();}
34			}
35		}
36	}

    在第3行定义的ReadWriteTool 类里,当村里人 在第4行创建了另有十几个 读写锁,并在第5和第6行,分别通过你一种生活 读写锁的readLock和writeLock方法,分别创建了读锁和写锁。

    在第8行的read方法里,当村里人 是先通过第12行的代码加“读锁“,之前 在第15行进行读操作。在第21行的write方法里,当村里人 是先通过第25行的代码加“写锁”,之前 在第500行进行写操作。    

37	class ReadThread extends Thread {
38		private ReadWriteTool readTool;
39		public ReadThread(ReadWriteTool readTool) 
40	    { this.readTool = readTool;	}
41		public void run() 
42	    { readTool.read();}
43	}
44	class WriteThread extends Thread {
45		private ReadWriteTool writeTool;
46		public WriteThread(ReadWriteTool writeTool) 
47	    { this.writeTool = writeTool; }
48		public void run() 
49	    { writeTool.write();	}
500	}

    在第37行和第44行里,当村里人 分别定义了读和写这另有十几个 多程序 ,在ReadThread多程序 的run方法里,当村里人 调用了ReadWriteTool类的read方法,而在WriteThread多程序 的run方法里,则调用了write方法。    

51	public class ReadWriteLockDemo {
52		public static void main(String[] args) {
53			ReadWriteTool tool = new ReadWriteTool();
54			for (int i = 0; i < 3; i++) {
55				new ReadThread(tool).start();
56				new WriteThread(tool).start();
57			}
58		}
59	}

    在main函数的第53行,当村里人 创建了另有十几个 ReadWriteTool类型的tool对象,在第55和56行初始化读写多程序 时,当村里人 传入了该tool对象,也却说说,通过54行for循环创建并启动的多个读写多程序 是通过同另有十几个 读写锁来控制读写并发操作的。

    出于多多程序 并发调度的导致 ,当村里人 每次运行都之前 得到不同的结果,但从有有哪些不同的结果里,当村里人 都態明显地看出读写锁协调管理读写多程序 的方法,比如来看下如下的要素输出结果。    

1	8 start to read
2	10 start to read
3	12 start to read
4	8 reading,0
5	10 reading,0
6	12 reading,0
7	9 start to write
8	9 write,2
9	11 start to write
10	11 write,6

    这里当村里人 是通过ReadWriteTool类里的读写锁管理其中的num值,从第1到第6行的输出中当村里人 能看多,人太好 8号多程序 之前 得到读锁现在结束读num资源时,10号和12号读多程序 依然都不能 得到读锁,从而能并发地读取num资源。但在读操作期间,是不允许有写操作的多程序 进入,也却说说,当num资源上有读锁期间,其它多程序 是无法得到该资源上的“写锁”的。

    从第7到第10行的输出中当村里人 能看多,当9号多程序 得到num资源上的“写锁”时,其它多程序 是无法得到该资源上的“读锁“和“写锁“的,而11号多程序 一定得当9号多程序 释放了“写锁”后,不能得到num资源的“写锁”。

    之前 在项目里对许多资源(比如文件)有读写操作,这时当村里人 不妨都不能 使用读写锁,之前 读操作的数量要远超过写操作时,没有更都不能 用读写锁来让读操作都不能 并发执行,从而提升性能。