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