线程安全

多线程的并发执行可以提高程序运行的效率,但当多个线程去处理同一个资源时,就容易产生一些安全问题。如模拟抢票程序,如果不加线程安全处理的话,就很容易多个线程抢到同一张票,或者出现余票为负数的情况。

synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,synchronized块或synchronized方法中的内容不被多个线程同时执行,确保我们数据的完整性。(在run方法里使用synchronized块,或在实现多线程方法里新建synchronized方法)

案例一,抢票程序

 1 package cn.ftf.threadsafe;
 2 
 3 public class SafeTest01 {
 4     public static void main(String[] args) {
 5         Safe12306 safe=new Safe12306();
 6     Thread th=new Thread(safe);
 7     Thread th1=new Thread(safe);
 8     Thread th2=new Thread(safe);
 9     th1.start();
10     th2.start();
11     th.start();
12     
13     }
14 }
15 class Safe12306 implements Runnable{
16         private int i=10;
17         private boolean flag=true;
18         public void run() {
19             while(flag)
20             test();
21     }
22         public synchronized void test() {  //synchronized方法,为提高效率,最好使用synchronized块
23             if(i<=0) {
24                 flag=false;
25                 return;    
26             }
27                 try {
28                     Thread.sleep(200);
29                 } catch (InterruptedException e) {
30                     e.printStackTrace();
31                 }
32                 System.out.println(i--);
33         }    
34         
35         //优化,尽可能锁定合理的范围,是指数据的完整性
36         public void test2() {
37             if(i<=0) {    //考虑的是没有票的情况
38                 flag=false;
39                 return;
40             }
41             synchronized (this) {
42                 if(i<=0) {    //多重检测。//考虑的是最后一张票的情况
43                     flag=false;
44                     return;
45                     
46                 }
47                 try {
48                     Thread.sleep(200);
49                 } catch (InterruptedException e) {
50                     e.printStackTrace();
51                 }
52                 System.out.println(i--);
53         }    
54     }        
55         
56 }

案例二,多人操作一个账户取钱

 1 package cn.ftf.threadsafe;
 2 /**
 3  *多人操作同一账户取钱 取钱
 4  * @author 房廷飞
 5  *
 6  */
 7 public class SafeTest02 {
 8     public static void main(String[] args) {
 9         Account acc=new Account(100, "礼金");
10         Drawing you=new Drawing(acc, 80);
11         Drawing she=new Drawing(acc, 90);
12         new Thread(you,"可悲的你").start();
13         new Thread(she,"happy的她").start();
14     }
15 
16 }
17 class Account{
18     int money;
19     String name;
20     public Account(int money, String name) {
21         super();
22         this.money = money;
23         this.name = name;
24     }
25 }
26 //模拟取款
27 class Drawing implements Runnable{
28     Account account;
29     int drawMoney;  //取地钱数
30     int packetTotal;  //口袋的钱数
31 
32     public Drawing(Account account, int drawMoney) {
33         super();
34         this.account = account;
35         this.drawMoney = drawMoney;
36     }
37 
38     @Override
39     public void run() {
40         synchronized (account) {
41             if(account.money-drawMoney>0) {
42                 account.money-=drawMoney;
43                 packetTotal+=drawMoney;
44                 System.out.println(Thread.currentThread().getName()+"账户余额"+account.money);
45                 System.out.println(Thread.currentThread().getName()+"口袋里的钱"+packetTotal);        
46             }else {
47                 System.out.println(Thread.currentThread().getName()+"取款失败,余额不足!");
48             }
49             }
50     }
51 }
52     

案例三  操作容器,多线程向容器里添加数据会有数据丢失或覆盖的情况

 1 package cn.ftf.threadsafe;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * 操作容器,多线程向容器里添加数据会有数据丢失或覆盖的情况
 8  * @author 房廷飞
 9  *
10  */
11 public class UnsafeTest03 {
12     public static void main(String[] args) throws InterruptedException {
13         List<String> list=new ArrayList();
14         for(int i=0;i<10000;i++) {
15             new Thread(()->{
16                 synchronized (list) {
17                     list.add(Thread.currentThread().getName());
18                 }
19             }).start();
20         }
21         Thread.sleep(2000);
22         System.out.println(list.size());
23     }
24 
25 }

涉及多线程读写的容器还可以使用CopyOnWriteArrayList类来创建容器,其内部维护了一个安全的用于线程操作的容器,也是用的synchronized代码块实现的容器安全。

 1 import java.util.ArrayList;
 2 import java.util.concurrent.CopyOnWriteArrayList;
 3 import java.util.List;
 4 
 5 public class SynContainer {
 6     public static void main(String[] args) throws InterruptedException {
 7         CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
 8         for(int i=0;i<10000;i++) {
 9             new Thread(()->{
10                 list.add(Thread.currentThread().getName());
11             }) .start();
12         }
13         Thread.sleep(10000);
14         System.out.println(list.size());
15     }
16 }

 

下面写一个多线程(多用户)买电影票的程序

包内包含三个文件,影院类,用户类和测试类:Cinema.java  ,  Person.java  ,  Test.java

 

 

 

 

 Cinema.java :共同操作的对象

 1 package cn.ftf.cinema;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class Cinema{
 7     public List<Integer> getTicket() {
 8         return ticket;
 9     }
10     public void setTicket(List<Integer> ticket) {
11         this.ticket = ticket;
12     }
13     private List<Integer> ticket;
14     private String name;
15     public Cinema(List<Integer> ticket, String name) {
16         super();
17         this.ticket = ticket;
18         this.name = name;
19         System.out.println(name+"欢迎你!现有余座:"+ticket.toString());
20     }
21     public boolean sell(List needTicket){
22         List<Integer> copy=new ArrayList<Integer>();
23         copy.addAll(ticket);
24         
25         copy.removeAll(needTicket);
26         if(ticket.size()-copy.size()!=needTicket.size()) {
27             return false;
28         }
29         ticket=copy;
30         return true;
31     }
32 }

Person.java  实现多线程的对象

 1 package cn.ftf.cinema;
 2 
 3 import java.util.List;
 4 
 5 public class Person implements Runnable{
 6     Cinema ci;
 7     private String pname;
 8     private List needTicket;
 9     public Person(Cinema ci, String pname, List  needTicket) {
10         super();
11         this.ci = ci;
12         this.pname = pname;
13         this.needTicket = needTicket;
14     }
15     @Override
16     public void run() {
17         synchronized (ci) {
18             boolean flag=ci.sell(needTicket);
19             if(flag) {
20                 System.out.println(pname+"购票成功,座位:"+needTicket);
21                 
22             }else {
23                 System.out.println(pname+"购票失败,余票不足!");
24             }
25         }
26     }
27 }

Test.java    测试类

 1 package cn.ftf.cinema;
 2 
 3 import java.util.List;
 4 
 5 public class Person implements Runnable{
 6     Cinema ci;
 7     private String pname;
 8     private List needTicket;
 9     public Person(Cinema ci, String pname, List  needTicket) {
10         super();
11         this.ci = ci;
12         this.pname = pname;
13         this.needTicket = needTicket;
14     }
15     @Override
16     public void run() {
17         synchronized (ci) {
18             boolean flag=ci.sell(needTicket);
19             if(flag) {
20                 System.out.println(pname+"购票成功,座位:"+needTicket);
21                 
22             }else {
23                 System.out.println(pname+"购票失败,余票不足!");
24             }
25         }
26     }
27 }

其中还包含了集合类List的部分使用,增删,复制等

 

posted @ 2019-07-27 17:17  codeFlyer  阅读(191)  评论(0)    收藏  举报