Java多线程高并发编程代码笔记(二)

ReentrantLock可以用来代替synchronized

ReentrantLock必须要手动释放锁。使用synchronized锁定如果遇到异常,jvm会自动释放锁,但是Lock必须手动释放,因此常常在finally中释放锁

package demo22;


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

/* ReentrantLock用来替代synchronized
 * ReentrantLock必须要手动释放锁。使用synchronized锁定如果遇到异常,jvm会自动释放锁,但是Lock必须手动释放,因此常常在finally中释放锁*/
public class T {
    Lock lock = new ReentrantLock();

    void m1() {
        try {
            lock.lock();  //加锁  //相当于synchronized(this)
            for (int i = 0; i < 10; i++) {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(" " + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();  //释放锁
        }
    }

    void m2() {
        lock.lock();  //加锁
        System.out.println(" m2()... ");
        lock.unlock();  //释放锁
    }

    public static void main(String[] args) {
        T t = new T();
        new Thread(t::m1).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(t::m2).start();
    }

}

运行结果

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
 m2()... 

ReentrantLock可以进行尝试锁定tryLock()

  • 不指定时间
package demo23;

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

/**
 * 使用ReentrantLock可以进行尝试锁定tryLock();若无法锁定或在指定时间内无法锁定,线程可以决定是否等待
 */
public class T1 {
    Lock lock = new ReentrantLock();

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

    /**
     * 使用tryLock进行尝试锁定,不管锁定与否,方法都将会继续执行,可以根据tryLock的返回值判定是否被锁定了
     * 可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unlock的处理,必须放到finally中。
     */
    void m2() {
        boolean locked = lock.tryLock();
        System.out.print("  m2..." + locked + "  ");
        if (locked) lock.unlock();  //false 不指定尝试时间
    }

    public static void main(String[] args) {
        T1 t = new T1();
        new Thread(t::m1).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(t::m2).start();
    }

}

运行结果

 0  m2...false   1 2 3 4 5 6 7 8 9
  • 指定时间
package demo23;

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

/**
 * 使用ReentrantLock可以进行尝试锁定tryLock();若无法锁定或在指定时间内无法锁定,线程可以决定是否等待
 */
public class T2 {
    Lock lock = new ReentrantLock();

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

    /**
     * 使用tryLock进行尝试锁定,不管锁定与否,方法都将会继续执行,可以根据tryLock的返回值判定是否被锁定了
     * 可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unlock的处理,必须放到finally中。
     */
    void m2() {
        boolean locked = false;
        try {
            locked = lock.tryLock(5, TimeUnit.SECONDS); //指定超时时间为5s
            System.out.println("  m2..." + locked + "  ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (locked) {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        T2 t = new T2();
        new Thread(t::m1).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(t::m2).start();
    }

}

运行结果

 0 1 2 3 4 5  m2...false  
 6 7 8 9

ReentrantLock的lockInterruptibly方法

ReentrantLock可调用lockInterruptibly方法,对线程的interrupt方法作出响应,在一个线程等待的过程中,可以被打断。

package demo24;


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

/*ReentrantLock可调用lockInterruptibly()方法,对线程的interrupt()方法作出响应,在一个线程等待的过程中,可以被打断。
 * ReentrantLock的lock()方法是不能被打断的,即锁用lock()方法锁定,线程调用interrupt()方法是毫无作用的*/
public class T {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            try {
                lock.lockInterruptibly();
                System.out.print("  t1 start...  ");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);  //t1不停的运行,睡死了
                System.out.print("  t1 end...  ");
            } catch (InterruptedException e) {
                System.out.print(" t1-interrupted! ");
            } finally {
                System.out.println("t1 解锁");
                lock.unlock();
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            try {
                 lock.lock(); //不能对interrupt()方法作出响应
               // lock.lockInterruptibly();  //也是上锁,但是可以对interrupt()方法作出响应
                System.out.print("  t2 start...  ");
                TimeUnit.SECONDS.sleep(5);
                System.out.print("  t2 end...   ");
            } catch (InterruptedException e) {
                System.out.println("  t2-interrupted!  ");
            } finally {
                try {
                    System.out.println("t2 解锁");
                    lock.unlock();
                } catch (Exception e) {
                    System.out.println("没有得到锁的线程运行结束");
                }
            }
        }, "t2");
        t2.start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.interrupt();  //打断t2的等待,如果使用的是t2使用的是lock()方法进行上锁就无法打断,如果使用lockInterruptibl()方法就可以打断
    }

}


运行结果

