Java多线程之Lock锁的线程交互

本文详细介绍了Java中的Lock机制,对比了lock与synchronized关键字的区别,深入解析了lock()与tryLock()方法的功能及应用场景,并通过实例展示了如何使用ReentrantLock进行线程间的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        之前创建锁的方式中,有说过Lock机制,并且比较了synchronized和lock的区别。本文主要讲解一下lock锁的功能以及实现线程交互。

1.lock()方法与tryLock()方法

        lock()方法指占用一个锁,当前线程占用这个对象,则其它线程则只能进入等待状态,而tryLock()方法,则是试图在指定时间内占用当前同步对象,如果占用不了继续等待,其它线程试图抢占。

        首先是lock()方法进行加锁,因为lock机制进行加锁,需要手动释放锁,只能在finally中调用lock对象的unlock()方法释放锁。

package waytobuildthread;

import person.Student;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest {

	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		final Lock lock=new ReentrantLock();
		
		
		Thread t1= new Thread(){
	           public void run(){
	            try
	            {
	            	lock.lock();
	            	System.out.println("b");
		        	
		        	System.out.println("c");
	            }
	            
	        	finally
	        	{
	        		lock.unlock();
	        	}
	           }  
	       };
	       t1.start();
	        
		
		
		
	       Thread t2= new Thread(){
	           public void run(){
	            try
	            {
	            	lock.lock();
		        	System.out.println("a");
		        	
		        	System.out.println("c");
	            }
	        	finally
	        	{
	        		lock.unlock();
	        	}
	           }
	            	
        		
	            	
	            
	       };
        t2.start();
	        
		
		
		

	}

}
b
c
a
c
        以下是使用tryLock()进行同步对象,因为是尝试加锁,所以需要用一个locked的布尔值进行判断是否加锁,只有加锁了才进行继续操作,以及释放锁操作。首先需要Lock lock=new ReentrantLock();创建lock对象。
package person;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;;
public class Student {
	public int k=0;
	public int total;
	public int perMonth;
	public String name;
	Lock lock=new ReentrantLock();
	Condition condition=lock.newCondition();//创建condition对象
	
