QQ思维国际_提供小磊资源网技术_QQ技术乐园资讯 - QQ思维国际在线分享QQ技术教程,分享QQ技巧,电脑技术,网络技术,热门活动等各种QQ思维国际,小磊资源网,QQ技术乐园资源

大发快3在线稳定计划_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资源的“写锁”。

    将会在项目里对类事于于资源(比如文件)有读写操作,这时大伙不妨都须要使用读写锁,将会读操作的数量要远超过写操作时,那么更都须要用读写锁来让读操作都须要并发执行,从而提升性能。