多线程出现安全问题
当多线程访问(有写操作,更改共享数据的值的时候)共享数据的时候,会产生线程安全问题
以卖票案例为例做代码实现:(三个窗口同时卖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();//无论程序是否异常都会把锁给释放掉,提高程序效率
}
}
}
}