  t1 start...  

无法打断t2线程。

改用lockInterruptibl()

package demo24;


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

/*ReentrantLock可调用lockInterruptibly()方法,对线程的interrupt()方法作出响应,在一个线程等待的过程中,可以被打断。
 * ReentrantLock的lock()方法是不能被打断的,即锁用lock()方法锁定,线程调用interrupt()方法是毫无作用的*/
public class T {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            try {
                lock.lockInterruptibly();
                System.out.print("  t1 start...  ");
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);  //t1不停的运行,睡死了
                System.out.print("  t1 end...  ");
            } catch (InterruptedException e) {
                System.out.print(" t1-interrupted! ");
            } finally {
                System.out.println("t1 解锁");
                lock.unlock();
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            try {
                 //lock.lock(); //不能对interrupt()方法作出响应
                 lock.lockInterruptibly();  //也是上锁,但是可以对interrupt()方法作出响应
                System.out.print("  t2 start...  ");
                TimeUnit.SECONDS.sleep(5);
                System.out.print("  t2 end...   ");
            } catch (InterruptedException e) {
                System.out.println("  t2-interrupted!  ");
            } finally {
                try {
                    System.out.println("t2 解锁");
                    lock.unlock();
                } catch (Exception e) {
                    System.out.println("没有得到锁的线程运行结束");
                }
            }
        }, "t2");
        t2.start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.interrupt();  //打断t2的等待,如果使用的是t2使用的是lock()方法进行上锁就无法打断,如果使用lockInterruptibl()方法就可以打断
    }

}


运行结果

  t1 start...    t2-interrupted!  
t2 解锁
没有得到锁的线程运行结束

ReentrantLock可以指定为公平锁

package demo25;


import java.util.concurrent.locks.ReentrantLock;

/*ReentrantLock可以指定为公平锁,构造方法中将fair属性设置为true即为公平锁,fair默认为false*/
public class T extends Thread {
    private static ReentrantLock lock = new ReentrantLock(true); //参数为true表示为公平锁,可对比输出结果

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "-获得锁 ");
            } finally {
                lock.unlock();
            }
        }
    }

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

运行结果

Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 
Thread-1-获得锁 
Thread-2-获得锁 

面试题

写一个固定容量的同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用。

使用wait和notifyAll方法来实现

下面使用wait和notifyAll方法来实现

package demo26;

import java.util.LinkedList;

/**
 * 写一个固定容量的同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用。
 * wait和notifyAll方法来实现。
 */
public class MyContainer1 {
    final private LinkedList<Object> list = new LinkedList<Object>();
    final private int MAX = 10; //最多十个元素
    private int count = 0;

