synchronized和volatile关键字

一、Demo1

1、demo1

package com.hskw.demo1;

/**
 * synchronized关键字
 * synchronized关键字锁定的是对象不是代码块,demo中锁的是object对象的实例
 * 锁定的对象有两种:1.类的实例 2.类对象(类锁)
 * 加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一。
 */
public class Demo1 {

    private int count = 10;
    private Object object = new Object();

    public void test(){
        synchronized (object){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }

}

 2、demo2

package com.hskw.demo1;

public class Demo2 {

    private int count = 10;

    public void test(){
        //synchronized(this)锁定的是当前类的实例,这里锁定的是Demo2类的实例
        synchronized (this){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }

}

3、demo3

package com.hskw.demo1;

public class Demo3 {

    private int count = 10;

    //直接加在方法声明上,相当于是synchronized(this)
    public synchronized void test(){
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

}

4、demo4

package com.hskw.demo1;

public class Demo4 {

    private static int count = 10;

    //synchronize关键字修饰静态方法锁定的是类对象
    //静态方法中synchronize锁定代码块,锁定的对象不能是类的实例,只能是类的.class文件。
    public synchronized static void test(){
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

    public static void test2(){
        synchronized (Demo4.class){//这里不能替换成this
            count--;
        }
    }

}

二、Demo2

1、demo1

package com.hskw.demo2;

import java.util.concurrent.TimeUnit;

/**
 * 锁对象的改变
 * 锁定某对象o,如果o的属性发生改变,不影响锁的使用
 * 但是如果o变成另外一个对象,则锁定的对象发生改变
 * 应该避免将锁定对象的引用变成另外一个对象
 */
public class Demo1 {

    Object o = new Object();

    public void test(){
        synchronized (o) {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }
        }
    }

    public static void main(String[] args) {
        Demo1 demo = new Demo1();

        new Thread(demo :: test, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread t2 = new Thread(demo :: test, "t2");

        demo.o = new Object();
        //t2能否执行?
        t2.start();
    }

}

2、demo2

package com.hskw.demo2;

import java.util.concurrent.TimeUnit;

/**
 * 不要以字符串常量作为锁定的对象
 * 在下面,test1和test2其实锁定的是同一个对象
 */
public class Demo2 {

    String s1 = "hello";
    String s2 = "hello";

    public void test1(){
        synchronized (s1) {
            System.out.println("t1 start...");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1 end...");
        }
    }

    public void test2(){
        synchronized (s2) {
            System.out.println("t2 start...");
        }
    }

    public static void main(String[] args) {
        Demo2 demo = new Demo2();
        new Thread(demo :: test1,"test1").start();
        new Thread(demo :: test2,"test2").start();
    }

}

3、demo3

package com.hskw.demo2;

import java.util.concurrent.TimeUnit;

/**
 * 同步代码快中的语句越少越好
 * 比较test1和test2
 * 业务逻辑中只有count++这句需要sync,这时不应该给整个方法上锁
 * 采用细粒度的锁,可以使线程争用时间变短,从而提高效率
 */
public class Demo3 {

    int count = 0;

