多线程---线程同步
说到线程同步就不得不说电影院卖票的案例:
- 某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
分析步骤:
-
-
在SellTicket类中重写run()方法实现卖票,代码步骤如下
-
判断票数大于0,就卖票,并告知是哪个窗口卖的
-
卖了票之后,总票数要减1
-
票卖没了,线程停止
-
定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下
-
创建SellTicket类的对象
-
创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称
-
1 public class SellTicket implements Runnable { 2 private int tickets = 100; 3 //在SellTicket类中重写run()方法实现卖票,代码步骤如下 4 @Override 5 public void run() { 6 while (true) { 7 if(ticket <= 0){ 8 //卖完了 9 break; 10 }else{ 11 try { 12 Thread.sleep(100); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 ticket--; 17 System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票"); 18 } 19 } 20 } 21 } 22 public class SellTicketDemo { 23 public static void main(String[] args) { 24 //创建SellTicket类的对象 25 SellTicket st = new SellTicket(); 26 27 //创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称 28 Thread t1 = new Thread(st,"窗口1"); 29 Thread t2 = new Thread(st,"窗口2"); 30 Thread t3 = new Thread(st,"窗口3"); 31 32 //启动线程 33 t1.start(); 34 t2.start(); 35 t3.start(); 36 } 37 }
卖票案例的问题:
- 相同的票出现了多次
- 出现了负数的票
synchronized(任意对象) {
多条语句操作共享数据的代码
}
1 public class SellTicket implements Runnable { 2 private int tickets = 100; 3 private Object obj = new Object(); 4 5 @Override 6 public void run() { 7 while (true) { 8 synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁 9 //t1进来后,就会把这段代码给锁起来 10 if (tickets > 0) { 11 try { 12 Thread.sleep(100); 13 //t1休息100毫秒 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 //窗口1正在出售第100张票 18 System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); 19 tickets--; //tickets = 99; 20 } 21 } 22 //t1出来了,这段代码的锁就被释放了 23 } 24 } 25 } 26 27 public class SellTicketDemo { 28 public static void main(String[] args) { 29 SellTicket st = new SellTicket(); 30 31 Thread t1 = new Thread(st, "窗口1"); 32 Thread t2 = new Thread(st, "窗口2"); 33 Thread t3 = new Thread(st, "窗口3"); 34 35 t1.start(); 36 t2.start(); 37 t3.start(); 38 } 39 }
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步静态方法的锁对象是:类名.class
1 public class MyRunnable implements Runnable { 2 private static int ticketCount = 100; 3 4 @Override 5 public void run() { 6 while(true){ 7 if("窗口一".equals(Thread.currentThread().getName())){ 8 //同步方法 9 boolean result = synchronizedMthod(); 10 if(result){ 11 break; 12 } 13 } 14 15 if("窗口二".equals(Thread.currentThread().getName())){ 16 //同步代码块 17 synchronized (MyRunnable.class){ 18 if(ticketCount == 0){ 19 break; 20 }else{ 21 try { 22 Thread.sleep(10); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 ticketCount--; 27 System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票"); 28 } 29 } 30 } 31 32 } 33 } 34 35 private static synchronized boolean synchronizedMthod() { 36 if(ticketCount == 0){ 37 return true; 38 }else{ 39 try { 40 Thread.sleep(10); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 ticketCount--; 45 System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票"); 46 return false; 47 } 48 } 49 }
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
- ReentrantLock构造方法
| 方法名 | 说明 |
| --------------- | -------------------- |
| ReentrantLock() | 创建一个ReentrantLock的实例 |
- 加锁解锁方法
| 方法名 | 说明 |
| ------------- | ---- |
| void lock() | 获得锁 |
| void unlock() | 释放锁 |
1 public class Ticket implements Runnable { 2 //票的数量 3 private int ticket = 100; 4 private Object obj = new Object(); 5 private ReentrantLock lock = new ReentrantLock(); 6 7 @Override 8 public void run() { 9 while (true) { 10 //synchronized (obj){//多个线程必须使用同一把锁. 11 try { 12 lock.lock(); 13 if (ticket <= 0) { 14 //卖完了 15 break; 16 } else { 17 Thread.sleep(100); 18 ticket--; 19 System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票"); 20 } 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } finally { 24 lock.unlock(); 25 } 26 // } 27 } 28 } 29 } 30 31 public class Demo { 32 public static void main(String[] args) { 33 Ticket ticket = new Ticket(); 34 35 Thread t1 = new Thread(ticket); 36 Thread t2 = new Thread(ticket); 37 Thread t3 = new Thread(ticket); 38 39 t1.setName("窗口一"); 40 t2.setName("窗口二"); 41 t3.setName("窗口三"); 42 43 t1.start(); 44 t2.start(); 45 t3.start(); 46 } 47 }
死锁
概念:
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行
产生死锁的情况:
代码如下:
1 public class Demo { 2 public static void main(String[] args) { 3 Object objA = new Object(); 4 Object objB = new Object(); 5 6 new Thread(()->{ 7 while(true){ 8 synchronized (objA){ 9 //线程一 10 synchronized (objB){ 11 System.out.println("小康同学正在走路"); 12 } 13 } 14 } 15 }).start(); 16 17 new Thread(()->{ 18 while(true){ 19 synchronized (objB){ 20 //线程二 21 synchronized (objA){ 22 System.out.println("小薇同学正在走路"); 23 } 24 } 25 } 26 }).start(); 27 } 28 }