    public synchronized void put(Object t) {
        while (list.size() == MAX) { //想想为什么用while而不是if
            try {
                this.wait(); 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.add(t);
        ++count;
        this.notifyAll(); //通知消费者进程进行消费
    }

    public synchronized Object get() {
        Object t = null;
        while (list.size() == 0) {  
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        t = list.removeFirst();
        count--;
        this.notifyAll();  //通知生产者进程进行生产
        return t;
    }

    public static void main(String[] args) {
        MyContainer1 pc = new MyContainer1();
        for (int i = 0; i < 10; i++) {  //10个消费者
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {  //每个消费者最多消费5个
                    System.out.println("消费者线程" + Thread.currentThread().getName() + ",开始消费: " + pc.get());
                }
            }, "c" + i).start();
        }
        //启动生产者线程
        for (int i = 0; i < 2; i++) { //2个生产者
            new Thread(() -> {
                for (int j = 0; j < 25; j++) {  //每个生产者最多生产25个
                    pc.put(Thread.currentThread().getName() + " " + j);
                }
            }, "p" + i).start();
        }
    }

}

运行结果

消费者线程c9,开始消费: p0 0
消费者线程c9,开始消费: p0 1
消费者线程c8,开始消费: p0 2
消费者线程c7,开始消费: p0 3
消费者线程c0,开始消费: p0 4
消费者线程c6,开始消费: p0 5
消费者线程c7,开始消费: p0 6
消费者线程c5,开始消费: p0 7
消费者线程c0,开始消费: p0 8
消费者线程c6,开始消费: p0 9
消费者线程c4,开始消费: p0 11
消费者线程c9,开始消费: p0 10
消费者线程c1,开始消费: p0 12
消费者线程c8,开始消费: p0 13
消费者线程c5,开始消费: p0 14
消费者线程c6,开始消费: p0 15
消费者线程c3,开始消费: p0 16
消费者线程c2,开始消费: p0 17
消费者线程c4,开始消费: p0 18
消费者线程c6,开始消费: p0 19
消费者线程c8,开始消费: p0 20
消费者线程c7,开始消费: p0 21
消费者线程c7,开始消费: p0 24
消费者线程c4,开始消费: p0 23
消费者线程c9,开始消费: p0 22
消费者线程c9,开始消费: p1 0
消费者线程c0,开始消费: p1 4
消费者线程c0,开始消费: p1 5
消费者线程c0,开始消费: p1 6
消费者线程c1,开始消费: p1 7
消费者线程c1,开始消费: p1 8
消费者线程c1,开始消费: p1 9
消费者线程c1,开始消费: p1 10
消费者线程c5,开始消费: p1 3
消费者线程c5,开始消费: p1 11
消费者线程c5,开始消费: p1 12
消费者线程c7,开始消费: p1 13
消费者线程c8,开始消费: p1 2
消费者线程c6,开始消费: p1 16
消费者线程c8,开始消费: p1 17
消费者线程c4,开始消费: p1 1
消费者线程c3,开始消费: p1 15
消费者线程c2,开始消费: p1 14
消费者线程c3,开始消费: p1 19
消费者线程c3,开始消费: p1 21
消费者线程c4,开始消费: p1 18
消费者线程c3,开始消费: p1 22
消费者线程c2,开始消费: p1 20
消费者线程c2,开始消费: p1 23
消费者线程c2,开始消费: p1 24

这里我们可以想想两个问题:

  1. public synchronized void put(Object t)方法中的判断while (list.size() == MAX) { //想想为什么用while而不是if这里为什么需要用while?而不用if?
  2. public synchronized void put(Object t)方法中的this.notifyAll(); //通知消费者进程进行消费为啥这里使用的是notifyAll()而不是notify()方法?

答1:

我们假设判断是if的情况如下:

 public synchronized void put(Object t) {
        if (list.size() == MAX) { 
            try {
                this.wait();
                //1. 假如有一个线程t1满足了size==MAX的情况,然后一进来就被wait了,并且释放了锁,线程t1等待在这里。
                //2. 这个时候其他的线程t2获取了锁,也调用了put的方法,这个时候判断是满的,所以t2线程也是释放了锁并wait等待在这里了。
                //3. 这个时候其他线程t3获取了锁,这时候调用的是get方法了。get方法执行完之后会size-1,这个时候size不是满的了,然后notifyAll,叫醒等待的线程。
                //4. 这时候刚好叫醒了等待的线程t1,t1线程重新获取锁,并从wait()方法继续往下执行,因为我们已经判断过if了,所以往下执行add的操作了,这个时候size+1了,又变成满的了,最后又调用了notifyAll方法。叫醒等待的线程
                //5. 这个时候刚刚好叫醒的是t2线程了,t2线程重新获取锁了,并从wait方法继续往下执行,因为if条件判断过了,所以这个时候不会再判断size是不是满了,但是这个时候size已经是满的了,但还是继续往下执行add的操作,所以size最终就变成了MAX+1。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        list.add(t);
        ++count;
        this.notifyAll(); 
    }

所以我们需要用while,让线程重新在判断一下size是不是满了。所以同理get方法中也是需要用while

答2:

这里假设使用notify的情况如下:

 public synchronized void put(Object t) {
        while (list.size() == MAX) { //想想为什么用while而不是if
            try {
                this.wait(); 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.add(t);
        ++count;
        this.notify(); //通知消费者进程进行消费
        //在某些情况下可能会出现问题,举个例子比如我现在有50个消费者线程,2个生产者线程
        //1. 运气不好一开始运行了20个消费者线程,然后发现size都为0,全部等待,等待了20个消费线程
        //2. 生产者线程开始执行了,而且再没加到size为MAX之前,CPU都恰巧调度了生产者线程,调度了10次,所以会叫醒10个消费者线程,还有10个消费线程在等待,然后现在size已经满了。
        //3. CPU又不巧调度了生产者线程,判断满了,所以wait了一个生产者线程,还剩下一个生产者线程,但是不巧的是,下次执行的还是一个生产者线程,判断满了,又wait了一个生产者线程,此时生产者线程都在等待了。
        //4. 还好我们还有消费者线程,接下来依次调度了10个消费者线程,所以会叫醒10个线程,但是叫醒的这10个线程不巧刚刚好是之前等待的那10个消费者线程。并且这个时候size已经为0了,而且全剩下消费者线程了,所以这些真完蛋了,剩下的消费者都会判断size为0,然后进入等待,最终所有的线程都gg了。所有的线程都在等待,程序卡死了。
        //总结:最好还是用notifyAll,别用notify
    }

值得注意的是notify只是把等待的线程变成可运行的状态,到底开始执行哪个线程还是要看CPU的调度,所以调用了notify不一定下一个执行的就是wait的线程,是有可能是其他的线程。

比如下面这个例子就可以说明了。

package demo26;


public class T {

    /**
     * 运行结果:
     * 
     * 主线程结束
     * 线程t1 start 
     * 线程t2 start
     * 线程t2 end
     * 线程t3 start
     * 线程t3 end
     * 线程t4 start
     * 线程t4 end
     * 线程t1 end
     * 
     * 
     * 
     * 1. t1线程开始wait
     * 2. 运行t2线程
     * 3. 运行t3线程,并且调用了notify方法
     * 4. 运行的不是wait线程t1,而是运行了t4线程
     * 5. 轮到t1线程执行了。
     * 
     * 所以说明调用了notify只是把wait的线程叫醒了,变成可执行的状态,并不是下一个一定会执行wait的线程
     */
    public static void main(String[] args) {
        Object lock = new Object();
        new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("线程t1 start");
                    lock.wait();
                    System.out.println("线程t1 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程t2 start");
                System.out.println("线程t2 end");

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


        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程t3 start");
                lock.notify();
                System.out.println("线程t3 end");

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

        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程t4 start");
                System.out.println("线程t4 end");

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


        System.out.println("主线程结束");

    }
}

运行结果

主线程结束
线程t1 start
线程t2 start
线程t2 end
线程t3 start
线程t3 end
线程t4 start
线程t4 end
线程t1 end

顺便附上一个线程的状态(参考:https://www.cnblogs.com/williamjie/p/9440846.html)

在这里插入图片描述

  • 当一个线程执行了start方法后,不代表这个线程就会立即被执行,只代表这个线程处于可运行的状态,最终由OS的线程调度来决定哪个可运行状态下的线程被执行。
  • 一个线程一次被选中执行是有时间限制的,这个时间段叫做CPU的时间片,当时间片用完但线程还没有结束时,这个线程又会变为可运行状态,等待OS的再次调度;在运行的线程里执行Thread.yeild()方法同样可以使当前线程变为可运行状态。
  • 在一个运行中的线程等待用户输入、调用Thread.sleep()、调用了其他线程的join()方法,则当前线程变为阻塞状态。
  • 阻塞状态的线程用户输入完毕、sleep时间到、join的线程结束,则当前线程由阻塞状态变为可运行状态。
  • 运行中的线程调用wait方法,此线程进入等待队列。
  • 运行中的线程遇到synchronized同时没有拿到对象的锁标记、等待队列的线程wait时间到、等待队列的线程被notify方法唤醒、有其他线程调用notifyAll方法,则线程变成锁池状态。
  • 锁池状态的线程获得对象锁标记,则线程变成可运行状态。
  • 运行中的线程run方法执行完毕或main线程结束,则线程运行结束。

使用Lock和Condition来实现

package demo26;

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

/**
 * 使用Lock和Condition来实现生产者和消费者的同步容器,
 * 相比使用wait/notifyAll,使用Condition的方式能更加精确地指定哪些线程被唤醒。
 */
public class MyContainer2 {
    final private LinkedList<Object> list = new LinkedList<>();
    final private 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(Object obj) {
        try {
            lock.lock();
            while (list.size() == MAX) {
                producer.await();
            }
            list.add(obj);
            ++count;
            consumer.signalAll(); //通知消费者进行消费
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public Object get() {
        Object obj = null;
        try {
            lock.lock();
            while (count == 0) {
                consumer.await();
            }
            obj = list.removeFirst();
            count--;
            producer.signalAll();  //通知生产者进行生产
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return obj;
    }

    public static void main(String[] args) {
        MyContainer2 c = new MyContainer2();
        for (int i = 0; i < 10; i++) {  //10个消费者
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {  //每个消费者最多消费5个
                    System.out.println("消费者线程" + Thread.currentThread().getName() + ",开始消费: " + c.get());
                }
            }, "c" + i).start();
        }
        //启动生产者线程
        for (int i = 0; i < 2; i++) { //2个生产者
            new Thread(() -> {
                for (int j = 0; j < 25; j++) {  //每个生产者最多生产25个
                    c.put(Thread.currentThread().getName() + " " + j);
                }
            }, "p" + i).start();
        }
    }
}

运行结果

消费者线程c0,开始消费: p0 0
消费者线程c0,开始消费: p1 4
消费者线程c0,开始消费: p1 7
消费者线程c4,开始消费: p1 3
消费者线程c3,开始消费: p1 2
消费者线程c0,开始消费: p0 3
消费者线程c9,开始消费: p0 5
消费者线程c9,开始消费: p0 8
消费者线程c9,开始消费: p0 9
消费者线程c9,开始消费: p0 10
消费者线程c1,开始消费: p1 0
消费者线程c1,开始消费: p0 11
消费者线程c1,开始消费: p0 12
消费者线程c2,开始消费: p1 1
消费者线程c2,开始消费: p0 14
消费者线程c2,开始消费: p0 15
消费者线程c2,开始消费: p0 16
消费者线程c2,开始消费: p0 17
消费者线程c1,开始消费: p0 13
消费者线程c1,开始消费: p0 18
消费者线程c4,开始消费: p0 7
消费者线程c4,开始消费: p0 19
消费者线程c4,开始消费: p0 20
消费者线程c4,开始消费: p0 21
消费者线程c0,开始消费: p0 6
消费者线程c3,开始消费: p0 4
消费者线程c3,开始消费: p0 22
消费者线程c3,开始消费: p0 23
消费者线程c3,开始消费: p0 24
消费者线程c8,开始消费: p0 2
消费者线程c8,开始消费: p1 8
消费者线程c8,开始消费: p1 9
消费者线程c8,开始消费: p1 10
消费者线程c8,开始消费: p1 11
消费者线程c7,开始消费: p0 1
消费者线程c7,开始消费: p1 12
消费者线程c7,开始消费: p1 13
消费者线程c6,开始消费: p1 6
消费者线程c5,开始消费: p1 5
消费者线程c5,开始消费: p1 16
消费者线程c5,开始消费: p1 17
消费者线程c5,开始消费: p1 18
消费者线程c6,开始消费: p1 15
消费者线程c7,开始消费: p1 14
消费者线程c6,开始消费: p1 20
消费者线程c5,开始消费: p1 19
消费者线程c6,开始消费: p1 22
消费者线程c6,开始消费: p1 23
消费者线程c7,开始消费: p1 21
消费者线程c9,开始消费: p1 24

ThreadLocal线程局部变量

package demo27;

import java.util.concurrent.TimeUnit;

/**
 * ThreadLocal线程局部变量
 * ThreadLocal是使用空间换时间,synchronized是使用时间换空间比如在hibernate中session就存在与ThreadLocal中,避免synchronized的使用
 * 线程局部变量属于每个线程都有自己的,线程间不共享,互不影响
 * 运行下面的程序,理解ThreadLocal
 */
public class T {

    static ThreadLocal<Person> tl = new ThreadLocal<>();

    public static void main(String[] args) {
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(tl.get());
        }).start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tl.set(new Person());
        }).start();
    }

}

class Person{
    private String name = "kevin";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

运行结果

null

参考

https://www.bilibili.com/video/av57098526

https://www.bilibili.com/video/av33688545?p=20

https://www.cnblogs.com/williamjie/p/9440846.html

https://blog.csdn.net/zl_StepByStep/article/details/88760572

源代码

https://gitee.com/cckevincyh/java_concurrent_learning

发布了647 篇原创文章 · 获赞 816 · 访问量 98万+

猜你喜欢

转载自blog.csdn.net/cckevincyh/article/details/103845815