    public synchronized void test1(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        count ++;

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void test2(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized (this) {
            count ++;
        }

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

三、Demo3

1、demo1

package com.hskw.demo3;

/**
 * 问题就在于线程重入的问题,
 * 第一个线程减了个1变成9了,还没打印,第二个线程又减了个1,第三个线程又减了个1,
 * 这时候虽然第一个线程只减了一个1但是却打印出来一个7(这里情况是不一定的)
 * 可以给方法加上synchronized
 */
public class Demo1 implements Runnable{

    private int count = 10;

    @Override
    public /*synchronized*/ void run() {
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }


    public static void main(String[] args) {
        Demo1 demo = new Demo1();
        for (int i = 0; i < 5; i++) {
            new Thread(demo,"THREAD" + i).start();
        }
    }

}

2、demo2

package com.hskw.demo3;

public class Demo2 implements Runnable{

    private int count = 10;

    @Override
    public synchronized void run() {
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            //相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自的锁标记,可以同时执行。
            Demo2 demo = new Demo2();
            new Thread(demo,"THREAD" + i).start();
        }
    }

}

四、Demo4

package com.hskw.demo4;

//同步方法和非同步方法是否可以同时调用?可以
public class Demo{

    public synchronized void test1(){
        System.out.println(Thread.currentThread().getName() + " test1 start...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " test1 end...");
    }

    public void test2(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " test2");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(demo :: test1,"test1").start();
        new Thread(demo :: test2,"test2").start();
    }

}

五、Demo5

package com.hskw.demo5;

import java.util.concurrent.TimeUnit;

/**
 * 脏读问题
 * 实际业务当中应该看是否允许脏读,不允许的情况下对读方法也要加锁
 */
public class Demo {

    String name;
    double balance;

    public synchronized void set(String name,double balance){
        this.name = name;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.balance = balance;
    }

    public /*synchronized*/ double getBalance(String name){
        return this.balance;
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(()->demo.set("huaan",100.0)).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(demo.getBalance("huaan"));//

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(demo.getBalance("huaan"));
    }

}

六、Demo6

package com.hskw.demo6;

import java.util.concurrent.TimeUnit;

//一个同步方法调用另外一个同步方法,能否得到锁?可以,synchronized本身可支持重入
public class Demo {

    synchronized void test1(){
        System.out.println("test1 start.........");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        test2();
    }

    synchronized void test2(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test2 start.......");
    }

    public static void main(String[] args) {
        Demo demo= new Demo();
        demo.test1();
    }

}

七、Demo7

package com.hskw.demo7;

import java.util.concurrent.TimeUnit;

//这里是重入锁的另外一种情况,继承
public class Demo {

    synchronized void test(){
        System.out.println("demo test start........");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("demo test end........");
    }

    public static void main(String[] args) {
            new Demo2().test();
    }

}

class Demo2 extends Demo {

    @Override
    synchronized void test(){
        System.out.println("demo2 test start........");
        super.test();
        System.out.println("demo2 test end........");
    }

}

八、Demo8

package com.hskw.demo8;

import java.util.concurrent.TimeUnit;

/**
 * T2线程能否执行?
 */
public class Demo {

    int count = 0;

    synchronized void test(){
        System.out.println(Thread.currentThread().getName() + " start......");
        while (true) {
            count ++;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 5) {
                //碰到异常的情况,如果没有处理,会自动释放锁,所以T2可以执行。
                int i = 1/0;
            }
        }
    }

    public static void main(String[] args) {
        Demo demo11 = new Demo();

        Runnable r = () -> demo11.test();

        new Thread(r, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(r, "t2").start();
    }

}

九、Demo9

package com.hskw.demo9;

/**
 * volatile 关键字,使一个变量在多个线程间可见
 * mian,t1线程都用到一个变量,java默认是T1线程中保留一份副本,这样如果main线程修改了该变量,
 * t1线程未必知道
 *
 * 使用volatile关键字,会让所有线程都会读到变量的修改值
 *
 * 在下面的代码中,running是存在于堆内存的t对象中
 * 当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个副本,
 * 并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
 *
 * 关于这个例子  在后面会专门花时间再讲
 */
public class Demo {

    boolean running = true;

    public void test(){
        System.out.println("test start...");
        while (running){

        }
        System.out.println("test end...");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();

        new Thread(demo :: test,"t1").start();

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        demo.running = false;
    }

}

十、Demo10

package com.hskw.demo10;

import java.util.ArrayList;
import java.util.List;

/**
 * 比如说第一个线程加到100了,还没往上加,另外一个线程来了,把100拿过来执行方法,
 * 然后第一个线程继续加到101,第二个线程也加到101,他两往回写都是101,线程不会管你加到哪儿了,
 * 虽然说加了2但是实际上只加了1.
 * volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,
 * 也就是说volatile不能替代synchronized或者说volatile保证不了原子性
 */
public class Demo {

    volatile int count = 0;

    public void test(){
        for (int i = 0; i < 10000; i++) {
            count ++;
        }
    }


    public static void main(String[] args) {
        Demo demo = new Demo();

        List<Thread> threads = new ArrayList();

        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(demo::test, "thread-" + i));
        }

        threads.forEach((o)->o.start());

        threads.forEach((o)->{
            try {
                o.join();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        System.out.println(demo.count);
    }

}

十一、Demo11

package com.hskw.demo11;

import java.util.ArrayList;
import java.util.List;

public class Demo {

    int count = 0;

    //相比较上一个例子,synchronized既保证了原子性又保证了可见性
    public synchronized void test(){
        for (int i = 0; i < 10000; i++) {
            count ++;
        }
    }

    public static void main(String[] args) {
        Demo demo = new Demo();

        List<Thread> threads = new ArrayList<Thread>();

        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(demo::test, "thread-" + i));
        }

        threads.forEach((o)->o.start());

        threads.forEach((o)->{
            try {
                o.join();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        System.out.println(demo.count);
    }

}

十二、Demo12

package com.hskw.demo12;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * atomicXXX
 * 一道面试题:多个atomic类连续调用能否构成原子性?
 */
public class Demo {

    AtomicInteger count = new AtomicInteger(0);

    //比如count加到999了,这时候一个线程拿到count判断,虽然.get方法保证原子性,但是他阻止
    //不了其它线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了
    public void test(){
        for (int i = 0; i < 10000; i++) {
            if(count.get() < 1000){
                count.incrementAndGet();
            }
        }
    }
    public static void main(String[] args) {
        Demo demo = new Demo();

        List<Thread> threads = new ArrayList();

        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(demo::test, "thread-" + i));
        }

        threads.forEach((o)->o.start());

        threads.forEach((o)->{
            try {
                o.join();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        System.out.println(demo.count);
    }

}

十三、Demo13

1、Container1

package com.hskw.demo13;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 一道面试题:实现一个容器,提供两个方法,add,size
 * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,
 * 当个数到5个时,线程2给出提示并结束线程2
 *
 * 这里list在两个线程之间不保证可见性,所以线程2始终结束不了
 */
public class Container1 {

    List lists = new ArrayList();

    public void add(Object o){
        lists.add(o);
    }

    public int size(){
        return lists.size();
    }

    public static void main(String[] args) {
        Container1 c = new Container1();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add " + i);

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }," t1").start();

        new Thread(()->{
            while (true) {
                if (c.size() == 5) {
                    break;
                }
            }
            System.out.println("t2线程结束");
        }, "t2").start();
    }

}

2、Container2

package com.hskw.demo13;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 一道面试题:实现一个容器,提供两个方法,add,size
 * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,
 * 当个数到5个时,线程2给出提示并结束
 *
 * 有两个问题,第一由于没有加同步,可能size等于5的时候,有另外一个线程加了一下才break,不是很精确
 * 第二个问题就是浪费cpu,T2线程用的是死循环
 */
public class Container2 {

    volatile List lists = new ArrayList();

    public void add(Object o){
        lists.add(o);
    }

    public int size(){
        return lists.size();
    }

    public static void main(String[] args) {
        Container2 c = new Container2();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add " + i);

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }

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

        new Thread(()->{
            while (true) {
                if (c.size() == 5) {
                    break;
                }
            }
            System.out.println("t2线程结束");
        }, "t2").start();

    }

}

3、Container3

package com.hskw.demo13;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 一道面试题:实现一个容器,提供两个方法,add,size
 * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,
 * 线程2给出提示并结束
 *
 * 这里虽然T2能够及时收到消息唤醒,但是wait会释放锁,notify不会释放锁,所以T1线程结束后
 * T2线程才执行完成
 */
public class Container3 {

    volatile List lists = new ArrayList();

    public void add(Object o){
        lists.add(o);
    }

    public int size(){
        return lists.size();
    }

    public static void main(String[] args) {
        Container3 c = new Container3();
        Object lock = new Object();

        new Thread(()->{
            synchronized (lock) {
                System.out.println("t2启动");
                if (c.size() != 5) {
                    try {
                        lock.wait();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2结束");
            }
        }," t2").start();

        new Thread(()->{
            System.out.println("t1启动");
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    c.add(new Object());
                    System.out.println("add " + i);

                    if (c.size() == 5) {
                        lock.notify();//唤醒了线程但是不会释放锁
                    }

                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "t1").start();

    }

}

4、Container4

package com.hskw.demo13;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 一道面试题:实现一个容器,提供两个方法,add,size
 * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,
 * 当个数到5个时,线程2给出提示并结束
 *
 * 相比较上一个例子,这里T1里面用wait释放锁,T2能够及时结束
 */
public class Container4 {

    volatile List lists = new ArrayList();

    public void add(Object o){
        lists.add(o);
    }

    public int size(){
        return lists.size();
    }

    public static void main(String[] args) {
        Container4 c = new Container4();
        Object lock = new Object();

        new Thread(()->{
            synchronized (lock) {
                System.out.println("t2启动");
                if (c.size() != 5) {
                    try {
					lock.wait();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2结束");
                lock.notify();
            }
        }," t2").start();

        new Thread(()->{
            System.out.println("t1启动");
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    c.add(new Object());
                    System.out.println("add " + i);
                    if (c.size() == 5) {
                        lock.notify();
                        try {
                            lock.wait();//要释放锁,T2才能得到锁得以执行
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "t1").start();

    }

}

5、Container5

package com.hskw.demo13;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 一道面试题:实现一个容器,提供两个方法,add,size
 * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,
 * 当个数到5个时,线程2给出提示并结束
 *
 * CountDownLatch
 * 使用await和countdown方法替代wait和notify
 * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
 * 相当于是发令枪,运动员线程调用await等待,计数到0开始运行
 * 当不涉及同步,只是涉及线程通信的时候,用synchronized加wait,notify就显得太重了
 */
public class Container5 {

    volatile List lists = new ArrayList();

    public void add(Object o){
        lists.add(o);
    }

    public int size(){
        return lists.size();
    }

    public static void main(String[] args) {
        Container5 c = new Container5();

        CountDownLatch latch = new CountDownLatch(1);

        new Thread(()->{
            System.out.println("t2启动");
            if (c.size() != 5) {
                System.out.println(c.size()+"==============");
                try {
                    latch.await();//准备
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("t2结束");
            }
        }," t2").start();

        new Thread(()->{
            System.out.println("t1启动");
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add " + i);
                if (c.size() == 5) {
                    latch.countDown();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();
    }

}

十四、Demo14

1、Container1

package com.hskw.demo14;

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

/**
 * 面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法,
 * 能够支持两个生产者线程以及10个消费者线程的阻塞调用
 * wait notifyAll
 *
 * 这里探究为什么大多数情况下wait和while是一起使用的,
 * 因为这里是有两个生产者线程并且容器容量是固定的,生产方法加了锁。
 * 如果容器满了,这时候第一个生产者线程拿到了锁,他会判断有没有满,如果满了就等待,在等待
 * 的时候会释放掉锁资源,这时候第二个生产者线程就会拿到锁,然后他也会判断是否满,因为容器是
 * 满了,第二个生产者线程也会等待并且释放锁,当消费者消费之后唤醒所有线程,这时候两个生产者线程
 * 都醒来了,因为要竞争锁资源,比如第一个生产者线程拿到了锁,他给容器又加到十了,陷入等待状态,
 * 锁资源释放掉,第二个生产者线程这时候拿到锁资源,他会继续执行(从上次睡眠的地方继续),如果是if
 * 的话,他在wait阻塞之前就已经执行了一次if,所以不会再执行,而是继续往下执行,那这时候就超过了
 * 容器的容量。所以为了让他再一次判断,这里使用while
 */
public class Container1<T>{

	private final LinkedList<T> lists = new LinkedList<>();
	private final int MAX = 10;
	private int count = 0;
	
	public synchronized void put(T t){
		while (lists.size() == MAX) {//
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		lists.add(t);
		++count;
		this.notifyAll();
	}
	
	public synchronized T get(){
		T t = null;
		while (lists.size() == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		t = lists.removeFirst();
		count--;
		this.notifyAll();
		return t;
	}
	
	public static void main(String[] args) {
		Container1<String> c = new Container1<>();
		for (int i = 0; i < 100; i++) {
			new Thread(()->{
				for (int j = 0; j < 5; j++) {
					System.out.println(c.get());
				}
			}, "c" + i).start();
		}
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		for (int i = 0; i < 2; i++) {
			new Thread(()->{
				for (int j = 0; j < 25; j++) {
					c.put(Thread.currentThread().getName() + "" + j);
				}
			}, "p" + i).start();
		}
	}

}

2、Container2

package com.hskw.demo14;

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

/**
 *
 * 使用Lock和Condition来实现
 * condition就是在什么条件下怎么做
 * 对比上一个例子,Condition的方式可以更加精确的指定哪些线程被唤醒
 *
 */
public class Container2<T> {

	private final LinkedList<T> lists = new LinkedList<>();
	private final int MAX = 10;
	private int count = 0;
	
	private Lock lock = new ReentrantLock();
	private Condition producer = lock.newCondition();
	private Condition consumer = lock.newCondition();
	
	public void put(T t){
		try {
			lock.lock();
			while (lists.size() == MAX) {
				producer.await();
			}
			lists.add(t);
			++count;
			consumer.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public T get(){
		T t = null;
		try {
			lock.lock();
			while (lists.size() == 0) {
				consumer.await();
			}
			
			t = lists.removeFirst();
			count --;
			producer.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
		
		return t;
	}
	
	public static void main(String[] args) {
		Container2<String> c = new Container2<>();
		for (int i = 0; i < 100; i++) {
			new Thread(()->{
				for (int j = 0; j < 5; j++) {
					System.out.println(c.get());
				}
			}, "c" + i).start();
		}
		
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		for (int i = 0; i < 2; i++) {
			new Thread(()->{
				for (int j = 0; j < 25; j++) {
					c.put(Thread.currentThread().getName() + " " + j);
				}
			}, "p" + i).start();
		}
	}
}

十五、Demo15

1、RLDemo1

package com.hskw.demo15;

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

public class RLDemo1 {

    /**
     * reentrantlock用于替代synchronized
     * 使用reentrantlock可以完成同样的功能
     * reentrantlock必须要手动释放锁
     * 使用syn锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,
     * 因此经常在finally中进行锁的释放
     */
    Lock lock = new ReentrantLock();

    public void test1(){
        try {
            lock.lock();//this
            for (int i = 0; i < 3; i++) {
                System.out.println(i);
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void test2(){
        lock.lock();
        System.out.println("test 2...");
        lock.unlock();
    }

    public static void main(String[] args) {
        RLDemo1 rlDemo1 = new RLDemo1();
        new Thread(rlDemo1::test1).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(rlDemo1::test2).start();
    }

}

2、RLDemo2

package com.hskw.demo15;

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

public class RLDemo2 {

    Lock lock = new ReentrantLock();

    public void test1(){
        try {
            lock.lock();
            for (int i = 0; i < 2; i++) {
                System.out.println(i);
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,
     * 线程可以决定是否继续等待
     * 可以使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
     * 可以根据tryLock的返回值来判定是否锁定
     * 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中
     */
    public void test2(){
        boolean locked = false;
        try {
            locked = lock.tryLock(3, TimeUnit.SECONDS);
            System.out.println("test2...." + locked);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (locked) {
                System.out.println("test2 end");
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        RLDemo2 rlDemo2 = new RLDemo2();
        new Thread(rlDemo2::test1).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(rlDemo2::test2).start();
    }

}

3、RLDemo3

package com.hskw.demo15;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class RLDemo3 {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(()->{
            try {
                lock.lock();
                System.out.println("t1 start");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
                System.out.println("t1 end");
            } catch (InterruptedException e) {
                System.out.println("interrupted!");
            } finally {
                lock.unlock();
            }
        });
        t1.start();

        Thread t2 = new Thread(()->{
            boolean locked = false;
            try {
                //lock.lock();
                //使用lockInterruptibly来锁定可以对Interrupt方法作出响应
                lock.lockInterruptibly();
                System.out.println("t2 start");
                TimeUnit.SECONDS.sleep(5);
                System.out.println("t2 end");
                locked = true;
            } catch (InterruptedException e) {
                System.out.println("interrupted!");
            } finally {
                if (locked){
                    lock.unlock();
                }
            }
        });
        t2.start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t2.interrupt();
    }

}

4、RLDemo4

package com.hskw.demo15;

import java.util.concurrent.locks.ReentrantLock;

public class RLDemo4 extends Thread{
	//ReentrantLock可以指定是否为公平锁,true为公平,默认为false
	private static ReentrantLock lock = new ReentrantLock(true);

	@Override
	public void run() {
		for (int i = 0; i < 100 ; i++) {
			lock.lock();
			try {
				System.out.println(Thread.currentThread().getName() + "获得锁");
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
		}
	}
	
	public static void main(String[] args) {
		RLDemo4 rlDemo4 = new RLDemo4();
		Thread t1 = new Thread(rlDemo4);
		Thread t2 = new Thread(rlDemo4);
		t1.start();
		t2.start();
	}

}

是你跟在我身后太久了,看不到我所看到的景象。               ———— 山鬼谣

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值