基础
synchronized对某个对象加锁
public class T {private int count = 10;private Object o = new Object();public void m() {synchronized(o) { // 任何线程要执行下面的代码,必须先拿到o的锁count--;System.out.println(Thread.currentThread().getName() + " count = " + count);}}}
public class T {private int count = 10;public void m() {synchronized (this) { // 任何线程要执行下面的代码,必须先拿到this的锁count--;System.out.println(Thread.currentThread().getName() + " count = " + count);}}}
synchronized对方法加锁
public class T {private int count = 10;public synchronized void m() { //等同于在方法的代码执行时要synchronized(this)count--;System.out.println(Thread.currentThread().getName() + " count = " + count);}}
public class T {private static int count = 10;public synchronized static void m() { // 这里等同于synchronized(T.class)count--;System.out.println(Thread.currentThread().getName() + " count = " + count);}public static void mm() {synchronized (T.class) {count--;}}}
volatile
// volatile不能保证原子性public class T implements Runnable {private /*volatile*/ int count = 10000;public /*synchronized*/ void run() {for (int i = 0; i < 100; i++)count--;System.out.println(Thread.currentThread().getName() + " count = " + count);}public static void main(String[] args) {T t = new T();for (int i = 0; i < 100; i++) {new Thread(t, "THREAD" + i).start();}}}
// 保证线程可见性public class T01_HelloVolatile {volatile boolean running = true; // 对比一下有无volatile的情况下,整个程序运行结果的区别void m() {System.out.println("m start");while(running) { }System.out.println("m end!");}public static void main(String[] args) {T01_HelloVolatile t = new T01_HelloVolatile();new Thread(t::m, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}t.running = false;}}
// volatile 引用类型(包括数组)只能保证引用本身的可见性,不能保证内部字段的可见性public class T02_VolatileReference1 {boolean running = true;volatile static T02_VolatileReference1 T = new T02_VolatileReference1();void m() {System.out.println("m start");while(running) {// try {// TimeUnit.MILLISECONDS.sleep(10);// } catch (InterruptedException e) {// e.printStackTrace();// }// 这段代码被放开后程序可以正常执性,可能是JVM对volatile有优化,但是没有考证过具体的细节}System.out.println("m end!");}public static void main(String[] args) {new Thread(T::m, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}T.running = false;}}
synchronized特点
// synchronized是可重入的,也就是说他是基于线程分配的public class T {synchronized void m1() {System.out.println("m1 start");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}m2();System.out.println("m1 end");}synchronized void m2() {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("m2");}public static void main(String[] args) {new T().m1();}}
// 继承并覆盖方法也可以冲入public class T {synchronized void m() {System.out.println("m start");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("m end");}public static void main(String[] args) {new TT().m();}}class TT extends T {@Overridesynchronized void m() {System.out.println("child m start");super.m();System.out.println("child m end");}}
// 遇到异常释放锁public class T {int count = 0;synchronized void m() {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) {int i = 1/0; // 此处抛出异常,锁将被释放System.out.println(i);}}}public static void main(String[] args) {T t = new T();Runnable r = () -> t.m();new Thread(r, "t1").start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}new Thread(r, "t2").start();}}
// 锁定某对象o,如果o的属性发生改变,不影响锁的使用但是如果o变成另外一个对象,则锁定的对象发生改变// 应该避免将锁定对象的引用变成另外的对象public class SyncSameObject {/*final*/ Object o = new Object();void m() {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) {SyncSameObject t = new SyncSameObject();// 启动第一个线程new Thread(t::m, "t1").start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}// 创建第二个线程Thread t2 = new Thread(t::m, "t2");t.o = new Object(); // 锁对象发生改变,所以t2线程得以执行,如果注释掉这句话,线程2将永远得不到执行机会t2.start();}}
面试题
1
实现一个容器,提供两个方法,add,size。写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。
WaitNotify
public class WaitNotify {List lists = new ArrayList();public void add(Object o) { lists.add(o); }public int size() { return lists.size(); }static volatile boolean t2Start = false;public static void main(String[] args) throws InterruptedException {WaitNotify c = new WaitNotify();final Object lock = new Object();new Thread(() -> {try {System.out.println("t1 start");for (int i = 0; i < 10; i++) {c.add(new Object());System.out.println("add " + i);synchronized (lock) {if (c.size() == 5) {if(t2Start)lock.notify();lock.wait();}}TimeUnit.SECONDS.sleep(1);}} catch (Exception e) {e.printStackTrace();}}, "t1").start();TimeUnit.SECONDS.sleep(1);new Thread(() -> {t2Start = true;try {System.out.println("t2 start");while (true) {synchronized (lock) {if (c.size() != 5) {lock.wait();} else {System.out.println("size == 5");System.out.println("t2 end");lock.notify();break;}}}} catch (Exception e) {e.printStackTrace();}}, "t2").start();}}
AwaitSignal
public class AwaitSignal {List lists = new ArrayList();public void add(Object o) { lists.add(o); }public int size() { return lists.size(); }static volatile boolean t2Start = false;public static void main(String[] args) throws InterruptedException {AwaitSignal c = new AwaitSignal();ReentrantLock lock = new ReentrantLock();Condition condition = lock.newCondition();new Thread(() -> {try {System.out.println("t1 start");for (int i = 0; i < 10; i++) {c.add(new Object());System.out.println("add " + i);lock.lock();if (c.size() == 5) {if (t2Start) {condition.signal();}condition.await();}lock.unlock();TimeUnit.SECONDS.sleep(1);}} catch (Exception e) {e.printStackTrace();}}, "t1").start();TimeUnit.SECONDS.sleep(7);new Thread(() -> {t2Start = true;try {System.out.println("t2 start");while (true) {lock.lock();if (c.size() != 5) {condition.await();} else {System.out.println("size == 5");System.out.println("t2 end");// 通知t1继续执行condition.signalAll();lock.unlock();break;}lock.unlock();}} catch (Exception e) {e.printStackTrace();}}, "t2").start();}}
2
写一个固定容量同步容器,拥有put和get方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用。
public class MyContainer2<T> {final private LinkedList<T> lists = new LinkedList<>();final private int MAX = 10; // 最多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();/*** 如果这个地方使用if不使用while,考虑一种情况* t1和t2两个线程在此等待,突然一个signal将他俩都唤醒了* t1抢到了资源,所以继续运行,又把队列加满了,然后释放了资源* t2终于获得了资源,但此时队列实际上满的,t2不应该去再往队列里加一个* 所以解决方案就是t2判断队列是不是满的,他发现是满的,就继续await*/while (lists.size() == MAX) {producer.await(); // 一定要注意,这个方法会释放临界资源}lists.add(t);++count;consumer.signalAll(); // 通知消费者线程进行消费} catch (InterruptedException 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 (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}return t;}public static void main(String[] args) {MyContainer2<String> c = new MyContainer2<>();// 启动消费者线程for (int i = 0; i < 10; 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();}}}
3
用线程顺序打印A1B2C3….Z26。
public class WaitNotify {private static volatile boolean t2Started = false;public static void main(String[] args) {final Object o = new Object();char[] aI = "1234567".toCharArray();char[] aC = "ABCDEFG".toCharArray();new Thread(() -> {try {synchronized (o) {if (!t2Started) {o.wait();}for (char c : aI) {System.out.print(c);o.notify();o.wait();}o.notify();}} catch (InterruptedException e) {e.printStackTrace();}}, "t1").start();new Thread(() -> {t2Started = true;try {synchronized (o) {for (char c : aC) {System.out.print(c);o.notify();o.wait();}o.notify();}} catch (InterruptedException e) {e.printStackTrace();}}, "t2").start();}}
