java —— 线程(二)

十一、隐式锁

隐式锁是互斥锁的一种,应用于不同线程面对同一个任务的情况,可以通过关键字 synchronized 对代码块或方法进行锁定,被锁定的代码块或方法在同一时刻只能被一个线程调用,其他线程只能等待上一个线程执行完毕解锁之后才能调用。

(一)synchronized 锁定代码块

public class Task extends Thread{
    static Integer i=5;   //加上static,是因为main方法里实例化了两个Task对象,如果不加static,就等于task1和task2各自处理各自的i,这样锁就失去了意义。
    public void run()
    {        
        while(i>0)
        {
            synchronized(i)   //括号内的参数为对象,所以如果是基本类型要转为包装类。
            {
                System.out.println("线程:"+Thread.currentThread()+"输出 "+i);
                i--;
            }
            //在释放锁之后还让线程休眠100毫秒,是为了让该线程继续占用时间,而其他线程有足够的时间抢到锁。
            try {Thread.sleep(100);}
            catch (InterruptedException e) {e.printStackTrace();}
        }
    }

    public static void main(String[] args)
    {
        Task task1=new Task();
        Task task2=new Task();
        task1.start();
        task2.start();
    }
}

 本案例中,由于 Task 继承的是 Thread 类,导致 Task 的实例化对象既是线程,又是一个具体的实例化对象,因而锁定对象前面要添加 static 关键字。如果 Task 实现的是 Runnable 或 Callable 接口,那么 Task 的实例化对象就只是一项任务,可以将该任务添加至不同的线程,因而不需要用 static 关键字修饰,这也是我们多数情况下不偏向于继承 Thread 类的原因。

(二)synchronized 锁定方法

public class Task implements Runnable{
    public synchronized void run()   //锁定方法
    {
        for(int i=0;i<=3;i++)
        {
            System.out.println("线程为:"+Thread.currentThread().getName()+"输出"+i);
        }
    }

    public static void main(String[] args)
    {
        Task task=new Task();
        Thread th1=new Thread(task);
        Thread th2=new Thread(task);
        th1.start();
        th2.start();
    }
}

十二、隐式锁的等待与唤醒

.wait()  强制当前线程等待,直到其他线程在同一个对象上调用 .notify() 方法唤醒该线程。如果要唤醒多个等待的线程,则使用 .notifyAll() 方法。

案例(生产者消费者模式):

public class Factory {
	int num=1;//库存
	
	//生产
	public synchronized void addProduct() throws InterruptedException {
		if(num>=10) {
			System.out.println("仓库已满,不能再生产了!");
			this.wait();//强制当前线程等待,直到其他线程在同一个对象调用notify()方法后释放
		}else {
			Thread.sleep(1000);
			num++;//生产出商品
			System.out.println("生产者生产了一个商品,现在库存为:"+num);
			this.notify();//唤醒消费商品的线程
		}
	}
	
	//消费
	public synchronized void subProduct() throws InterruptedException {
		if(num<=0) {
			System.out.println("库存没了,不能再消费!");
			this.wait();//强制当前线程等待
		}else {
			Thread.sleep(1000);
			num--;//消费商品
			System.out.println("消费者消费了一个商品,现在库存为:"+num);
			this.notify();//唤醒生产商品的线程
		}
	}
}
public class Productor implements Runnable{
	
	Factory factory;
	public Productor(Factory factory) {
		this.factory = factory;
	}


