JUC中的Lock和读写锁的介绍

1.实现线程安全的是那种方法:

用于解决多线程的安全的三种方法:同步方法、同步代码块、同步锁

其中同步方法和同步代码块用的是synchronized修饰的,它是属于隐式锁。

在 JDK 1.5之后出现同步锁Lock(显示锁)

注意:需要通过lock()的方法进行上锁,必须通过unlock()方法释放锁。

  • 在Java 5.0之前,协调共享对象的访问时,可以使用的机制只有synchronized和volatile。Java5.0后增加了一些新的机制,但并不是一种代替内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。
  • ReentrantLock实现了Lock接口,提供了与synchronized提供了更高的处理锁的灵活性。
public class TestLock {
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		new Thread(ticket,"1号").start();
		new Thread(ticket,"2号").start();
		new Thread(ticket,"3号").start();
	}
}
class Ticket implements Runnable{
	private int tick = 100;
	private Lock lock= new ReentrantLock();//多态的提现
	@Override
	public void run() {
		while (true) {
			lock.lock();//此处为上锁
			try {
				if (tick > 0) {	
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
					}
					System.out.println(Thread.currentThread().getName()+"完成售票,余票为:"+ --tick);			
				}
			} finally {
				lock.unlock();//释放锁
			}
		}
	}
	
}

2.生产者和消费者案例:

1.最基本的例子:

public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk1 clerk1 = new Clerk1();
        Productor productor = new Productor(clerk1);
        Consumer consumer = new Consumer(clerk1);
        new Thread(productor,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }
}
//店员
class Clerk1{
    private int product=0;//货物

    //进货
    public synchronized void get(){
     if(product >=10){
         //如果产品满了之后,该线程需要进行等待
         System.out.println("产品已满");
         try {
             this.wait();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }  else{
         //如果存在库存的话,将会唤醒所有的线程
         System.out.println(Thread.currentThread().getName()+" : "+ ++product);
         this.notifyAll();
     }
    }
    //卖货
    public synchronized void set(){
        if (product <=0){
            //如果没有货物的话,当前线程需要进行等待
            System.out.println("缺货,需要进货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            System.out.println(Thread.currentThread().getName()+" : "+ --product);
            this.notifyAll();
        }
    }
}

//生产者
class Productor implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
             try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk1.get();
        }
    }

    public Productor(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}
//消费者
class Consumer implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            clerk1.set();
        }
    }

    public Consumer(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}

注意:上述的方法是存在一个问题的,就是程序会在一直的运行中,因为有个else判断。

案例2:在1的基础上进行修改。

public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk1 clerk1 = new Clerk1();
        Productor productor = new Productor(clerk1);
        Consumer consumer = new Consumer(clerk1);
        new Thread(productor,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }
}
//店员
class Clerk1{
    private int product=0;//货物

    //进货
    public synchronized void get(){
     if(product >=10){
         //如果产品满了之后,该线程需要进行等待
         System.out.println("产品已满");
         try {
             this.wait();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
     //如果存在库存的话,将会唤醒所有的线程
     System.out.println(Thread.currentThread().getName()+" : "+ ++product);
     this.notifyAll();
    }
    //卖货
    public synchronized void set(){
        if (product <=0){
            //如果没有货物的话,当前线程需要进行等待
            System.out.println("缺货,需要进货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+" : "+ --product);
        this.notifyAll();
    }
}

//生产者
class Productor implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk1.get();
        }
    }

    public Productor(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}
//消费者
class Consumer implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            clerk1.set();
        }
    }

    public Consumer(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}

注意:根据问题1,进行了改进,就是把else删除,这样的话就会让线程结束。从而让真个程序结束。

案例3:在案例2的基础上,进行了整改:

public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk1 clerk1 = new Clerk1();
        Productor productor = new Productor(clerk1);
        Consumer consumer = new Consumer(clerk1);
        new Thread(productor,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }
}
//店员
class Clerk1{
    private int product=0;//货物

