1.采用synchronized解决线程并发问题
public class SaleTickDemo01 {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() -> {for (int i = 0; i < 60; i++) {ticket.sale();}}, "A").start();new Thread(() -> {for (int i = 0; i < 60; i++) {ticket.sale();}}, "B").start();new Thread(() -> {for (int i = 0; i < 60; i++) {ticket.sale();}}, "C").start();}}class Ticket {private int number = 50;// synchronizedpublic void sale() {if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);}}}
没有加synchronized执行的结果如下:
给sale方法添加synchronized后的结果
2.采用Lock解决线程并发问题
public class SaleTickDemo02 {public static void main(String[] args) {// 并发:多线程操作同一个资源类,把资源类丢入线程Ticket2 ticket = new Ticket2();new Thread(() -> {for (int i = 0; i < 60; i++) ticket.sale();} , "A").start();new Thread(() -> {for (int i = 0; i < 60; i++) ticket.sale();} , "B").start();new Thread(() -> {for (int i = 0; i < 60; i++) ticket.sale();} , "C").start();}}// Lock三部曲// 1.Lock lock = new ReentrantLock();// 2.lock.lock(); 加锁// 3.lock.unlock(); 解锁class Ticket2 {private int number = 50;Lock lock = new ReentrantLock();public void sale() {lock.lock();try {if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}
3.传统消费者生产者问题
3.1 synchronized
/*** 线程中间的通讯问题:生产者和消费者问题!* 线程交替执行 A V 操作同一个变量 num = 0* A num+1* B num-1*/public class TackA {public static void main(String[] args) {Data data = new Data();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}}class Data {private int number = 0;//+1public synchronized void increment() throws InterruptedException {if (number != 0) {this.wait();}number++;System.out.println(Thread.currentThread().getName() + "=>" + number);// 通知其它线程,我+1完毕了this.notifyAll();}//-1public synchronized void decrement() throws InterruptedException {if (number == 0) {this.wait();}number--;System.out.println(Thread.currentThread().getName() + "=>" + number);// 通知其它线程,我-1完毕了this.notifyAll();}}
如上这段代码使用if的话会引发 虚假唤醒 ,等待应该总是出现在循环中
将if换成while后代码执行结果正常
3.2 Lock
/*** 线程中间的通讯问题:生产者和消费者问题!* 线程交替执行 A V 操作同一个变量 num = 0* A num+1* B num-1*/public class LockPc {public static void main(String[] args) {Data2 data = new Data2();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}}class Data2 {private int number = 0;Lock lock = new ReentrantLock();Condition condition = lock.newCondition();//+1public void increment() throws InterruptedException {lock.lock();try {while (number != 0) {condition.await();}number++;System.out.println(Thread.currentThread().getName() + "=>" + number);// 通知其它线程,我+1完毕了condition.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}//-1public void decrement() throws InterruptedException {lock.lock();try {while (number == 0) {condition.await();}number--;System.out.println(Thread.currentThread().getName() + "=>" + number);// 通知其它线程,我-1完毕了condition.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}
3.3 Lock 精准通知实现一条生产线
A->B->C->A 形成一个循环的通知
/*** 线程中间的通讯问题:生产者和消费者问题!* 线程交替执行 A B 操作同一个变量 num = 0* A->B->C->A 形成一个循环的通知*/public class LockPcOrder {public static void main(String[] args) {Data3 data3 = new Data3();new Thread(() -> {for (int i = 0; i < 10; i++) {data3.printA();}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {data3.printB();}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {data3.printC();}}, "C").start();}}class Data3 {private int number = 1;Lock lock = new ReentrantLock();Condition condition1 = lock.newCondition();Condition condition2 = lock.newCondition();Condition condition3 = lock.newCondition();Condition condition4 = lock.newCondition();public void printA() {lock.lock();try {// 业务,判断->执行->通知while (number != 1) {// 等待condition1.await();}System.out.println(Thread.currentThread().getName() + "=>AAAAAAAA");// 唤醒指定的人,Bnumber = 2;condition2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void printB() {lock.lock();try {// 业务,判断->执行->通知while (number != 2) {// 等待condition2.await();}System.out.println(Thread.currentThread().getName() + "=>BBBBBBB");// 唤醒指定的人,Cnumber = 3;condition3.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void printC() {lock.lock();try {// 业务,判断->执行->通知while (number != 3) {// 等待condition3.await();}System.out.println(Thread.currentThread().getName() + "=>CCCCCCC");// 唤醒指定的人,Anumber = 1;condition1.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}
4.八大锁现象
问题1:请问先打印 发短信还是打电话
答:两个方法用的是同一个锁,谁先拿到执行谁
问题2:sendSms加了4秒睡眠后 先打印 发短信还是打电话
答:两个方法用的是同一个锁,谁先拿到执行谁
public class Test1 {public static void main(String[] args) {Phone phone = new Phone();new Thread(phone::sendSms, "A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(phone::call, "B").start();}}class Phone {// synchronize 锁的对象是方法的调用者// 两个方法用的是同一个锁,谁先拿到执行谁synchronized void sendSms() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}synchronized void call() {System.out.println("打电话");}}
问题3:先执行hello还是 打短信?hello是普通方法
答:先打印hello,因为它不是同步方法,不受锁的影响
public class Test2 {public static void main(String[] args) {Phone2 phone = new Phone2();new Thread(phone::sendSms, "A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(phone::hello, "B").start();}}class Phone2 {// synchronize 锁的对象是方法的调用者// 两个方法用的是同一个锁,谁先拿到执行谁synchronized void sendSms() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}synchronized void call() {System.out.println("打电话");}// 这里没有锁!不是同步方法,不受锁的影响void hello(){System.out.println("hello");}}
问题4:两个Phone的情况下先执行sendSms还是call
答:两把锁根据时间来所以先调用call,因为phone的sendSms等待了4秒,所以phone1先获得锁
public class Test2 {public static void main(String[] args) {// 两个对象,两个调用者,两把锁!Phone2 phone = new Phone2();Phone2 phone1 = new Phone2();new Thread(phone::sendSms, "A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(phone1::call, "B").start();}}class Phone2 {// synchronize 锁的对象是方法的调用者// 两个方法用的是同一个锁,谁先拿到执行谁synchronized void sendSms() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}synchronized void call() {System.out.println("打电话");}// 这里没有锁!不是同步方法,不受锁的影响void hello(){System.out.println("hello");}}
问题5:增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话?
答:Phone2只会有一个class对象,所以谁先调用就给谁锁,sendSms->call
public class Test2 {public static void main(String[] args) {Phone2 phone = new Phone2();new Thread(()->{phone.sendSms();} ,"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{phone.call();}, "B").start();}}class Phone2 {// synchronize 锁的对象是方法的调用者// static 静态方法// 嘞一加载就有了!锁的是classstatic synchronized void sendSms() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}static synchronized void call() {System.out.println("打电话");}// 这里没有锁!不是同步方法,不受锁的影响void hello(){System.out.println("hello");}}
问题6:两个静态的同步方法,两个对象,先打印 发短信?打电话?
答:Phone2只会有一个class对象,所以谁先调用就给谁锁,sendSms->call
public class Test3 {public static void main(String[] args) {Phone3 phone = new Phone3();Phone3 phone1 = new Phone3();new Thread(()->{phone.sendSms();} ,"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{phone1.call();}, "B").start();}}// Phone3 唯一的一个class对象class Phone3 {// synchronize 锁的对象是方法的调用者// static 静态方法// 类一加载就有了!锁的是classstatic synchronized void sendSms() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}static synchronized void call() {System.out.println("打电话");}// 这里没有锁!不是同步方法,不受锁的影响void hello(){System.out.println("hello");}}
问题7:一个静态同步方法,一个普通同步方法,一个对象,发短信?打电话
答:静态的同步方法锁的是class的模板,普通的同步方法,锁的调用者 call->sendSms
问题8:一个静态同步方法,一个普通同步方法,两个个对象,发短信?打电话
答:静态的同步方法锁的是class的模板,普通的同步方法,锁的调用者 call->sendSms
public class Test4 {public static void main(String[] args) {Phone4 phone = new Phone4();new Thread(()->{phone.sendSms();} ,"A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{phone.call();}, "B").start();}}// Phone4 唯一的一个class对象class Phone4 {// 静态的同步方法锁的是class的模板static synchronized void sendSms() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 普通的同步方法,锁的调用者static void call() {System.out.println("打电话");}}
5.集合类不安全
5.1 ArrayList
执行如下这段代码会发生java.util.ConcurrentModificationException(并发异常)
public class ListTest {public static void main(String[] args) {// 并发下 ArrayList 不安全的吗,Synchronized;/** 解决方案;* 1、List<String> list = new Vector<>();* 2、List<String> list = Collections.synchronizedList(new ArrayList<>());* 3、List<String> list = new CopyOnWriteArrayList<>();*/List<String> list = new ArrayList<>();for (int i = 0; i < 20; i++) {new Thread(() -> {list.add(UUID.randomUUID().toString().substring(0, 5));System.out.println(list);}, String.valueOf(i)).start();}}}
5.2 HashSet
执行如下这段代码会发生java.util.ConcurrentModificationException(并发异常)
HashSet底层是调用HashMap,add set本质就是map key是无法重复的
public class SetTest {public static void main(String[] args) {// hashmap// Set<String> set = Collections.synchronizedSet(new HashSet<>());// Set<String> set = new CopyOnWriteArraySet<>();Set<String> set = new HashSet<>();for (int i = 0; i < 20; i++) {new Thread(() -> {set.add(UUID.randomUUID().toString().substring(0, 5));System.out.println(set);}, String.valueOf(i)).start();}}}
5.3 HashMap
换成new HashMap<>()执行如下这段代码会发生java.util.ConcurrentModificationException(并发异常)
public class MapTest {public static void main(String[] args) {// map 是这样用的吗? 不是,工作中不用 HashMap// 默认等价于什么? new HashMap<>(16,0.75);// Map<String, String> map = new HashMap<>();Map<String, String> map = new ConcurrentHashMap<>();for (int i = 1; i <=30; i++) {new Thread(()->{map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));System.out.println(map);},String.valueOf(i)).start();}}}
6.Callable 可获得执行任务返回值
public class CallableTest {public static void main(String[] args) throws ExecutionException, InterruptedException {// new Thread(new Runnable()).start();// new Thread(new FutureTask<V>()).start();// new Thread(new FutureTask<V>( Callable )).start();new Thread().start(); // 怎么启动CallableMyThread thread = new MyThread();FutureTask<Integer> futureTask = new FutureTask<>(thread); // 适配类new Thread(futureTask,"A").start();new Thread(futureTask,"B").start(); // 结果会被缓存,效率高Integer o = futureTask.get(); //这个get 方法可能会产生阻塞!把他放到最后// 或者使用异步通信来处理!System.out.println(o);}}class MyThread implements Callable<Integer> {@Overridepublic Integer call() {System.out.println("call()"); // 会打印几个call// 耗时的操作return 1024;}}
7.CountDownLatch 线程减法计数器
线程减法计数器,需要全部线程执行完后再输入出Close Door
// 计数器public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {// 总数是6,必须要执行任务的时候,再使用!CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <=6 ; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName()+" Go out");countDownLatch.countDown(); // 数量-1},String.valueOf(i)).start();}countDownLatch.await(); // 等待计数器归零,然后再向下执行System.out.println("Close Door");}}
8.CyclicBarrier 线程加法计数器
线程加法计数器,线程数到8时才打印内容
public class CyclicBarrierDemo {public static void main(String[] args) {/** 集齐7颗龙珠召唤神龙*/// 召唤龙珠的线程CyclicBarrier cyclicBarrier = new CyclicBarrier(8, () -> {System.out.println("召唤神龙成功!");});for (int i = 1; i <= 7; i++) {final int temp = i;// lambda能操作到 i 吗new Thread(() -> {System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠");try {cyclicBarrier.await(); // 等待} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}).start();}}}
9.Semaphore 限流
public class SemaphoreDemo {public static void main(String[] args) {// 线程数量:停车位! 限流!Semaphore semaphore = new Semaphore(3);for (int i = 1; i <=6 ; i++) {new Thread(()->{// acquire() 得到try {semaphore.acquire();System.out.println(Thread.currentThread().getName()+"抢到车位");TimeUnit.SECONDS.sleep(2);System.out.println(Thread.currentThread().getName()+"离开车位");} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release(); // release() 释放}},String.valueOf(i)).start();}}}
10.ReadWriteLock 读写锁
/*** 独占锁(写锁) 一次只能被一个线程占有* 共享锁(读锁) 多个线程可以同时占有* ReadWriteLock* 读-读 可以共存!* 读-写 不能共存!* 写-写 不能共存!*/public class ReadWriteLockDemo {public static void main(String[] args) {MyCacheLock myCache = new MyCacheLock();// MyCache myCache = new MyCache();// 写入for (int i = 1; i <= 5 ; i++) {final int temp = i;new Thread(()->{myCache.put(temp+"",temp+"");},String.valueOf(i)).start();}// 读取for (int i = 1; i <= 5 ; i++) {final int temp = i;new Thread(()->{myCache.get(temp+"");},String.valueOf(i)).start();}}}// 加锁的class MyCacheLock{private volatile Map<String,Object> map = new HashMap<>();// 读写锁: 更加细粒度的控制private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private Lock lock = new ReentrantLock();// 存,写入的时候,只希望同时只有一个线程写public void put(String key,Object value){readWriteLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName()+"写入"+key);map.put(key,value);System.out.println(Thread.currentThread().getName()+"写入OK");} catch (Exception e) {e.printStackTrace();} finally {readWriteLock.writeLock().unlock();}}// 取,读,所有人都可以读!public void get(String key){readWriteLock.readLock().lock();try {System.out.println(Thread.currentThread().getName()+"读取"+key);Object o = map.get(key);System.out.println(Thread.currentThread().getName()+"读取OK");} catch (Exception e) {e.printStackTrace();} finally {readWriteLock.readLock().unlock();}}}/*** 自定义缓存*/class MyCache{private volatile Map<String,Object> map = new HashMap<>();// 存,写public void put(String key,Object value){System.out.println(Thread.currentThread().getName()+"写入"+key);map.put(key,value);System.out.println(Thread.currentThread().getName()+"写入OK");}// 取,读public void get(String key){System.out.println(Thread.currentThread().getName()+"读取"+key);Object o = map.get(key);System.out.println(Thread.currentThread().getName()+"读取OK");}}
加锁与没加锁效果对比:

11.
