多线程中采用锁控制并发

本文探讨了多线程编程中共享变量控制的重要性,通过示例对比了synchronized关键字与重入锁在解决多线程资源竞争问题上的区别与优势。

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

在多线程编程当中对于共享变量的控制非常重要,平常的程序当中由于是单线程去处理的,因此不会出现变量资源同时被多个线程同时访问修改,程序的运行是顺序的。然而多线程的环境中就会出现资源同时被多个线程获取,同时去做修改的状况,可以看下面一段程序:

public class MyThread implements Runnable{

	private int i=0;
	
	public void run() {
		try{
			i++;
			System.out.println(Thread.currentThread().getId() +":i is " + i);
			Thread.sleep(1000);
			
		} catch(Exception e){
			System.out.println(e.getMessage());
		} 
	}
	
	public static void main(String[] args) {
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		MyThread thread = new MyThread ();
		for(int i=0; i<6; i++){
			executorService.submit(thread);
		}
	}

}

运行这段程序后显示的一个可能结果如下:


由线程池产生的ID为10,11,12的三个线程开始工作,在第一次的时候就同时拿到i并进行自增操作,最后同时打印输出,因此i的值显示均为3,后续三个线程分别是陆续拿到i并做修改,因此输出的是4,5,6。

如果想对共享资源变量i的访问采用顺序的方式进行,就需要加锁,这时大家首先想到的是用synchronized来进行锁的控制。

public synchronized void run() {
		try{
			i++;
			System.out.println(Thread.currentThread().getId() +":i is " + i);
			Thread.sleep(1000);
			
		} catch(Exception e){
			System.out.println(e.getMessage());
		} 
	}

加锁后执行结果:

从结果中可以看出 i 是顺序加1

还有一种方式就是采用重入锁

public class ThreadWithReentrantLock implements Runnable{

	public static ReentrantLock lock = new ReentrantLock();
	
	private int i=0;
	
	public void run() {
		try{
			lock.lock();
			i++;
			System.out.println(Thread.currentThread().getId() +":i is " + i);
			Thread.sleep(1000);
			
		} catch(Exception e){
			System.out.println(e.getMessage());
		} finally {
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		ThreadWithReentrantLock thread = new ThreadWithReentrantLock();
		for(int i=0; i<6; i++){
			executorService.submit(thread);
		}
	}

}

运行结果:

无论是采用关键字Sychronized还是采用重入锁都不会造成死锁,Sychronized会有默认的计数器实现锁的释放,而重入锁在finaiily代码块都会将锁释放。重入锁有什么好处呢,请看下面的示例。

public class ThreadWithReentrantLock implements Runnable {

    public static ReentrantLock lock = new ReentrantLock();  
    
    private int i=0;  
      
    public void run() {
        System.out.println(Thread.currentThread().getId()+":begin..."); 
        try{
            if(lock.tryLock())
            {
                System.out.println(Thread.currentThread().getId() + ":get lock successfully...");
            } else {
                System.out.println(Thread.currentThread().getId() + ":get lock failed...");
                lock.lockInterruptibly();
            }
            
            lock.lock();  
            i++;  
            System.out.println(Thread.currentThread().getId() +":i is " + i);  
            Thread.sleep(3000);  
              
        } catch(Exception e){  
            System.out.println(e.getMessage());  
        } finally {  
            lock.unlock();  
        }  
    }  
      
    public static void main(String[] args) {  
        ExecutorService executorService = Executors.newFixedThreadPool(3);  
        ThreadWithReentrantLock  thread = new ThreadWithReentrantLock ();  
        for(int i=0; i<6; i++){
            executorService.submit(thread); 
        }  
    }  

}

运行结果:

可以看到线程11一直占据着锁,而12和10在尝试获取锁的时候由于是在并发状况下,线程11持有锁,因此对他们采取了中断,让他们退出锁的竞争,保证了程序的继续进行。

从中可以得出重入锁比Sychronized好的地方是能发现锁是否被占有,并且能进行中断控制。从中可以看到,Sychronized虽然是JDK提供的同步关键字,但它同样实现了锁的功

能,本质Sychronized是一种内置锁,是由JVM对于并发对象设置了内置锁,保证了并发,然而内置锁的弊病就是看不见摸不着,无法对其进行控制。开发中还是建议采用可重

入锁,能够显示的控制。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值