    //进货
    public synchronized void get(){
     while (product >=10){
         //如果产品满了之后,该线程需要进行等待
         System.out.println("产品已满");
         try {
             this.wait();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
     //如果存在库存的话,将会唤醒所有的线程
     System.out.println(Thread.currentThread().getName()+" : "+ ++product);
     this.notifyAll();
    }
    //卖货
    public synchronized void set(){
        while (product <=0){
            //如果没有货物的话,当前线程需要进行等待
            System.out.println("缺货,需要进货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+" : "+ --product);
        this.notifyAll();
    }
}

//生产者
class Productor implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk1.get();
        }
    }

    public Productor(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}
//消费者
class Consumer implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            clerk1.set();
        }
    }

    public Consumer(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}

问题:如果出现两个消费者和生产者的话,在线程等待或者唤醒,会把所有的都唤醒,不能唤醒指定的线程,所以这样也是存在问题的。

案例4:使用lock同步锁完成(Condition控制线程通信)

public class TestProductorAndConsumer {
    public static void main(String[] args) {
        Clerk1 clerk1 = new Clerk1();
        Productor productor = new Productor(clerk1);
        Consumer consumer = new Consumer(clerk1);
        new Thread(productor,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }
}
//店员
class Clerk1{
    private int product=0;//货物
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    //进货
    public void get(){
        //上锁
        lock.lock();
        try {
            while (product >=10){
                //如果产品满了之后,该线程需要进行等待
                System.out.println("产品已满");
                try {
                    condition.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果存在库存的话,将会唤醒所有的线程
            System.out.println(Thread.currentThread().getName()+" : "+ ++product);
            condition.signalAll();//唤醒所有的线程
        }finally {
            //释放资源
            lock.unlock();
        }
    }
    //卖货
    public void set(){
        lock.lock();

        try {
            while (product <=0){
                //如果没有货物的话,当前线程需要进行等待
                System.out.println("缺货,需要进货");
                try {
                   condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+" : "+ --product);
           condition.signalAll();
        } finally {
            lock.unlock();
        }

    }
}

//生产者
class Productor implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk1.get();
        }
    }

    public Productor(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}
//消费者
class Consumer implements Runnable{
    Clerk1 clerk1 = new Clerk1();
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            clerk1.set();
        }
    }

    public Consumer(Clerk1 clerk1) {
        this.clerk1 = clerk1;
    }
}
  • Condition接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用Object.wait访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个Lock可能与多个Condition对象关联。为了便面兼容性问题,Condition方法的名称与对应的Object版本中的不同。
  • 在Condition对象中,与wait、notify和notifyAll方法对应的分别是await\signal \signalAll.
  • Condition实例实质上被绑定到一个锁上。要为特定的Lock实例获取Condition实例,使用的是newCondition()方法。

3.线程按序交替:

问题:编写一个程序,开启3个线程,这三个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要输出的要求必须按顺序显示。

public class TestABCAlternate {
    public static void main(String[] args) {
        AlternateDemo alternateDemo = new AlternateDemo();
        //执行A
        new Thread(new Runnable() {//使用的是内部类的形式
            @Override
            public void run() {
                //打印10遍
                for (int i = 0; i < 10; i++) {
                    alternateDemo.loopA(i);
                }
            }
        },"A").start();
        //执行B
        new Thread(new Runnable() {//使用的是内部类的形式
            @Override
            public void run() {
                //打印10遍
                for (int i = 0; i < 10; i++) {
                    alternateDemo.loopB(i);
                }
            }
        },"B").start();

        //执行C
        new Thread(new Runnable() {//使用的是内部类的形式
            @Override
            public void run() {
                //打印10遍
                for (int i = 0; i < 10; i++) {
                    alternateDemo.loopC(i);
                    System.out.println("-------------------");
                }
            }
        },"C").start();
    }
}

