java中的JUC总结二——lock接口与线程通信

1、synchronized

        1.1、Synchronized关键字回顾

        synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

        (1)、修饰一个代码块。

        (2)、修饰方法。

          备注:虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了

        (3)、修改一个静态的方法

        (4)、修改一个类

列子:

package com.study.demo;

  class Data{
    private Integer number = 40; //商品数量
    
    public synchronized void rob(){
        if( number > 0) {
            
            System.out.println(Thread.currentThread().getName()+"---抢出:"+(number--)+"库存:----"+number);
        }
    }
}

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有
2)线程执行发生异常,此时JVM会让线程自动释放锁
那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。
因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。

2、Lock

        2.1、介绍

        Lock接口

        

public interface Lock { 
void lock(); //获取锁
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 
void unlock();
Condition newCondition(); //实现线程等待/通知模型
(在synchrozied 中使用wait()/notify()也可实现等待/通知模型)
}

        Lock锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。Lock提供了比synchronized更多的功能。

        2.2、synchronizd与lock的区别

        • Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;

        • Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

        •  Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

        • Lock可以提高多个线程进行读操作的效率。

        • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

        • 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。

       使用lock实现代码同步的列子:

package com.study.demo;

import java.util.concurrent.locks.ReentrantLock;

class Data2{
	private Integer number = 40; //商品数量
	/*
	 * public synchronized void rob(){ if( number > 0) {
	 * 
	 * System.out.println(Thread.currentThread().getName()+"---抢出:"+(number--)+
	 * "库存:----"+number); } }
	 */
	//使用lock实现方法同步
	private final ReentrantLock lock = new ReentrantLock();
	public void rob() {
		lock.lock();
		try {
			if( number > 0) {
				System.out.println(Thread.currentThread().getName()+"---抢出:"+(number--)+"库存:----"+number); 
			}
		}finally {
			lock.unlock();//放锁
		}
	}


}
public class LockDemo2 {

	public static void main(String[] args) {
		Data2 data = new Data2();

		new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 0 ; i <=40; i ++) {
					//System.out.println("BB线程运行次数:"+i);
					data.rob();
				}
			}
		},"BB").start();

		new Thread(new Runnable() {

			@Override
			public void run() {
				for(int i = 0 ; i <=40; i ++) {
					//System.out.println("AA线程运行次数:"+i);
					data.rob();
				}

			}
		},"AA").start();


	}

}


        2.2、线程间的通信

        线程间通信的模型有两种:共享内存和消息传递

        2.2.1、使用synchronized实现

        

package com.study.demo;
/**
 * 线程间的通信
 * 使用synchornizd实现线程之间的通信
 * 实现:
 * 使用多线程同时访问一个资源,一个线程判断资源对数据 为0是做+1,
 * 一个线程判断资源为1是-1,是资源在0与1之间交替即为:(0,1,0,1 .....)
 * @author marvin
 *
 */
class Data3{
	//资源类
	private int number = 0;
	public synchronized void incr() throws InterruptedException { //+1
		//判断(等待)
		while(number != 0) {//在是使用wait时,判断应该使用while,若使用if可能会造成虚假唤醒
			this.wait();//如果判断为if时,虚假唤醒,问题出现在此处,如果在不为0时,此处在等待,下次此线程如果抢占到时,会从此处,往下执行,不会参与判断	
		}				
		//操作
		System.out.println(Thread.currentThread().getName()+"--加数:"+(number++)+"当前数:----"+number); 
		//通知
		this.notifyAll();
	}
	public synchronized void encr() throws InterruptedException { //+1
		//判断(等待)
		while(number != 1) {
			this.wait();
		}
		//操作
		System.out.println(Thread.currentThread().getName()+"--减数:"+(number--)+"当前数:----"+number); 
		//通知
		this.notifyAll();

	}
}
public class LockDemo3 {

