3分快3最新网址_Java多线程,对锁机制的进一步分析

  • 时间:
  • 浏览:0

1 可重入锁

    可重入锁,也叫递归锁。它有两层含义,第一,当另3个 线程运行在外层函数得到可重入锁后,能直接递归地调用该函数,第二,同一线程运行在外层函数获得可重入锁后,内层函数可不想想 直接获取该锁对应其它代码的控制权。事先 朋友 提到的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法律方式里的输出。

    在项目的其他场景里,另3个 线程运行有不可能 都要多次进入被锁关联的法律方式,比如某数据库的操作的线程运行都要多次调用被锁管理的“获取数据库连接”的法律方式,这时,不可能 使用可重入锁就能除理死锁的问題,相反,不可能 朋友 就有用可重入锁,只能在第二次调用“获取数据库连接”法律方式时,就有不可能 被锁住,从而意味死锁问題。

2 公平锁和非公平锁

    在创建Semaphore对象时,朋友 可不想想 通过第另3个 参数,来指定该Semaphore对象与非 以公平锁的法律方式来调度资源。

    公平锁会维护另3个 等候队列,多个在阻塞清况 等候的线程运行会被插入到你你这个 等候队列,在调度时是按它们所发请求的时间顺序获取锁,而对于非公平锁,当另3个 线程运行请求非公平锁时,不可能 此时该锁变成可用清况 ,只能你你这个 线程运行会跳过等候队列中所有的等候线程运行而获得锁。

    朋友 在创建可重入锁时,也可不想想 通过调用带布尔类型参数的构造函数来指定该锁与非 公平锁。ReentrantLock(boolean fair)。

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

    比如有个服务窗口,不可能 采用非公平锁的法律方式,当窗口空闲时,就有让下一号来,全都假如有一天来人就服务,另另3个 能缩短窗口的空闲等候时间,从而提升单位时间内的服务数量(也全都吞吐量)。相反,不可能 这是个比较冷门的服务窗口,在全都有时间里来请求服务的频次不想高,比如一小时才来另3个 人,只能就可不想想 取舍公平锁了。不可能 ,不可能 要缩短用户的平均等候时间,只能可不想想 取舍公平锁,另另3个 就能除理“早到的请求晚除理“的清况 。

3 读写锁

    事先 朋友 通过synchronized和ReentrantLock来管理临界资源时,只全都另3个 线程运行得到锁,其它线程运行只能操作你你这个 临界资源,你你这个 锁可不想想 叫做“互斥锁”。

    和你你这个 管理法律方式相比,ReentrantReadWriteLock对象会使用两把锁来管理临界资源,另3个 是“读锁“,另另3个 是“写锁“。

    不可能 另3个 线程运行获得了某资源上的“读锁“,只能其它对该资源执行“读操作“的线程运行还是可不想想 继续获得该锁,也全都说,“读操作“可不想想 并发执行,但执行“写操作“的线程运行会被阻塞。不可能 另3个 线程运行获得了某资源的“写锁“,只能其它任何企图获得该资源“读锁“和“写锁“的线程运行都将被阻塞。

    和互斥锁相比,读写锁在保证并发时数据准确性的同去,允其他个线程运行同去“读“某资源,从而能提升强度。通过下面的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(30);		
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(30);
29					num = (int) (Math.random() * 10);
30				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行创建了另3个 读写锁,并在第5和第6行,分别通过你你这个 读写锁的readLock和writeLock法律方式,分别创建了读锁和写锁。

    在第8行的read法律方式里,朋友 是先通过第12行的代码加“读锁“,并且在第15行进行读操作。在第21行的write法律方式里,朋友 是先通过第25行的代码加“写锁”,并且在第30行进行写操作。    

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();	}
30	}

    在第37行和第44行里,朋友 分别定义了读和写这另3个 线程运行,在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行,朋友 创建了另3个 ReadWriteTool类型的tool对象,在第55和56行初始化读写线程运行时,朋友 传入了该tool对象,也全都说,通过54行for循环创建并启动的多个读写线程运行是通过同另3个 读写锁来控制读写并发操作的。

    出于线程运行并发调度的意味,朋友 每次运行就有可能 得到不同的结果,但从哪好多个不同的结果里,朋友 都態明显地看出读写锁协调管理读写线程运行的法律方式,比如来看下如下的要素输出结果。    

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

    不可能 在项目里对其他资源(比如文件)有读写操作,这时朋友 不妨可不想想 使用读写锁,不可能 读操作的数量要远超过写操作时,只能更可不想想 用读写锁来让读操作可不想想 并发执行,从而提升性能。