JAVA进阶27(多线程/02)
1、线程同步
并发:同一个对象多个线程同步操作
1 package cn.Thread_demo; 2 3 /** 4 * @Classname SynBlockTest01 5 * @Description TODO 6 * @Date 2019-5-13 12:16 7 * @Created by Administrator 8 */ 9 public class SynBlockTest01 { 10 public static void main(String[] args) { 11 //一份资源 12 SynWeb12306 web = new SynWeb12306(); 13 //多个代理 14 new Thread(web, "张三").start(); 15 new Thread(web, "张四").start(); 16 new Thread(web, "张五").start(); 17 } 18 } 19 20 class SynWeb12306 implements Runnable { 21 //票数 22 private int ticketNums = 10; 23 private boolean flag = true; 24 25 @Override 26 public void run() { 27 while (flag) { 28 try { 29 Thread.sleep(100); 30 } catch (InterruptedException e) { 31 e.printStackTrace(); 32 } 33 test2(); 34 } 35 } 36 public synchronized void test2(){ 37 synchronized (this){ 38 if (ticketNums <= 0) { 39 flag = false; 40 return; 41 } 42 } 43 //模拟延时 44 try { 45 Thread.sleep(100); 46 } catch (InterruptedException e) { 47 e.printStackTrace(); 48 } 49 System.out.println(Thread.currentThread().getName()+"--"+ticketNums--); 50 } 51 52 //线程不安全, tickNums对象在变 53 public synchronized void test(){ 54 if (ticketNums <= 0) { 55 flag = false; 56 return; 57 } 58 //模拟延时 59 try { 60 Thread.sleep(200); 61 } catch (InterruptedException e) { 62 e.printStackTrace(); 63 } 64 System.out.println(Thread.currentThread().getName()+"--"+ticketNums--); 65 } 66 }
运行图
2、影院购票模拟
-----简单版
1 package cn.Thread_demo; 2 3 import javax.security.auth.login.AccountException; 4 5 /** 6 * @Classname HappyCinema 7 * @Description TODO 8 * @Date 2019-5-14 10:30 9 * @Created by Administrator 10 * <p> 11 * #####选位置 12 */ 13 public class HappyCinema { 14 public static void main(String[] args) { 15 Cinema c = new Cinema(2, "后天"); 16 new Thread(new Customer(c,4),"张三").start(); 17 new Thread(new Customer(c,2),"李四").start(); 18 } 19 } 20 21 //顾客 22 class Customer implements Runnable { 23 Cinema cinema; 24 int seats; 25 26 public Customer(Cinema cinema, int seats) { 27 this.cinema = cinema; 28 this.seats = seats; 29 } 30 31 @Override 32 public void run() { 33 synchronized (cinema) { 34 boolean flag = cinema.bookTickets(seats); 35 if (flag) { 36 System.out.println("出票成功" + Thread.currentThread().getName() + "位置为:" + seats); 37 } else { 38 System.out.println("出票失败" + Thread.currentThread().getName() + "位置为不够"); 39 } 40 } 41 } 42 } 43 44 //影院 45 class Cinema { 46 int available; //可供选择的位置 47 String name; //名称 48 49 public Cinema(int available, String name) { 50 this.available = available; 51 this.name = name; 52 } 53 54 //购票 55 public boolean bookTickets(int seats) { 56 System.out.println("可用位置为:" + available); 57 if (seats > available) { 58 return false; 59 } 60 available -= seats; 61 return true; 62 } 63 }
-----加入容器版
1 package cn.Thread_demo; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * @Classname HappyCinema 8 * @Description TODO 9 * @Date 2019-5-14 10:30 10 * @Created by Administrator 11 * <p> 12 * #####选位置 13 */ 14 public class HappyCinema01 { 15 public static void main(String[] args) { 16 //可用位置 17 List<Integer> available = new ArrayList<>(); 18 available.add(1); 19 available.add(2); 20 available.add(4); 21 available.add(5); 22 available.add(7); 23 24 //顾客需要的位置 25 List<Integer> seats1 = new ArrayList<>(); 26 seats1.add(1); 27 seats1.add(2); 28 List<Integer> seats2 = new ArrayList<>(); 29 seats2.add(3); 30 seats2.add(7); 31 HtCinema c = new HtCinema(available,"HtCinema"); 32 new Thread(new HappyCustomer(c, seats1), "张三").start(); 33 new Thread(new HappyCustomer(c, seats2), "李四").start(); 34 } 35 } 36 37 //顾客 38 class HappyCustomer implements Runnable { 39 HtCinema cinema; 40 List<Integer> seats; 41 42 public HappyCustomer(HtCinema cinema, List<Integer> seats) { 43 this.cinema = cinema; 44 this.seats = seats; 45 } 46 47 @Override 48 public void run() { 49 synchronized (cinema) { 50 boolean flag = cinema.bookTickets(seats); 51 if (flag) { 52 System.out.println("出票成功" + Thread.currentThread().getName() + "位置为:" + seats); 53 } else { 54 System.out.println("出票失败" + Thread.currentThread().getName() + "位置为不够"); 55 } 56 } 57 } 58 } 59 60 //影院 61 class HtCinema { 62 List<Integer> available; //可供选择的位置 63 String name; //名称 64 65 public HtCinema(List<Integer> available, String name) { 66 this.available = available; 67 this.name = name; 68 } 69 70 //购票 71 public boolean bookTickets(List<Integer> seats) { 72 System.out.println("可用位置为:" + available); 73 List<Integer> copy = new ArrayList<>(); 74 copy.addAll(available); 75 76 //相减 77 copy.retainAll(seats); 78 //判断大小 79 if (available.size()-copy.size()!=seats.size()){ 80 return false; 81 } 82 //成功 83 available = copy; 84 return true; 85 } 86 }
3、死锁
过多的同步可能造成相互不释放资源。从而相互等待,一般发生于同步中持有多个对象的锁。
4、线程协作
并发协作模型:“生产者/消费者模式”---管程法、信号灯法
生产者:负责生产数据的模块(这里的模块可能是:方法、对象、线程、进程)
消费者:负责处理数据的模块(这里的模块可能是:方法、对象、线程、进程)
缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”;生产者将生产好的数据放入“缓冲区”,消费者从“缓冲区”拿要处理的数据。
1 package cn.Thread_demo; 2 3 /** 4 * @Classname CoTest01 5 * @Description TODO 6 * @Date 2019-5-15 14:16 7 * @Created by Administrator 8 * 协作模型:生产者消费者实现方式一:管程法 9 */ 10 public class CoTest01 { 11 public static void main(String[] args) { 12 SynContainer container = new SynContainer(); 13 new Productor(container).start(); 14 new Consumer(container).start(); 15 } 16 } 17 18 //生产者 19 class Productor extends Thread { 20 SynContainer container; 21 22 public Productor(SynContainer container) { 23 this.container = container; 24 } 25 26 @Override 27 public void run() { 28 // 生产 29 for (int i = 0; i < 100; i++) { 30 System.out.println("生产--" + i + "个"); 31 container.push(new Steamedbun(i)); 32 } 33 } 34 } 35 36 //消费者 37 class Consumer extends Thread { 38 SynContainer container; 39 40 public Consumer(SynContainer container) { 41 this.container = container; 42 } 43 44 @Override 45 public void run() { 46 // 消费 47 for (int i = 0; i < 100; i++) { 48 System.out.println("消费--" + container.pop().id + "个"); 49 container.push(new Steamedbun(i)); 50 } 51 } 52 } 53 54 //缓冲区 55 class SynContainer { 56 Steamedbun[] buns = new Steamedbun[10]; //存储容器 57 int count = 0; //计数器 58 59 // 存储 / 生产 60 public synchronized void push(Steamedbun bun) { 61 // 何时能生产--- 容器存在空间 62 // 不能生产---等待 63 if (count==buns.length){ 64 try { 65 this.wait(); //线程阻塞,消费者通知生产解除 66 } catch (InterruptedException e) { 67 e.printStackTrace(); 68 } 69 } 70 // 存在空间--- 可以生产 71 buns[count] = bun; 72 count++; 73 this.notifyAll(); 74 } 75 76 // 获取 / 消费 77 public synchronized Steamedbun pop() { 78 // 何时消费,容器中是否存在数据 79 // 没有数据---等待 80 if (count==0){ 81 try { 82 this.wait(); //线程阻塞。生产者通知消费时解除 83 } catch (InterruptedException e) { 84 e.printStackTrace(); 85 } 86 } 87 // 存在数据---可以消费 88 count--; 89 Steamedbun bun = buns[count]; 90 this.notifyAll(); 91 return bun; 92 } 93 } 94 95 //数据 96 class Steamedbun { 97 int id; 98 99 public Steamedbun(int id) { 100 this.id = id; 101 } 102 }
1 package cn.Thread_demo; 2 3 /** 4 * @Classname CoTest02 5 * @Description TODO 6 * @Date 2019-5-15 14:48 7 * @Created by Administrator 8 * 协作模型:生产者消费者实现方式一:信号灯法 9 * 借助标志位 10 */ 11 public class CoTest02 { 12 public static void main(String[] args) { 13 Tv tv = new Tv(); 14 new Player(tv).start(); 15 new Watch(tv).start(); 16 } 17 } 18 19 //生产者--演员 20 class Player extends Thread { 21 Tv tv; 22 23 public Player(Tv tv) { 24 this.tv = tv; 25 } 26 27 @Override 28 public void run() { 29 for (int i = 0; i < 20; i++) { 30 if (i % 2 == 0) { 31 this.tv.play("奇葩说"); 32 } else { 33 this.tv.play("hello world"); 34 } 35 } 36 } 37 } 38 39 //消费者--观众 40 class Watch extends Thread { 41 Tv tv; 42 43 public Watch(Tv tv) { 44 this.tv = tv; 45 } 46 47 @Override 48 public void run() { 49 for (int i = 0; i < 20; i++) { 50 tv.watch(); 51 } 52 } 53 } 54 55 //同一个资源--电视 56 class Tv { 57 String voice; 58 //信号灯 59 //T 表示演员表演 观众等待 60 //F 表示观众观看 演员等待 61 boolean flag = true; 62 63 //表演 64 public synchronized void play(String voice) { 65 //演员等待 66 if (!flag) { 67 try { 68 this.wait(); 69 } catch (InterruptedException e) { 70 e.printStackTrace(); 71 } 72 } 73 System.out.println("表演了" + voice); 74 this.voice = voice; 75 //唤醒 76 this.notifyAll(); 77 this.flag = !this.flag; //切换标志 78 } 79 80 //观看 81 public synchronized void watch() { 82 //观众等待 83 if (flag) { 84 try { 85 this.wait(); 86 } catch (InterruptedException e) { 87 e.printStackTrace(); 88 } 89 } 90 //观看 91 System.out.println("听到了" + voice); 92 //唤醒 93 this.notifyAll(); 94 this.flag = !this.flag; //切换标志 95 } 96 }
5、单例模式
1 package cn.Thread_demo; 2 3 /** 4 * @Classname DoubleCheckedLocking 5 * @Description TODO 6 * @Date 2019-5-16 10:36 7 * @Created by Administrator 8 * ---单例模式:套路,在多线程环境下,对外存在一个对象 9 * 1、构造器私有化--避免外部new构造器 10 * 2、提供私有的静态属性--存储对象的地址 11 * 3、提供公共的静态方法--获取属性 12 */ 13 public class DoubleCheckedLocking { 14 // 1、构造器私有化 15 private DoubleCheckedLocking() { 16 17 } 18 19 // 2、提供私有的静态属性(没有volatile,其他线程可能访问一个没有初始化的对象) 20 private static volatile DoubleCheckedLocking instance; 21 22 // 3、提供公共的静态方法 23 public static DoubleCheckedLocking getInstance() { 24 //再次检测 25 if (null != instance) { //避免不必要的同步,已经存在对象 26 return instance; 27 } 28 synchronized (DoubleCheckedLocking.class) { 29 if (null == instance) { 30 instance = new DoubleCheckedLocking(); 31 } 32 return instance; 33 } 34 } 35 36 public static void main(String[] args) { 37 Thread t = new Thread(() -> { 38 System.out.println(DoubleCheckedLocking.getInstance()); 39 }); 40 t.start(); 41 System.out.println(DoubleCheckedLocking.getInstance()); 42 } 43 }
运行图
6、ThreadLocal
每个线程存储自己的数据,更改不会影响其他线程
1 package cn.Thread_demo; 2 3 /** 4 * @Classname ThreadLocalTest01 5 * @Description TODO 6 * @Date 2019-5-16 14:17 7 * @Created by Administrator 8 * ---ThreadLocal:每个线程自身的存储区域(本地、局部) 9 * get/set/initialValue 10 */ 11 public class ThreadLocalTest01 { 12 // private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); 13 // 更改初始化值 14 private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->200); 15 public static void main(String[] args) { 16 //获取值 17 System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); 18 //设置值 19 threadLocal.set(199); 20 System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); 21 new Thread(new MyRun()).start(); 22 } 23 public static class MyRun implements Runnable{ 24 @Override 25 public void run() { 26 threadLocal.set((int)(Math.random()*99)); 27 System.out.println(Thread.currentThread().getName() + "-->" + threadLocal.get()); 28 } 29 } 30 }
运行图
7、可重入锁:锁可以延续使用
1 package cn.Thread_demo; 2 3 /** 4 * @Classname LockTest 5 * @Description TODO 6 * @Date 2019-5-16 15:14 7 * @Created by Administrator 8 * 可重入锁:锁可以延续使用 9 */ 10 public class LockTest { 11 public void test(){ 12 //第一次获得锁 13 synchronized (this){ 14 while (true){ 15 //第二次获得同样的锁 16 synchronized (this){ 17 System.out.println("ReentrantLock!"); 18 } 19 try { 20 Thread.sleep(1000); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 } 26 } 27 28 public static void main(String[] args) { 29 new LockTest().test(); 30 } 31 }
运行图
8、JUC