基础

synchronized对某个对象加锁

  1. public class T {
  2. private int count = 10;
  3. private Object o = new Object();
  4. public void m() {
  5. synchronized(o) { // 任何线程要执行下面的代码,必须先拿到o的锁
  6. count--;
  7. System.out.println(Thread.currentThread().getName() + " count = " + count);
  8. }
  9. }
  10. }
  1. public class T {
  2. private int count = 10;
  3. public void m() {
  4. synchronized (this) { // 任何线程要执行下面的代码,必须先拿到this的锁
  5. count--;
  6. System.out.println(Thread.currentThread().getName() + " count = " + count);
  7. }
  8. }
  9. }

synchronized对方法加锁

  1. public class T {
  2. private int count = 10;
  3. public synchronized void m() { //等同于在方法的代码执行时要synchronized(this)
  4. count--;
  5. System.out.println(Thread.currentThread().getName() + " count = " + count);
  6. }
  7. }
  1. public class T {
  2. private static int count = 10;
  3. public synchronized static void m() { // 这里等同于synchronized(T.class)
  4. count--;
  5. System.out.println(Thread.currentThread().getName() + " count = " + count);
  6. }
  7. public static void mm() {
  8. synchronized (T.class) {
  9. count--;
  10. }
  11. }
  12. }

