并发编程(二)【ReentrantLock】

筑基初期(lock等待锁)

  • concurrent是jdk1.5后的包,避免synchronized的出现而设计出来的一种锁机制。
  • ReentrantLock 重入锁,在一个对象上加一个标记信息,这个标记信息代表锁机制。
public class Test_01 {
	Lock lock = new ReentrantLock();

	void m1() {
		try {
			lock.lock(); // 加锁
			for (int i = 0; i < 10; i++) {
				TimeUnit.SECONDS.sleep(1);
				System.out.println("m1() method " + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock(); // 解锁
		}
	}

	void m2() {
		lock.lock();
		System.out.println("m2() method");
		lock.unlock();
	}

	public static void main(String[] args) {
		final Test_01 t = new Test_01();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m1();
			}
		}).start();
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m2();
			}
		}).start();
	}
}

筑基中期(tryLock尝试锁)

尝试锁有阻塞和非阻塞两种

public class Test_02 {
	Lock lock = new ReentrantLock();
	
	void m1(){
		try{
			lock.lock();
			for(int i = 0; i < 10; i++){
				TimeUnit.SECONDS.sleep(1);
				System.out.println("m1() method " + i);
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	
	void m2(){
		boolean isLocked = false;
		try{
			// 尝试锁, 如果有锁,无法获取锁标记,返回false。
			// 非阻塞,如果获取锁标记,返回true
			// isLocked = lock.tryLock();
			
			// 阻塞尝试锁,阻塞参数代表的时长,尝试获取锁标记。
			// 如果超时,不等待。直接返回。
			isLocked = lock.tryLock(5, TimeUnit.SECONDS); 
			
			if(isLocked){
				System.out.println("m2() method synchronized");
			}else{
				System.out.println("m2() method unsynchronized");
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(isLocked){
				// 尝试锁在解除锁标记的时候,一定要判断是否获取到锁标记。
				// 如果当前线程没有获取到锁标记,会抛出异常。
				lock.unlock();
			}
		}
	}
	
	public static void main(String[] args) {
		final Test_02 t = new Test_02();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m1();
			}
		}).start();
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m2();
			}
		}).start();
	}
}

筑基后期(lockInterruptibly可打断锁)

  • 阻塞状态有3种: 包括普通阻塞(不释放锁),等待队列(释放锁),锁池队列。
    • 普通阻塞: sleep(10000), 可以被打断。调用thread.interrupt()方法,可以打断阻塞状态,抛出异常。
    • 等待队列: wait()方法被调用,也是一种阻塞状态,只能由notify唤醒。无法打断。
    • 锁池队列: 执行过程中,遇到同步代码,无法获取锁标记。不是所有的锁池队列都可被打断。
      • 使用ReentrantLock的lock方法,获取锁标记的时候,如果需要阻塞等待锁标记,无法被打断。
      • 使用ReentrantLock的lockInterruptibly方法,获取锁标记的时候,如果需要阻塞等待,可以被打断。
  • 可打断锁意义:软件锁死了,无响应,去去任务管理器结束任务
public class Test_03 {
	Lock lock = new ReentrantLock();

	void m1() {
		try {
			lock.lock();
			for (int i = 0; i < 5; i++) {
				TimeUnit.SECONDS.sleep(1);
				System.out.println("m1() method " + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	void m2() {
		try {
			// 线程执行到这里,本来是不能获得锁标记的,要进入等待队列的。
			// 当通过调用当前线程的interrupt(),通过打断当前线程,抛出异常,使线程被唤醒,阻塞结束
			lock.lockInterruptibly(); // 可尝试打断的,阻塞等待锁。可以被其他的线程打断阻塞状态
			System.out.println("m2() method");
		} catch (InterruptedException e) {
			// 被打断的异常,被打断与唤醒、阻塞结束都是不一样的
			// sleep任何一个线程都可以把他打断,强行唤醒
			// 如果是lock不可被打断的
			// 如果是lockInterruptibly,阻塞等待这把锁,类似sleep,可以通过interrupt()打断
			System.out.println("m2() method interrupted");
		} finally {
			try {
				lock.unlock();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		final Test_03 t = new Test_03();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m1();
			}
		}).start();
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				t.m2();
			}
		});
		t2.start();
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 不调用interrupt()方法,t2最后可以获得锁,继续执行
		t2.interrupt();// 打断t2线程,锁的位置会抛出异常。
	}
}

筑基圆满(公平锁)

  • 在cpu和os中本身线程竞争锁标记是不公平的,不考虑线程的等待时间的。
  • 运用在轮询的场景,如打牌。
  • 需要效果一部分的cpu资源计算等待的时间,性能有所降低。要仅能少用,并发量在10之内。
public class Test_04 {

    public static void main(String[] args) {
        TestReentrantlock t = new TestReentrantlock();
        //TestSync t = new TestSync();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        t2.start();
    }
}

class TestReentrantlock extends Thread {
    // 定义一个公平锁
    private static ReentrantLock lock = new ReentrantLock(true);

    public void run() {
        for (int i = 0; i < 5; i++) {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " get lock");
            } finally {
                lock.unlock();
            }
        }
    }

}

class TestSync extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            //不公平的
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " get lock in TestSync");
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值