	@Override
	public void run() {
		//生产者不停生产商品
		while(true) {
			try {
				this.factory.addProduct();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}

}
public class Customer implements Runnable{
	
	Factory factory;
	public Customer(Factory factory) {
		this.factory = factory;
	}

	@Override
	public void run() {
		//消费者不停消费商品
		while(true) {
			try {
				this.factory.subProduct();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}

}
public class Test {

	public static void main(String[] args) {
		
		Factory factory=new Factory();
		Productor p=new Productor(factory);//生产者线程对象
		Customer c=new Customer(factory);  //消费者线程对象
		
		new Thread(p).start();
		new Thread(c).start();
	}
}

 十三、显式锁

显式锁(Lock)与隐式锁(synchronized)作用相同,但比隐式锁更为灵活。隐式锁是用关键字 synchronized 锁定代码块或方法,显式锁则是将需要锁定的内容直接编写在 .lock() 与 .unlock() 之间即可。比较以下区别:

(一).lock 与 .unlock 方法

① 隐式锁

public class Task implements Runnable{
	public void run()
	{
		synchronized(this)   //代码写在synchronized大括号内
		{
		try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
		System.out.println(Thread.currentThread().getName());
		}
	}
	public static void main(String[] args) 
	{
		Task task=new Task();
		Thread thread1=new Thread(task);
		Thread thread2=new Thread(task);
		thread1.start();
		thread2.start();
	}
}

② 显式锁

public class Task implements Runnable{
	//实例化一个Lock对象,ReentrantLock是Lock的一个子类,称为可重入锁
	private Lock lock=new ReentrantLock();  
	public void run()
	{
		//锁定的代码写在.lock()与.unlock()之间
		lock.lock();  
		try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
		System.out.println(Thread.currentThread().getName());
		lock.unlock();		
	}
	public static void main(String[] args) 
	{
		Task task=new Task();
		Thread thread1=new Thread(task);
		Thread thread2=new Thread(task);
		thread1.start();
		thread2.start();
	}
}

本案例中,锁定的代码为:休眠 1 秒钟之后,输出当前线程的名称。从输出结果来看,一个线程调用锁定的代码块结束之后,另一个线程才开始调用,证明显式锁与隐式锁具有相同效果。 

(二).tryLock 与 .unlock 方法

在前面的内容中,我们所学的锁均是阻塞式获取锁,也就是一个线程拿到锁之后,别的线程只能默默等待。使用 Lock 的 .tryLock() 方法可以将 run() 方法里的代码块分成两部分,一部分加锁,一部分不加锁。当一个线程抢到锁之后,另一个线程不必等待,而是执行不加锁的内容。

public class Task implements Runnable{
	private Lock lock=new ReentrantLock();
	public void run()
	{
		if(lock.tryLock())              //开始锁定
		{
			try {System.out.println(Thread.currentThread().getName()+"正在执行锁定代码");}
			finally{lock.unlock();}     //解除锁定
		}
		else {System.out.println("喂:"+Thread.currentThread().getName()+",代码锁着,你先执行我这段任务吧");}
	}
	public static void main(String[] args) 
	{
		Task task=new Task();
		Thread thread1=new Thread(task);
		Thread thread2=new Thread(task);
		thread1.start();
		thread2.start();
	}
}

 在本案例中,if 语句里面编写锁定的代码块,else 语句里面编写不锁定的代码块;当一个线程拿到 .tryLock() 锁之后,别的线程不等锁释放,而是转头去执行 else 语句的内容。if 语句里面用 try……finally…… 语句将锁定执行的代码块放在 try 里面,将解锁语句放在 finally 里面。

十四、显式锁的等待与唤醒

隐式锁使用的是 .wait() .notify() 或 .notifyAll() 方法,显式锁对应的是 condition.await() condition.signal() 或 condition.signalAll() 方法。

public class TaskSum{

	Lock lock;
	Condition condition;
		
	public TaskSum(Lock lock, Condition condition) {
		this.lock = lock;
		this.condition = condition;
	}

	public void method1() throws InterruptedException
	{
		lock.lock();		
		condition.await();
		System.out.println(Thread.currentThread().getName()+" AAA");
		lock.unlock();
	}
	
	public void method2() throws InterruptedException
	{
		lock.lock();		
		System.out.println(Thread.currentThread().getName()+" BBB");
		Thread.sleep(2000);
		condition.signal();		
		lock.unlock();
	}
}
public class Task1 implements Runnable{

	TaskSum tasksum;
	
	public Task1(TaskSum tasksum) {
		this.tasksum = tasksum;
	}

	@Override
	public void run() {
		try {
			tasksum.method1();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}
}
public class Task2 implements Runnable{

	TaskSum tasksum;
	
	public Task2(TaskSum tasksum) {
		this.tasksum = tasksum;
	}

	@Override
	public void run() {
		try {
			tasksum.method2();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}
}
public class Test {

	public static void main(String[] args) {
		Lock lock=new ReentrantLock();
		Condition condition=lock.newCondition();
		TaskSum tasksum=new TaskSum(lock,condition);
		Task1 task1=new Task1(tasksum);
		Task2 task2=new Task2(tasksum);
		Thread th1=new Thread(task1);
		Thread th2=new Thread(task2);
		th1.start();
		th2.start();
	}
}

 

  • 25
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值