	public static void main(String[] args) {
		Data3 date3 = new Data3();
		new Thread(() ->{//执行+1,操作
			try {
				for(int i = 0; i <= 40; i ++) {
					date3.incr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"AA").start();
		new Thread(() ->{//执行-1,操作
			try {
				for(int i = 0; i <= 40; i ++) {
					date3.encr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"BB").start();
		
		new Thread(() ->{//执行+1,操作
			try {
				for(int i = 0; i <= 40; i ++) {
					date3.encr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"CC").start();
		new Thread(() ->{//执行-1,操作
			try {
				for(int i = 0; i <= 40; i ++) {
					date3.encr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"DD").start();
	}
}

2.2.2、使用Lock实现(ReentrantLock可重入锁)

        

package com.study.demo;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程间的通信
 * 使用Lock实现线程之间的通信
 * 实现:
 * 使用多线程同时访问一个资源,一个线程判断资源对数据 为0是做+1,
 * 一个线程判断资源为1是-1,是资源在0与1之间交替即为:(0,1,0,1 .....)
 * @author marvin
 *
 */
class Data4{
	//资源类
	private int number = 0;
	//是定义锁
	 private  Lock lock = new ReentrantLock(); 
	 private Condition condition = lock.newCondition();
	public  void incr() throws InterruptedException { //+1
		lock.lock();//获取锁
		try {
			//判断(等待)
			while(number != 0) {//在是使用wait时,判断应该使用while,若使用if可能会造成虚假唤醒
				condition.await();
				//this.wait();//如果判断为if时,虚假唤醒,问题出现在此处,如果在不为0时,此处在等待,下次此线程如果抢占到时,会从此处,往下执行,不会参与判断	
			}				
			//操作
			System.out.println(Thread.currentThread().getName()+"--加数:"+(number++)+"当前数:----"+number); 
			//通知
			condition.signalAll();
			//this.notifyAll();
		} finally {
			lock.unlock();//手动释放锁
		}
		
	}
	public  void encr() throws InterruptedException { //-1
		lock.lock();//获取锁
		try {
			//判断(等待)
			while(number != 1) {//在是使用wait时,判断应该使用while,若使用if可能会造成虚假唤醒
				condition.await();
				//this.wait();//如果判断为if时,虚假唤醒,问题出现在此处,如果在不为0时,此处在等待,下次此线程如果抢占到时,会从此处,往下执行,不会参与判断	
			}				
			//操作
			System.out.println(Thread.currentThread().getName()+"--减数:"+(number--)+"当前数:----"+number); 
			//通知
			condition.signalAll();
			//this.notifyAll();
		} finally {
			lock.unlock();//手动释放锁
		}

	}
}
public class LockDemo4 {

	public static void main(String[] args) {
		Data4 date4 = new Data4();
		new Thread(() ->{//执行+1,操作
			try {
				for(int i = 0; i <= 10; i ++) {
					date4.incr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"AA").start();
		new Thread(() ->{//执行-1,操作
			try {
				for(int i = 0; i <= 10; i ++) {
					date4.encr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"BB").start();
		
		new Thread(() ->{//执行+1,操作
			try {
				for(int i = 0; i <= 10; i ++) {
					date4.incr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"CC").start();
		new Thread(() ->{//执行-1,操作
			try {
				for(int i = 0; i <= 10; i ++) {
					date4.encr();
				}

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		},"DD").start();
	}
}

2.2.3、线程的定制化通信

        列子: A线程打印5次A,B线程打印10次B,C线程打印15次C,按照此顺序循环10轮

package com.study.demo;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程间的通信
 * 使用Lock实现线程之间的通信
 * 实现:
 * 使用多线程同时访问一个资源,一个线程判断资源对数据 为0是做+1,
 * 一个线程判断资源为1是-1,是资源在0与1之间交替即为:(0,1,0,1 .....)
 * @author marvin
 *
 */
class Data5{
	//资源类
	private int number = 0;
	//线程标志位,1:AA线程 2:BB线程 3:CC线程
	private int start = 1;
	//是定义锁
	 private  Lock lock = new ReentrantLock(); 
	 声明各个线程的钥匙
	 private Condition conditionAA = lock.newCondition();
	 private Condition conditionBB = lock.newCondition();
	 private Condition conditionCC = lock.newCondition();
	 
	 /**
	  * 
	  * @param i 标识轮数
	 * @throws InterruptedException 
	  */
	 public void DataAA(int i) throws InterruptedException {
		 //获取锁
		 lock.lock();
		 try {
			//判断
			 while(start != 1) {
				 conditionAA.await();//等待
			 }
			 //操作
			 for(int j = 1;j <= 5; j++ ) {
				 System.out.println(Thread.currentThread().getName()+"------打印次数:"+j+"-------第"+i+"轮");
			 }
			 start = 2;//修改标志位
			 conditionBB.signal();//通知BB线程
		} finally {
			lock.unlock();//释放锁
		}
	 }
	 
	 public void DataBB(int i) throws InterruptedException {
		 //获取锁
		 lock.lock();
		 try {
			//判断
			 while(start != 2) {
				 conditionBB.await();//等待
			 }
			 //操作
			 for(int j = 1;j <= 10; j++ ) {
				 System.out.println(Thread.currentThread().getName()+"------打印次数:"+j+"-------第"+i+"轮");
			 }
			 start = 3;//修改标志位
			 conditionCC.signal();//通知CC线程
		} finally {
			lock.unlock();//释放锁
		}
	 }
	 
	 public void DataCC(int i) throws InterruptedException {
		 //获取锁
		 lock.lock();
		 try {
			//判断
			 while(start != 3) {
				 conditionCC.await();//等待
			 }
			 //操作
			 for(int j = 1;j <= 15; j++ ) {
				 System.out.println(Thread.currentThread().getName()+"------打印次数:"+j+"-------第"+i+"轮");
			 }
			 start = 1;//修改标志位
			 conditionAA.signal();//通知AA线程
		} finally {
			lock.unlock();//释放锁
		}
	 }

}
public class LockDemo5 {

	public static void main(String[] args) {
		Data5 data5 = new Data5();
		//创建AA、BB、CC线程
		new Thread(()-> {
			for(int i = 1; i <= 10; i++) {
				try {
					data5.DataAA(i);
				} catch (InterruptedException e) {					
					e.printStackTrace();
				}
			}
		},"AA").start(); ;
		new Thread(()-> {
			for(int i = 1; i <= 10; i++) {
				try {
					data5.DataBB(i);
				} catch (InterruptedException e) {					
					e.printStackTrace();
				}
			}
		},"BB").start() ;
		new Thread(()-> {
			for(int i = 1; i <= 10; i++) {
				try {
					data5.DataCC(i);
				} catch (InterruptedException e) {					
					e.printStackTrace();
				}
			}
		},"CC").start() ;
		
	}
}

Jdk6在线中文文档

        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值