Java基础复习 Day 20
多线程(3)
-
多线程出现安全问题
当多线程访问(有写操作,更改共享数据的值的时候)共享数据的时候,会产生线程安全问题
以卖票案例为例做代码实现:(三个窗口同时卖100张1-100号的票,就会出现安全问题,同一张票可能会被卖多次,或者出现没有票了却还在被卖。。)
//创建Runnable接口的实现类,定义共享数据
/*
* 实现卖票案例
* */
public class RunnableImpl implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+" 正在卖第: "+ ticket +"张票");
ticket--;
}
}
}
}
//定义三个线程去访问这个共享数据
/*
* 创建三个线程让这三个线程都对共享的票进行出售
* 共享体现在只有一个runnable的实现类,但是用三个线程来执行
* */
public class DemoMain {
public static void main(String[] args) {
RunnableImpl run = new RunnableImpl();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
打印结果
Thread-2 正在卖第: 100张票
Thread-0 正在卖第: 100张票
Thread-1 正在卖第: 100张票
Thread-1 正在卖第: 97张票
Thread-0 正在卖第: 97张票
Thread-2 正在卖第: 97张票
Thread-2 正在卖第: 94张票
Thread-1 正在卖第: 94张票
Thread-0 正在卖第: 94张票
Thread-0 正在卖第: 91张票
Thread-2 正在卖第: 91张票
Thread-1 正在卖第: 91张票
Thread-1 正在卖第: 88张票
Thread-2 正在卖第: 88张票
Thread-0 正在卖第: 88张票
Thread-2 正在卖第: 85张票
Thread-0 正在卖第: 85张票
Thread-1 正在卖第: 85张票
Thread-1 正在卖第: 82张票
Thread-2 正在卖第: 82张票
Thread-0 正在卖第: 82张票
Thread-2 正在卖第: 79张票
Thread-1 正在卖第: 79张票
Thread-0 正在卖第: 79张票
Thread-1 正在卖第: 76张票
Thread-0 正在卖第: 76张票
Thread-2 正在卖第: 76张票
Thread-2 正在卖第: 73张票
Thread-0 正在卖第: 73张票
Thread-1 正在卖第: 73张票
Thread-2 正在卖第: 70张票
Thread-0 正在卖第: 70张票
Thread-1 正在卖第: 70张票
Thread-1 正在卖第: 67张票
Thread-0 正在卖第: 67张票
Thread-2 正在卖第: 67张票
Thread-0 正在卖第: 64张票
Thread-2 正在卖第: 64张票
Thread-1 正在卖第: 64张票
Thread-0 正在卖第: 61张票
Thread-1 正在卖第: 61张票
Thread-2 正在卖第: 61张票
Thread-1 正在卖第: 58张票
Thread-0 正在卖第: 58张票
Thread-2 正在卖第: 58张票
Thread-0 正在卖第: 55张票
Thread-2 正在卖第: 54张票
Thread-1 正在卖第: 54张票
Thread-2 正在卖第: 52张票
Thread-1 正在卖第: 52张票
Thread-0 正在卖第: 52张票
Thread-2 正在卖第: 49张票
Thread-1 正在卖第: 49张票
Thread-0 正在卖第: 48张票
Thread-1 正在卖第: 46张票
Thread-2 正在卖第: 45张票
Thread-0 正在卖第: 46张票
Thread-0 正在卖第: 43张票
Thread-2 正在卖第: 43张票
Thread-1 正在卖第: 43张票
Thread-1 正在卖第: 40张票
Thread-2 正在卖第: 40张票
Thread-0 正在卖第: 40张票
Thread-0 正在卖第: 37张票
Thread-2 正在卖第: 37张票
Thread-1 正在卖第: 37张票
Thread-1 正在卖第: 34张票
Thread-0 正在卖第: 33张票
Thread-2 正在卖第: 32张票
Thread-1 正在卖第: 31张票
Thread-0 正在卖第: 31张票
Thread-2 正在卖第: 30张票
Thread-2 正在卖第: 28张票
Thread-1 正在卖第: 28张票
Thread-0 正在卖第: 28张票
Thread-0 正在卖第: 25张票
Thread-2 正在卖第: 25张票
Thread-1 正在卖第: 25张票
Thread-2 正在卖第: 22张票
Thread-1 正在卖第: 22张票
Thread-0 正在卖第: 22张票
Thread-1 正在卖第: 19张票
Thread-0 正在卖第: 19张票
Thread-2 正在卖第: 19张票
Thread-2 正在卖第: 16张票
Thread-1 正在卖第: 16张票
Thread-0 正在卖第: 16张票
Thread-0 正在卖第: 13张票
Thread-1 正在卖第: 13张票
Thread-2 正在卖第: 13张票
Thread-2 正在卖第: 10张票
Thread-1 正在卖第: 9张票
Thread-0 正在卖第: 9张票
Thread-2 正在卖第: 7张票
Thread-1 正在卖第: 7张票
Thread-0 正在卖第: 7张票
Thread-0 正在卖第: 4张票
Thread-2 正在卖第: 4张票
Thread-1 正在卖第: 3张票
Thread-2 正在卖第: 1张票
Thread-0 正在卖第: 1张票
Thread-1 正在卖第: 1张票
-
多线程安全问题的解决
多线程的同步来解决线程安全问题,多线程的同步可以有以下三种实现方式:
- 同步代码块
- 同步方法
- 锁机制
①同步代码块:
synchronized
关键字可以用于方法中的某个区块中,表示只对这个区块的资源进行互斥访问格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁:对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
-
锁对象可以是任一类型
-
多个线程对象,要使用同一把锁
注意:多个线程在任何时候,都最多允许只有一个线程拿到这个同步锁,谁拿到同步锁谁就进入代码块,其他线程则必须等待(BLOCKED)
//在重写runnalbe的类中加入一个object(任一)的锁对象,同时在重写run方法时,在其中修改共享数据的语句包裹在synchronizede中 /* * 同步代码块解决实现卖票案例中出现的线程安全问题 * */ public class RunnableImpl implements Runnable{ private int ticket = 100; //创建一个锁对象 Object object = new Object(); @Override public void run() { while (true){ synchronized (object){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket>0) { System.out.println(Thread.currentThread().getName()+" 正在卖第: "+ ticket +"张票"); ticket--; } } } } } //主方法不变,继续让三个线程来执行这个run方法 /* * 创建三个线程让这三个线程都对共享的票进行出售 * 共享体现在只有一个runnable的实现类,但是用三个线程来执行 * */ public class DemoMain { public static void main(String[] args) { RunnableImpl run = new RunnableImpl(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); t1.start(); t2.start(); t3.start(); } }
打印结果能看到,解决了线程安全问题:
Thread-0 正在卖第: 100张票 Thread-0 正在卖第: 99张票 Thread-0 正在卖第: 98张票 Thread-0 正在卖第: 97张票 Thread-0 正在卖第: 96张票 Thread-0 正在卖第: 95张票 Thread-0 正在卖第: 94张票 Thread-0 正在卖第: 93张票 Thread-0 正在卖第: 92张票 Thread-0 正在卖第: 91张票 Thread-0 正在卖第: 90张票 Thread-0 正在卖第: 89张票 Thread-0 正在卖第: 88张票 Thread-0 正在卖第: 87张票 Thread-0 正在卖第: 86张票 Thread-0 正在卖第: 85张票 Thread-0 正在卖第: 84张票 Thread-0 正在卖第: 83张票 Thread-0 正在卖第: 82张票 Thread-0 正在卖第: 81张票 Thread-0 正在卖第: 80张票 Thread-0 正在卖第: 79张票 Thread-0 正在卖第: 78张票 Thread-0 正在卖第: 77张票 Thread-0 正在卖第: 76张票 Thread-0 正在卖第: 75张票 Thread-0 正在卖第: 74张票 Thread-0 正在卖第: 73张票 Thread-0 正在卖第: 72张票 Thread-0 正在卖第: 71张票 Thread-0 正在卖第: 70张票 Thread-0 正在卖第: 69张票 Thread-0 正在卖第: 68张票 Thread-0 正在卖第: 67张票 Thread-0 正在卖第: 66张票 Thread-0 正在卖第: 65张票 Thread-0 正在卖第: 64张票 Thread-0 正在卖第: 63张票 Thread-0 正在卖第: 62张票 Thread-0 正在卖第: 61张票 Thread-0 正在卖第: 60张票 Thread-0 正在卖第: 59张票 Thread-1 正在卖第: 58张票 Thread-1 正在卖第: 57张票 Thread-1 正在卖第: 56张票 Thread-1 正在卖第: 55张票 Thread-1 正在卖第: 54张票 Thread-1 正在卖第: 53张票 Thread-1 正在卖第: 52张票 Thread-1 正在卖第: 51张票 Thread-2 正在卖第: 50张票 Thread-2 正在卖第: 49张票 Thread-2 正在卖第: 48张票 Thread-2 正在卖第: 47张票 Thread-2 正在卖第: 46张票 Thread-2 正在卖第: 45张票 Thread-2 正在卖第: 44张票 Thread-2 正在卖第: 43张票 Thread-2 正在卖第: 42张票 Thread-2 正在卖第: 41张票 Thread-2 正在卖第: 40张票 Thread-2 正在卖第: 39张票 Thread-2 正在卖第: 38张票 Thread-2 正在卖第: 37张票 Thread-2 正在卖第: 36张票 Thread-2 正在卖第: 35张票 Thread-2 正在卖第: 34张票 Thread-2 正在卖第: 33张票 Thread-2 正在卖第: 32张票 Thread-2 正在卖第: 31张票 Thread-2 正在卖第: 30张票 Thread-2 正在卖第: 29张票 Thread-2 正在卖第: 28张票 Thread-2 正在卖第: 27张票 Thread-2 正在卖第: 26张票 Thread-2 正在卖第: 25张票 Thread-2 正在卖第: 24张票 Thread-2 正在卖第: 23张票 Thread-1 正在卖第: 22张票 Thread-1 正在卖第: 21张票 Thread-1 正在卖第: 20张票 Thread-1 正在卖第: 19张票 Thread-1 正在卖第: 18张票 Thread-1 正在卖第: 17张票 Thread-1 正在卖第: 16张票 Thread-1 正在卖第: 15张票 Thread-1 正在卖第: 14张票 Thread-1 正在卖第: 13张票 Thread-1 正在卖第: 12张票 Thread-1 正在卖第: 11张票 Thread-1 正在卖第: 10张票 Thread-1 正在卖第: 9张票 Thread-1 正在卖第: 8张票 Thread-1 正在卖第: 7张票 Thread-1 正在卖第: 6张票 Thread-1 正在卖第: 5张票 Thread-1 正在卖第: 4张票 Thread-1 正在卖第: 3张票 Thread-1 正在卖第: 2张票 Thread-1 正在卖第: 1张票
同步代码块原理
②同步方法:使用synchronized修饰的方法就叫做,同步方法,保证线程A在使用该方法的时候,其他方法只能在方法外等待
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
//可能会产生线程安全问题的代码
}
同步锁:
在同步非静态方法中,同步锁就是this
在静态方法中,同步锁就是类的字节码对象(类名.class)
//在Runnable的实现类中,重新定义一个非静态同步方法,把可能产生线程安全问题的代码写入该方法中 //在run方法中去调用该同步方法、 /* * 同步方法结局卖票案例中的线程安全问题 * */ public class RunnableImpl implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ sellTicket(); } } public synchronized void sellTicket(){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket>0) { System.out.println(Thread.currentThread().getName()+" 正在卖第: "+ ticket +"张票"); ticket--; } } } //主方法不变,三个线程去调用run方法 //在同步非静态方法中,同步锁就是this 在这个例子中就是new的对象run /* * 创建三个线程让这三个线程都对共享的票进行出售 * 共享体现在只有一个runnable的实现类,但是用三个线程来执行 * */ public class DemoMain { public static void main(String[] args) { RunnableImpl run = new RunnableImpl(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); t1.start(); t2.start(); t3.start(); } }
打印结果:解决线程安全问题
Thread-0 正在卖第: 100张票 Thread-0 正在卖第: 99张票 Thread-0 正在卖第: 98张票 Thread-0 正在卖第: 97张票 Thread-0 正在卖第: 96张票 Thread-0 正在卖第: 95张票 Thread-0 正在卖第: 94张票 Thread-0 正在卖第: 93张票 Thread-0 正在卖第: 92张票 Thread-0 正在卖第: 91张票 Thread-0 正在卖第: 90张票 Thread-0 正在卖第: 89张票 Thread-0 正在卖第: 88张票 Thread-0 正在卖第: 87张票 Thread-0 正在卖第: 86张票 Thread-0 正在卖第: 85张票 Thread-0 正在卖第: 84张票 Thread-0 正在卖第: 83张票 Thread-2 正在卖第: 82张票 Thread-2 正在卖第: 81张票 Thread-2 正在卖第: 80张票 Thread-2 正在卖第: 79张票 Thread-2 正在卖第: 78张票 Thread-2 正在卖第: 77张票 Thread-2 正在卖第: 76张票 Thread-2 正在卖第: 75张票 Thread-2 正在卖第: 74张票 Thread-2 正在卖第: 73张票 Thread-2 正在卖第: 72张票 Thread-2 正在卖第: 71张票 Thread-2 正在卖第: 70张票 Thread-2 正在卖第: 69张票 Thread-2 正在卖第: 68张票 Thread-2 正在卖第: 67张票 Thread-2 正在卖第: 66张票 Thread-1 正在卖第: 65张票 Thread-1 正在卖第: 64张票 Thread-1 正在卖第: 63张票 Thread-1 正在卖第: 62张票 Thread-1 正在卖第: 61张票 Thread-1 正在卖第: 60张票 Thread-1 正在卖第: 59张票 Thread-1 正在卖第: 58张票 Thread-1 正在卖第: 57张票 Thread-1 正在卖第: 56张票 Thread-1 正在卖第: 55张票 Thread-1 正在卖第: 54张票 Thread-1 正在卖第: 53张票 Thread-1 正在卖第: 52张票 Thread-1 正在卖第: 51张票 Thread-1 正在卖第: 50张票 Thread-1 正在卖第: 49张票 Thread-1 正在卖第: 48张票 Thread-1 正在卖第: 47张票 Thread-1 正在卖第: 46张票 Thread-1 正在卖第: 45张票 Thread-1 正在卖第: 44张票 Thread-1 正在卖第: 43张票 Thread-1 正在卖第: 42张票 Thread-1 正在卖第: 41张票 Thread-1 正在卖第: 40张票 Thread-1 正在卖第: 39张票 Thread-1 正在卖第: 38张票 Thread-1 正在卖第: 37张票 Thread-1 正在卖第: 36张票 Thread-1 正在卖第: 35张票 Thread-1 正在卖第: 34张票 Thread-1 正在卖第: 33张票 Thread-1 正在卖第: 32张票 Thread-1 正在卖第: 31张票 Thread-1 正在卖第: 30张票 Thread-1 正在卖第: 29张票 Thread-2 正在卖第: 28张票 Thread-2 正在卖第: 27张票 Thread-2 正在卖第: 26张票 Thread-2 正在卖第: 25张票 Thread-2 正在卖第: 24张票 Thread-2 正在卖第: 23张票 Thread-2 正在卖第: 22张票 Thread-2 正在卖第: 21张票 Thread-2 正在卖第: 20张票 Thread-2 正在卖第: 19张票 Thread-2 正在卖第: 18张票 Thread-2 正在卖第: 17张票 Thread-2 正在卖第: 16张票 Thread-0 正在卖第: 15张票 Thread-0 正在卖第: 14张票 Thread-0 正在卖第: 13张票 Thread-0 正在卖第: 12张票 Thread-0 正在卖第: 11张票 Thread-0 正在卖第: 10张票 Thread-0 正在卖第: 9张票 Thread-0 正在卖第: 8张票 Thread-0 正在卖第: 7张票 Thread-0 正在卖第: 6张票 Thread-0 正在卖第: 5张票 Thread-0 正在卖第: 4张票 Thread-0 正在卖第: 3张票 Thread-0 正在卖第: 2张票 Thread-0 正在卖第: 1张票
静态同步方法:
//在Runnable的实现类中,重新定义一个静态同步方法,把可能产生线程安全问题的代码写入该方法中 //在run方法中去调用该同步方法、 /* * 同步方法结局卖票案例中的线程安全问题 * */ public class RunnableImpl implements Runnable{ private static int ticket = 100; @Override public void run() { while (true){ sellTicketStatic(); } } public static synchronized void sellTicketStatic(){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket>0) { System.out.println(Thread.currentThread().getName()+" 正在卖第: "+ ticket +"张票"); ticket--; } } } //主方法开启三个线程去执行run方法 /* * 创建三个线程让这三个线程都对共享的票进行出售 * 共享体现在只有一个runnable的实现类,但是用三个线程来执行 此时静态方法的锁对象就是 Runnable.class * */ public class DemoMain { public static void main(String[] args) { RunnableImpl run = new RunnableImpl(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); t1.start(); t2.start(); t3.start(); } }
打印结果:依然是解决了线程安全问题
Thread-0 正在卖第: 100张票 Thread-0 正在卖第: 99张票 Thread-0 正在卖第: 98张票 Thread-0 正在卖第: 97张票 Thread-0 正在卖第: 96张票 Thread-0 正在卖第: 95张票 Thread-0 正在卖第: 94张票 Thread-2 正在卖第: 93张票 Thread-2 正在卖第: 92张票 Thread-2 正在卖第: 91张票 Thread-2 正在卖第: 90张票 Thread-2 正在卖第: 89张票 Thread-2 正在卖第: 88张票 Thread-2 正在卖第: 87张票 Thread-2 正在卖第: 86张票 Thread-2 正在卖第: 85张票 Thread-2 正在卖第: 84张票 Thread-2 正在卖第: 83张票 Thread-2 正在卖第: 82张票 Thread-2 正在卖第: 81张票 Thread-2 正在卖第: 80张票 Thread-2 正在卖第: 79张票 Thread-2 正在卖第: 78张票 Thread-2 正在卖第: 77张票 Thread-2 正在卖第: 76张票 Thread-2 正在卖第: 75张票 Thread-2 正在卖第: 74张票 Thread-2 正在卖第: 73张票 Thread-2 正在卖第: 72张票 Thread-2 正在卖第: 71张票 Thread-1 正在卖第: 70张票 Thread-1 正在卖第: 69张票 Thread-1 正在卖第: 68张票 Thread-1 正在卖第: 67张票 Thread-1 正在卖第: 66张票 Thread-1 正在卖第: 65张票 Thread-1 正在卖第: 64张票 Thread-1 正在卖第: 63张票 Thread-1 正在卖第: 62张票 Thread-1 正在卖第: 61张票 Thread-1 正在卖第: 60张票 Thread-1 正在卖第: 59张票 Thread-1 正在卖第: 58张票 Thread-1 正在卖第: 57张票 Thread-2 正在卖第: 56张票 Thread-2 正在卖第: 55张票 Thread-2 正在卖第: 54张票 Thread-0 正在卖第: 53张票 Thread-0 正在卖第: 52张票 Thread-0 正在卖第: 51张票 Thread-0 正在卖第: 50张票 Thread-0 正在卖第: 49张票 Thread-0 正在卖第: 48张票 Thread-0 正在卖第: 47张票 Thread-0 正在卖第: 46张票 Thread-0 正在卖第: 45张票 Thread-0 正在卖第: 44张票 Thread-0 正在卖第: 43张票 Thread-0 正在卖第: 42张票 Thread-0 正在卖第: 41张票 Thread-0 正在卖第: 40张票 Thread-0 正在卖第: 39张票 Thread-0 正在卖第: 38张票 Thread-0 正在卖第: 37张票 Thread-0 正在卖第: 36张票 Thread-0 正在卖第: 35张票 Thread-0 正在卖第: 34张票 Thread-0 正在卖第: 33张票 Thread-0 正在卖第: 32张票 Thread-0 正在卖第: 31张票 Thread-2 正在卖第: 30张票 Thread-2 正在卖第: 29张票 Thread-2 正在卖第: 28张票 Thread-2 正在卖第: 27张票 Thread-1 正在卖第: 26张票 Thread-1 正在卖第: 25张票 Thread-1 正在卖第: 24张票 Thread-1 正在卖第: 23张票 Thread-1 正在卖第: 22张票 Thread-1 正在卖第: 21张票 Thread-1 正在卖第: 20张票 Thread-1 正在卖第: 19张票 Thread-1 正在卖第: 18张票 Thread-1 正在卖第: 17张票 Thread-1 正在卖第: 16张票 Thread-1 正在卖第: 15张票 Thread-1 正在卖第: 14张票 Thread-1 正在卖第: 13张票 Thread-1 正在卖第: 12张票 Thread-1 正在卖第: 11张票 Thread-1 正在卖第: 10张票 Thread-1 正在卖第: 9张票 Thread-1 正在卖第: 8张票 Thread-1 正在卖第: 7张票 Thread-1 正在卖第: 6张票 Thread-1 正在卖第: 5张票 Thread-1 正在卖第: 4张票 Thread-1 正在卖第: 3张票 Thread-1 正在卖第: 2张票 Thread-1 正在卖第: 1张票
③Lock锁
java.util.concurrent.locks.Lock
机制提供比使用synchronized
方法和语句可以获得的更广泛的锁定操作。同步代码块/同步方法所具有的的方法,Lock都有,除此之外还更强大,更体现面向对象-
public interface Lock
Lock
实现提供比使用synchronized
方法和语句可以获得的更广泛的锁定操作。它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition
。锁是用于通过多个线程控制对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如
ReadWriteLock
的读锁。使用
synchronized
方法或语句提供对与每个对象相关联的隐式监视器锁的访问,但是强制所有锁获取和释放以块结构的方式发生:当获取多个锁时,它们必须以相反的顺序被释放,并且所有的锁都必须被释放在与它们相同的词汇范围内。虽然
synchronized
方法和语句的范围机制使得使用监视器锁更容易编程,并且有助于避免涉及锁的许多常见编程错误,但是有时您需要以更灵活的方式处理锁。 例如,用于遍历并发访问的数据结构的一些算法需要使用“手动”或“链锁定”:您获取节点A的锁定,然后获取节点B,然后释放A并获取C,然后释放B并获得D等。 所述的实施方式中Lock
接口通过允许获得并在不同的范围释放的锁,并允许获得并以任何顺序释放多个锁使得能够使用这样的技术。 -
同时加锁和解锁进行方法化
void lock()
:获得锁void unlock()
:释放锁 -
代码实现:
/* * 使用Lock锁来解决卖票案例中产生的线程安全问题 * Lock的实现类:java.util.concurrent.locks.ReentrantLock implements Lock * 使用步骤:1.在Runnable实现类的成员位置创建Lock的实现类 * 2.在可能出现安全问题的代码处调用Lock的方法void lock();获取锁 * 3.在可能出现安全问题的代码后调用Lock的方法void unlock();方法解锁 * * */ public class RunnableImpl implements Runnable{ private int ticket = 100; //1.在Runnable实现类的成员位置创建Lock的实现类 Lock lock = new ReentrantLock(); @Override public void run() { while (true){ //2.在可能出现安全问题的代码处调用Lock的方法void lock();获取锁 lock.lock(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket>0) { System.out.println(Thread.currentThread().getName()+" 正在卖第: "+ ticket +"张票"); ticket--; } //3.在可能出现安全问题的代码后调用Lock的方法void unlock();方法解锁 lock.unlock(); } } } //主方法开启三个线程 /* * 创建三个线程让这三个线程都对共享的票进行出售 * 共享体现在只有一个runnable的实现类,但是用三个线程来执行 * */ public class DemoMain { public static void main(String[] args) { RunnableImpl run = new RunnableImpl(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); t1.start(); t2.start(); t3.start(); } }
打印结果:解决了线程安全问题
Thread-0 正在卖第: 100张票 Thread-0 正在卖第: 99张票 Thread-0 正在卖第: 98张票 Thread-0 正在卖第: 97张票 Thread-0 正在卖第: 96张票 Thread-0 正在卖第: 95张票 Thread-0 正在卖第: 94张票 Thread-0 正在卖第: 93张票 Thread-0 正在卖第: 92张票 Thread-0 正在卖第: 91张票 Thread-0 正在卖第: 90张票 Thread-0 正在卖第: 89张票 Thread-0 正在卖第: 88张票 Thread-0 正在卖第: 87张票 Thread-0 正在卖第: 86张票 Thread-0 正在卖第: 85张票 Thread-0 正在卖第: 84张票 Thread-0 正在卖第: 83张票 Thread-0 正在卖第: 82张票 Thread-0 正在卖第: 81张票 Thread-0 正在卖第: 80张票 Thread-0 正在卖第: 79张票 Thread-0 正在卖第: 78张票 Thread-0 正在卖第: 77张票 Thread-0 正在卖第: 76张票 Thread-1 正在卖第: 75张票 Thread-1 正在卖第: 74张票 Thread-1 正在卖第: 73张票 Thread-1 正在卖第: 72张票 Thread-1 正在卖第: 71张票 Thread-1 正在卖第: 70张票 Thread-1 正在卖第: 69张票 Thread-1 正在卖第: 68张票 Thread-1 正在卖第: 67张票 Thread-1 正在卖第: 66张票 Thread-1 正在卖第: 65张票 Thread-1 正在卖第: 64张票 Thread-1 正在卖第: 63张票 Thread-1 正在卖第: 62张票 Thread-1 正在卖第: 61张票 Thread-1 正在卖第: 60张票 Thread-1 正在卖第: 59张票 Thread-1 正在卖第: 58张票 Thread-1 正在卖第: 57张票 Thread-1 正在卖第: 56张票 Thread-1 正在卖第: 55张票 Thread-1 正在卖第: 54张票 Thread-1 正在卖第: 53张票 Thread-1 正在卖第: 52张票 Thread-1 正在卖第: 51张票 Thread-1 正在卖第: 50张票 Thread-1 正在卖第: 49张票 Thread-1 正在卖第: 48张票 Thread-1 正在卖第: 47张票 Thread-1 正在卖第: 46张票 Thread-1 正在卖第: 45张票 Thread-1 正在卖第: 44张票 Thread-1 正在卖第: 43张票 Thread-1 正在卖第: 42张票 Thread-1 正在卖第: 41张票 Thread-1 正在卖第: 40张票 Thread-1 正在卖第: 39张票 Thread-1 正在卖第: 38张票 Thread-1 正在卖第: 37张票 Thread-1 正在卖第: 36张票 Thread-1 正在卖第: 35张票 Thread-1 正在卖第: 34张票 Thread-1 正在卖第: 33张票 Thread-1 正在卖第: 32张票 Thread-1 正在卖第: 31张票 Thread-1 正在卖第: 30张票 Thread-1 正在卖第: 29张票 Thread-1 正在卖第: 28张票 Thread-1 正在卖第: 27张票 Thread-1 正在卖第: 26张票 Thread-1 正在卖第: 25张票 Thread-1 正在卖第: 24张票 Thread-1 正在卖第: 23张票 Thread-1 正在卖第: 22张票 Thread-1 正在卖第: 21张票 Thread-1 正在卖第: 20张票 Thread-1 正在卖第: 19张票 Thread-1 正在卖第: 18张票 Thread-1 正在卖第: 17张票 Thread-1 正在卖第: 16张票 Thread-1 正在卖第: 15张票 Thread-1 正在卖第: 14张票 Thread-1 正在卖第: 13张票 Thread-1 正在卖第: 12张票 Thread-1 正在卖第: 11张票 Thread-1 正在卖第: 10张票 Thread-1 正在卖第: 9张票 Thread-1 正在卖第: 8张票 Thread-1 正在卖第: 7张票 Thread-1 正在卖第: 6张票 Thread-1 正在卖第: 5张票 Thread-1 正在卖第: 4张票 Thread-1 正在卖第: 3张票 Thread-1 正在卖第: 2张票 Thread-1 正在卖第: 1张票
可以看API文档优化以下lock锁部分,把unlock放到finally
public class RunnableImpl implements Runnable{
private int ticket = 100;
//1.在Runnable实现类的成员位置创建Lock的实现类
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
//2.在可能出现安全问题的代码处调用Lock的方法void lock();获取锁
lock.lock();
try {
Thread.sleep(10);
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+" 正在卖第: "+ ticket +"张票");
ticket--;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//3.在可能出现安全问题的代码后调用Lock的方法void unlock();方法解锁
lock.unlock();//无论程序是否异常都会把锁给释放掉,提高程序效率
}
}
}
}