volatile

  1. // volatile不能保证原子性
  2. public class T implements Runnable {
  3. private /*volatile*/ int count = 10000;
  4. public /*synchronized*/ void run() {
  5. for (int i = 0; i < 100; i++)
  6. count--;
  7. System.out.println(Thread.currentThread().getName() + " count = " + count);
  8. }
  9. public static void main(String[] args) {
  10. T t = new T();
  11. for (int i = 0; i < 100; i++) {
  12. new Thread(t, "THREAD" + i).start();
  13. }
  14. }
  15. }
  1. // 保证线程可见性
  2. public class T01_HelloVolatile {
  3. volatile boolean running = true; // 对比一下有无volatile的情况下,整个程序运行结果的区别
  4. void m() {
  5. System.out.println("m start");
  6. while(running) { }
  7. System.out.println("m end!");
  8. }
  9. public static void main(String[] args) {
  10. T01_HelloVolatile t = new T01_HelloVolatile();
  11. new Thread(t::m, "t1").start();
  12. try {
  13. TimeUnit.SECONDS.sleep(1);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. t.running = false;
  18. }
  19. }
  1. // volatile 引用类型(包括数组)只能保证引用本身的可见性,不能保证内部字段的可见性
  2. public class T02_VolatileReference1 {
  3. boolean running = true;
  4. volatile static T02_VolatileReference1 T = new T02_VolatileReference1();
  5. void m() {
  6. System.out.println("m start");
  7. while(running) {
  8. // try {
  9. // TimeUnit.MILLISECONDS.sleep(10);
  10. // } catch (InterruptedException e) {
  11. // e.printStackTrace();
  12. // }
  13. // 这段代码被放开后程序可以正常执性,可能是JVM对volatile有优化,但是没有考证过具体的细节
  14. }
  15. System.out.println("m end!");
  16. }
  17. public static void main(String[] args) {
  18. new Thread(T::m, "t1").start();
  19. try {
  20. TimeUnit.SECONDS.sleep(1);
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. T.running = false;
  25. }
  26. }

synchronized特点

  1. // synchronized是可重入的,也就是说他是基于线程分配的
  2. public class T {
  3. synchronized void m1() {
  4. System.out.println("m1 start");
  5. try {
  6. TimeUnit.SECONDS.sleep(1);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. m2();
  11. System.out.println("m1 end");
  12. }
  13. synchronized void m2() {
  14. try {
  15. TimeUnit.SECONDS.sleep(2);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. System.out.println("m2");
  20. }
  21. public static void main(String[] args) {
  22. new T().m1();
  23. }
  24. }
  1. // 继承并覆盖方法也可以冲入
  2. public class T {
  3. synchronized void m() {
  4. System.out.println("m start");
  5. try {
  6. TimeUnit.SECONDS.sleep(1);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.println("m end");
  11. }
  12. public static void main(String[] args) {
  13. new TT().m();
  14. }
  15. }
  16. class TT extends T {
  17. @Override
  18. synchronized void m() {
  19. System.out.println("child m start");
  20. super.m();
  21. System.out.println("child m end");
  22. }
  23. }
  1. // 遇到异常释放锁
  2. public class T {
  3. int count = 0;
  4. synchronized void m() {
  5. System.out.println(Thread.currentThread().getName() + " start");
  6. while(true) {
  7. count ++;
  8. System.out.println(Thread.currentThread().getName() + " count = " + count);
  9. try {
  10. TimeUnit.SECONDS.sleep(1);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. if(count == 5) {
  15. int i = 1/0; // 此处抛出异常,锁将被释放
  16. System.out.println(i);
  17. }
  18. }
  19. }
  20. public static void main(String[] args) {
  21. T t = new T();
  22. Runnable r = () -> t.m();
  23. new Thread(r, "t1").start();
  24. try {
  25. TimeUnit.SECONDS.sleep(3);
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. new Thread(r, "t2").start();
  30. }
  31. }
  1. // 锁定某对象o,如果o的属性发生改变,不影响锁的使用但是如果o变成另外一个对象,则锁定的对象发生改变
  2. // 应该避免将锁定对象的引用变成另外的对象
  3. public class SyncSameObject {
  4. /*final*/ Object o = new Object();
  5. void m() {
  6. synchronized (o) {
  7. while (true) {
  8. try {
  9. TimeUnit.SECONDS.sleep(1);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. System.out.println(Thread.currentThread().getName());
  14. }
  15. }
  16. }
  17. public static void main(String[] args) {
  18. SyncSameObject t = new SyncSameObject();
  19. // 启动第一个线程
  20. new Thread(t::m, "t1").start();
  21. try {
  22. TimeUnit.SECONDS.sleep(3);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. // 创建第二个线程
  27. Thread t2 = new Thread(t::m, "t2");
  28. t.o = new Object(); // 锁对象发生改变,所以t2线程得以执行,如果注释掉这句话,线程2将永远得不到执行机会
  29. t2.start();
  30. }
  31. }

面试题

1

实现一个容器,提供两个方法,add,size。写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。

WaitNotify

  1. public class WaitNotify {
  2. List lists = new ArrayList();
  3. public void add(Object o) { lists.add(o); }
  4. public int size() { return lists.size(); }
  5. static volatile boolean t2Start = false;
  6. public static void main(String[] args) throws InterruptedException {
  7. WaitNotify c = new WaitNotify();
  8. final Object lock = new Object();
  9. new Thread(() -> {
  10. try {
  11. System.out.println("t1 start");
  12. for (int i = 0; i < 10; i++) {
  13. c.add(new Object());
  14. System.out.println("add " + i);
  15. synchronized (lock) {
  16. if (c.size() == 5) {
  17. if(t2Start)
  18. lock.notify();
  19. lock.wait();
  20. }
  21. }
  22. TimeUnit.SECONDS.sleep(1);
  23. }
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. }, "t1").start();
  28. TimeUnit.SECONDS.sleep(1);
  29. new Thread(() -> {
  30. t2Start = true;
  31. try {
  32. System.out.println("t2 start");
  33. while (true) {
  34. synchronized (lock) {
  35. if (c.size() != 5) {
  36. lock.wait();
  37. } else {
  38. System.out.println("size == 5");
  39. System.out.println("t2 end");
  40. lock.notify();
  41. break;
  42. }
  43. }
  44. }
  45. } catch (Exception e) {
  46. e.printStackTrace();
  47. }
  48. }, "t2").start();
  49. }
  50. }

AwaitSignal

  1. public class AwaitSignal {
  2. List lists = new ArrayList();
  3. public void add(Object o) { lists.add(o); }
  4. public int size() { return lists.size(); }
  5. static volatile boolean t2Start = false;
  6. public static void main(String[] args) throws InterruptedException {
  7. AwaitSignal c = new AwaitSignal();
  8. ReentrantLock lock = new ReentrantLock();
  9. Condition condition = lock.newCondition();
  10. new Thread(() -> {
  11. try {
  12. System.out.println("t1 start");
  13. for (int i = 0; i < 10; i++) {
  14. c.add(new Object());
  15. System.out.println("add " + i);
  16. lock.lock();
  17. if (c.size() == 5) {
  18. if (t2Start) {
  19. condition.signal();
  20. }
  21. condition.await();
  22. }
  23. lock.unlock();
  24. TimeUnit.SECONDS.sleep(1);
  25. }
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. }
  29. }, "t1").start();
  30. TimeUnit.SECONDS.sleep(7);
  31. new Thread(() -> {
  32. t2Start = true;
  33. try {
  34. System.out.println("t2 start");
  35. while (true) {
  36. lock.lock();
  37. if (c.size() != 5) {
  38. condition.await();
  39. } else {
  40. System.out.println("size == 5");
  41. System.out.println("t2 end");
  42. // 通知t1继续执行
  43. condition.signalAll();
  44. lock.unlock();
  45. break;
  46. }
  47. lock.unlock();
  48. }
  49. } catch (Exception e) {
  50. e.printStackTrace();
  51. }
  52. }, "t2").start();
  53. }
  54. }

2

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

  1. public class MyContainer2<T> {
  2. final private LinkedList<T> lists = new LinkedList<>();
  3. final private int MAX = 10; // 最多10个元素
  4. private int count = 0;
  5. private Lock lock = new ReentrantLock();
  6. private Condition producer = lock.newCondition();
  7. private Condition consumer = lock.newCondition();
  8. public void put(T t) {
  9. try {
  10. lock.lock();
  11. /**
  12. * 如果这个地方使用if不使用while,考虑一种情况
  13. * t1和t2两个线程在此等待,突然一个signal将他俩都唤醒了
  14. * t1抢到了资源,所以继续运行,又把队列加满了,然后释放了资源
  15. * t2终于获得了资源,但此时队列实际上满的,t2不应该去再往队列里加一个
  16. * 所以解决方案就是t2判断队列是不是满的,他发现是满的,就继续await
  17. */
  18. while (lists.size() == MAX) {
  19. producer.await(); // 一定要注意,这个方法会释放临界资源
  20. }
  21. lists.add(t);
  22. ++count;
  23. consumer.signalAll(); // 通知消费者线程进行消费
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. } finally {
  27. lock.unlock();
  28. }
  29. }
  30. public T get() {
  31. T t = null;
  32. try {
  33. lock.lock();
  34. while (lists.size() == 0) {
  35. consumer.await();
  36. }
  37. t = lists.removeFirst();
  38. count--;
  39. producer.signalAll(); // 通知生产者进行生产
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. } finally {
  43. lock.unlock();
  44. }
  45. return t;
  46. }
  47. public static void main(String[] args) {
  48. MyContainer2<String> c = new MyContainer2<>();
  49. // 启动消费者线程
  50. for (int i = 0; i < 10; i++) {
  51. new Thread(() -> {
  52. for (int j = 0; j < 5; j++)
  53. System.out.println(c.get());
  54. }, "c" + i).start();
  55. }
  56. try {
  57. TimeUnit.SECONDS.sleep(2);
  58. } catch (InterruptedException e) {
  59. e.printStackTrace();
  60. }
  61. // 启动生产者线程
  62. for (int i = 0; i < 2; i++) {
  63. new Thread(() -> {
  64. for (int j = 0; j < 25; j++)
  65. c.put(Thread.currentThread().getName() + " " + j);
  66. }, "p" + i).start();
  67. }
  68. }
  69. }

3

用线程顺序打印A1B2C3….Z26。

  1. public class WaitNotify {
  2. private static volatile boolean t2Started = false;
  3. public static void main(String[] args) {
  4. final Object o = new Object();
  5. char[] aI = "1234567".toCharArray();
  6. char[] aC = "ABCDEFG".toCharArray();
  7. new Thread(() -> {
  8. try {
  9. synchronized (o) {
  10. if (!t2Started) {
  11. o.wait();
  12. }
  13. for (char c : aI) {
  14. System.out.print(c);
  15. o.notify();
  16. o.wait();
  17. }
  18. o.notify();
  19. }
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }, "t1").start();
  24. new Thread(() -> {
  25. t2Started = true;
  26. try {
  27. synchronized (o) {
  28. for (char c : aC) {
  29. System.out.print(c);
  30. o.notify();
  31. o.wait();
  32. }
  33. o.notify();
  34. }
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. }, "t2").start();
  39. }
  40. }