大发快三在线注册平台_Java多线程,对锁机制的进一步分析

  • 时间:
  • 浏览:1
  • 来源:河北生活网_河北人的网上生活家园

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资源的“写锁”。

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