	public void saveMoney(Student s) {
		// TODO Auto-generated method stub
		
		try
		{
			lock.lock();
			while(total>=3000)
			{
				try
				{
					condition.await();
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			
			s.total=s.total+2*(s.perMonth);
			System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
	
			condition.signal();
			
		}
		
		finally
		{
			lock.unlock();
			
			
		}
			
		
	}
	public void getMoney(Student s)
	{
		
		try
		{
			lock.lock();
			while(s.total<=0)
			{
				try
				{
					condition.await();//使当前线程暂停,让出占用当前同步对象的锁,并进入等待状态
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			
			s.total=s.total-s.perMonth;
			System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");	

			condition.signal();//唤醒那些暂停的线程,使其进入等待列表,类似于synchronized下的notify()方法
		
		}
		
		finally
		{
			lock.unlock();
			
			
		}
		


	}

}

运行程序,结果如下:

b
c
a
c

2.线程交互

        以上讲述了lock锁中的lock方法和tryLock()方法进行创建同步对象的,所以接下来就分别这两个方法进行交互,之前synchronized下的交互是由同步对象的wait()、notify()、notifyAll()三个方法实现的,而上述三个方法只能在synchronized块下进行使用,因此无法在lock锁中使用,而lock锁下实现交互对应的方法是condition对象的await()、signal()、signalAll()方法。

以下先介绍测试程序:

package waytobuildthread;

import person.Student;

public class RunnableTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		final Object obj=new Object();
		final Student s=new Student();
		final Student jack=new Student();
		s.total=1000;
		s.perMonth=200;
		s.name="Tom";
		System.out.println("Tom最开始拥有"+s.total+"元");
		Thread[] save=new Thread[10];
		Thread[] get=new Thread[10];
		for(int i=0;i<10;i++)
		{
			Thread t1= new Thread(){
	            public void run(){
	            	
	            	
	            	s.getMoney(s);;
	            	
	        		try {
	        			Thread.sleep(1000);;
	        		}
	        		catch(InterruptedException e) {
	        			e.printStackTrace();
	        		}
	            	
	            }
	        };
	        t1.start();
	        get[i]=t1;
		}
		
		for(int i=0;i<10;i++)
		{
			Thread t2= new Thread(){
	            public void run(){
	            	
	            	s.saveMoney(s);
	            	
	            	
	        		try {
	        			Thread.sleep(1000);;
	        		}
	        		catch(InterruptedException e) {
	        			e.printStackTrace();
	        		}
	            	
	            }
	        };
	        t2.start();
	        save[i]=t2;
		}
		//等待线程结束
		for(Thread t:get)
		{
			try
			{
				t.join();
				
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}

		}
		//等待线程结束
		for(Thread t:save)
		{
			try
			{
				t.join();
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}

		}
		
		System.out.println("最后Tom总共拥有"+s.total+"元");
		
	}

}

        首先使用lock()方法同步下进行交互,需要创建Lock对象,以及condition()对象。注意lock锁需要手动释放锁,即在finally中调用Lock对象的unlock()方法。以下是Student类:

package person;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;;
public class Student {
	public int k=0;
	public int total;
	public int perMonth;
	public String name;
	Lock lock=new ReentrantLock();
	Condition condition=lock.newCondition();//创建condition对象
	
	public void saveMoney(Student s) {
		// TODO Auto-generated method stub
		
		try
		{
			lock.lock();
			while(total>=3000)
			{
				try
				{
					condition.await();
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			
			s.total=s.total+2*(s.perMonth);
			System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
	
			condition.signal();
			
		}
		
		finally
		{
			lock.unlock();
			
			
		}
			
		
	}
	public void getMoney(Student s)
	{
		
		try
		{
			lock.lock();
			while(s.total<=0)
			{
				try
				{
					condition.await();//使当前线程暂停,让出占用当前同步对象的锁,并进入等待状态
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			
			s.total=s.total-s.perMonth;
			System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");	

			condition.signal();//唤醒那些暂停的线程,使其进入等待列表,类似于synchronized下的notify()方法
		
		}
		
		finally
		{
			lock.unlock();
			
			
		}
		


	}

}

运行测试程序,结果如下:

Tom最开始拥有1000元
Tom取了200当前总共拥有800元
Tom取了200当前总共拥有600元
Tom取了200当前总共拥有400元
Tom取了200当前总共拥有200元
Tom取了200当前总共拥有0元
Tom存了400当前总共拥有400元
Tom存了400当前总共拥有800元
Tom取了200当前总共拥有600元
Tom存了400当前总共拥有1000元
Tom取了200当前总共拥有800元
Tom存了400当前总共拥有1200元
Tom取了200当前总共拥有1000元
Tom存了400当前总共拥有1400元
Tom存了400当前总共拥有1800元
Tom取了200当前总共拥有1600元
Tom取了200当前总共拥有1400元
Tom存了400当前总共拥有1800元
Tom存了400当前总共拥有2200元
Tom存了400当前总共拥有2600元
Tom存了400当前总共拥有3000元
最后Tom总共拥有3000元
        以下是使用tryLock()方法的交互,从之前知道,需要设置一个标志进行判断是否占用到了,如果占用到才能进行继续操作。同时需要捕获异常:
package person;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;;
public class Student {
	public int k=0;
	public int total;
	public int perMonth;
	public String name;
	Lock lock=new ReentrantLock();
	Condition condition=lock.newCondition();
	
	public void saveMoney(Student s) {
		// TODO Auto-generated method stub
		boolean locked=false;
		try
		{
			locked=lock.tryLock(1, TimeUnit.SECONDS);
			while(total>=3000)
			{
				try
				{
					condition.await();
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			if(locked)
			{
				s.total=s.total+2*(s.perMonth);
				System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
				condition.signal();
			}
		}
		catch(InterruptedException e)
		{
			e.printStackTrace();
		}
		finally
		{
			if(locked)
			{
				lock.unlock();
			}
		}
			
		
		
		
	}
	public void getMoney(Student s)
	{
		boolean locked=false;
		try
		{
			locked=lock.tryLock(1, TimeUnit.SECONDS);
			while(s.total<=0)
			{
				try
				{
					condition.await();//使当前线程暂停,让出占用当前同步对象的锁,并进入等待状态
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			if(locked)
			{
				s.total=s.total-s.perMonth;
				System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");	
				condition.signal();//唤醒那些暂停的线程,使其进入等待列表,类似于synchronized下的notify()方法
			}
		}
		catch(InterruptedException e)
		{
			e.printStackTrace();
		}
		finally
		{
			if(locked)
			{
				lock.unlock();
			}
		}
		


	}

}

运行测试程序,结果如下:

Tom最开始拥有1000元
Tom取了200当前总共拥有800元
Tom存了400当前总共拥有1200元
Tom存了400当前总共拥有1600元
Tom取了200当前总共拥有1400元
Tom取了200当前总共拥有1200元
Tom取了200当前总共拥有1000元
Tom取了200当前总共拥有800元
Tom取了200当前总共拥有600元
Tom取了200当前总共拥有400元
Tom取了200当前总共拥有200元
Tom存了400当前总共拥有600元
Tom取了200当前总共拥有400元
Tom存了400当前总共拥有800元
Tom存了400当前总共拥有1200元
Tom取了200当前总共拥有1000元
Tom存了400当前总共拥有1400元
Tom存了400当前总共拥有1800元
Tom存了400当前总共拥有2200元
Tom存了400当前总共拥有2600元
Tom存了400当前总共拥有3000元
最后Tom总共拥有3000元
        根据以上两种方法对应的交互方式,可以看出区别不大,但是也有不同之处:lock()不需要捕获异常,而tryLock()需要捕获异常;lock()不需要判别标志,而tryLock()需要判别标志。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值