线程安全
多线程的并发执行可以提高程序运行的效率,但当多个线程去处理同一个资源时,就容易产生一些安全问题。如模拟抢票程序,如果不加线程安全处理的话,就很容易多个线程抢到同一张票,或者出现余票为负数的情况。
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的部分使用,增删,复制等