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 }
View Code

运行图

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 }
View Code

-----加入容器版

 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 }
View Code

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 }
View Code
 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 }
View Code

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 }
View Code

运行图

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 }
View Code

运行图

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 }
View Code

运行图

8、JUC

原文地址:https://www.cnblogs.com/Anemia-BOY/p/10857710.html