以售票员售票为例
线程不安全体现
public class Demo3 {
public static void main(String[] args) {
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
@Override
public void run() {
while (true) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);
} else {
break;
}
}
}
}
}
结果:
正在准备卖票
正在准备卖票
正在准备卖票
Thread-1出票成功,余票8
正在准备卖票
Thread-0出票成功,余票7
正在准备卖票
Thread-2出票成功,余票9
正在准备卖票
Thread-2出票成功,余票6
Thread-0出票成功,余票5
正在准备卖票
正在准备卖票
Thread-1出票成功,余票4
正在准备卖票
Thread-1出票成功,余票3
正在准备卖票
Thread-2出票成功,余票2
正在准备卖票
Thread-0出票成功,余票1
正在准备卖票
Thread-2出票成功,余票0
Thread-1出票成功,余票-1
Thread-0出票成功,余票-2
Process finished with exit code 0
解决方案1.线程同步:synchronized
同步代码块
可以想象为在改对象上上了一把锁
1.锁可以是任意的类型
2.多个线程对象要使用同一把锁
任何时候都最多允许一个对象拥有同步锁谁拿到锁就谁进入同步代码块
格式: synchronized(锁对象) {
}
public class Demo1 {
public static void main(String[] args) {
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
private Object o = new Object();
@Override
public void run() {
while (true) {
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);
} else {
break;
}
}
}
}
}
}
结果:
正在准备卖票
Thread-0出票成功,余票9
正在准备卖票
Thread-0出票成功,余票8
正在准备卖票
Thread-2出票成功,余票7
正在准备卖票
Thread-1出票成功,余票6
正在准备卖票
Thread-2出票成功,余票5
正在准备卖票
Thread-0出票成功,余票4
正在准备卖票
Thread-2出票成功,余票3
正在准备卖票
Thread-1出票成功,余票2
正在准备卖票
Thread-2出票成功,余票1
正在准备卖票
Thread-2出票成功,余票0
Process finished with exit code 0
解决方案2.同步方法
使用synchronized修饰的方法叫做同步方法,保证线程安全,当a线程执行该方法的时候,其他线程只可以在方法外等待。
public class Demo2 {
public static void main(String[] args) {
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if (!flag) {
break;
}
}
}
public synchronized boolean sale() {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);
return true;
}
return false;
}
}
}
结果:
正在准备卖票
Thread-0出票成功,余票9
正在准备卖票
Thread-0出票成功,余票8
正在准备卖票
Thread-1出票成功,余票7
正在准备卖票
Thread-1出票成功,余票6
正在准备卖票
Thread-2出票成功,余票5
正在准备卖票
Thread-1出票成功,余票4
正在准备卖票
Thread-0出票成功,余票3
正在准备卖票
Thread-0出票成功,余票2
正在准备卖票
Thread-1出票成功,余票1
正在准备卖票
Thread-1出票成功,余票0
Process finished with exit code 0
解决方案2.显式锁 Lock 子类 ReentrantLock
java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作, 同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁的功能
public void lock()加同步锁
public void unlock() 释放同步锁
同步代码块 和 同步方法都是隐式锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo3 {
public static void main(String[] args) {
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
//显式锁
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);
} else {
break;
}
l.unlock();
}
}
}
}
结果:
正在准备卖票
Thread-0出票成功,余票9
正在准备卖票
Thread-2出票成功,余票8
正在准备卖票
Thread-2出票成功,余票7
正在准备卖票
Thread-2出票成功,余票6
正在准备卖票
Thread-1出票成功,余票5
正在准备卖票
Thread-1出票成功,余票4
正在准备卖票
Thread-0出票成功,余票3
正在准备卖票
Thread-2出票成功,余票2
正在准备卖票
Thread-2出票成功,余票1
正在准备卖票
Thread-2出票成功,余票0