class AlternateDemo{
    private int number=1;//用来记录正在执行的线程的标记
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    //A
    public void loopA(int totalLoop){
        //上锁
        lock.lock();
        try {
            //1.判断值
            if (number != 1){
                //如果number的值不为1的话,线程就要进入等待状态
                condition1.await();
            }
            //2.打印
            for (int i = 1 ; i <=1 ; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
            }
            //3.唤醒
            number =2;
            condition2.signal();//唤醒指定的线程
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    //A
    public void loopB(int totalLoop){
        //上锁
        lock.lock();
        try {
            //1.判断值
            if (number != 2){
                //如果number的值不为1的话,线程就要进入等待状态
                condition2.await();
            }
            //2.打印
            for (int i = 1 ; i <=1 ; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
            }
            //3.唤醒
            number =3;
            condition3.signal();//唤醒指定的线程
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    //A
    public void loopC(int totalLoop){
        //上锁
        lock.lock();
        try {
            //1.判断值
            if (number != 3){
                //如果number的值不为1的话,线程就要进入等待状态
                condition3.await();
            }
            //2.打印
            for (int i = 1 ; i <=1 ; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
            }
            //3.唤醒
            number =1;
            condition1.signal();//唤醒指定的线程
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

主要是通过线程通信的原理,来唤醒或者等待指定的线程,这样就可以达到按照顺序输出。

4.读写锁(ReadWriteLock)

  • ReadWriteLock维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有writer,读取锁可以由多个reader线程同时保持。写入锁是独占的。
  • ReadWriteLock读取操作通常不会改变共享资源,但是执行写入操作时,必须独占方式来获取锁。对于读取操作占多数的数据结构。ReadWriteLock能提供比独占锁更高的并发性。而对于只读的数据结构,其中包含的不变形可以完全不需要考虑加锁操作。
/*
 * 1.ReadWriteLock:读写锁
 * 
 * 写写/读写 需要“互斥”
 * 读读 不需要互斥
 */
public class TestReadWriteLock {
	public static void main(String[] args) {
		ReadWriteLockDemo tw = new ReadWriteLockDemo();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				tw.set((int)(Math.random()*101));
				
			}
		},"写").start();
		
		for (int i = 0; i < 100; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					tw.get();
					
				}
			},"读").start();
		}
	}
}
class ReadWriteLockDemo{
	private int number = 0;
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	//读
	public void get() {
		lock.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName() + " : " + number);
		} finally {
			lock.readLock().unlock();
		}
	}
	
	//写
	public void set(int number) {
		lock.writeLock().lock();
		try {
			System.out.println(Thread.currentThread().getName());
			this.number = number;
		} finally {
			lock.writeLock().unlock();
		}
	}
}

5.八种锁打印出的不同值

/* 
 * 1.两个普通同步方法,两个线程,标准打印//one two 
 * 2.新增Thread.sleep()给getOne(),打印 //one two
 * 3.新增普通方法getThree(),打印://three one two
 * 4.两个普通同步方法,两个Number对象,打印://two one
 * 5.修改getOne()为静态同步方法,打印://two one
 * 6.修改这两个方法均为静态同步方法,同一个Number对象,打印://one two
 * 7.一个静态同步方法,一个非静态同步方法,两个Number 打印:one two
 * 8.两个静态同步方法,两个对象打印:one two
 * 
 * 线程八锁的关键:
 * 1.非静态方法的默认为this,静态方法的锁为对应的Class实例
 * 2.某一个时刻内,只有一个线程持有锁,无论几个方法。
 */
public class TestThread8Monitor {
	public static void main(String[] args) {
		Number number = new Number();
		Number number1 = new Number();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				number.getOne();
			}
		}).start();

		
		new Thread(new Runnable() {

			@Override
			public void run() {
				//number.getTwo();
				number1.getTwo();
			}
		}).start();
		
//		new Thread(new Runnable() {
//
//			@Override
//			public void run() {
//				number.getThree();
//			}
//		}).start();
	}
}

class Number {

	public static synchronized void getOne() {
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("one");
	}

	public static synchronized void getTwo() {
		System.out.println("two");
	}


	 public void getThree() { 
		 System.out.println("three"); 
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值