线程安全
经典问题:卖票问题,多个线程一起执行该任务,当余票只有1一张时,三个线程都进入了卖票,这时就会发生错误。导致了余票<0 的问题
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
// 线程安全问题
Runnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
// 票数
private int ticket = 10;
@Override
public void run() {
// 卖票
while(ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在准备卖票");
try {
Thread.sleep(1000); //让发生问题的错误几率加大
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + ticket);
}
}
}
运行结果:卖票出现了错误
Thread-0正在准备卖票
Thread-1正在准备卖票
Thread-2正在准备卖票
Thread-2出票成功,余票:8
Thread-2正在准备卖票
Thread-1出票成功,余票:8
Thread-1正在准备卖票
Thread-0出票成功,余票:8
Thread-0正在准备卖票
Thread-1出票成功,余票:7
Thread-0出票成功,余票:7
Thread-0正在准备卖票
Thread-2出票成功,余票:6
Thread-2正在准备卖票
Thread-1正在准备卖票
Thread-0出票成功,余票:4
Thread-2出票成功,余票:3
Thread-2正在准备卖票
Thread-1出票成功,余票:5
Thread-1正在准备卖票
Thread-0正在准备卖票
Thread-0出票成功,余票:2
Thread-0正在准备卖票
Thread-1出票成功,余票:2
Thread-1正在准备卖票
Thread-2出票成功,余票:2
Thread-2正在准备卖票
Thread-2出票成功,余票:1
Thread-1出票成功,余票:0
Thread-0出票成功,余票:-1
解决方法:
1、同步代码块
使用synchronized 关键字 (隐式锁)
格式:
synchronized (锁对象){ //这里的锁对象必须的同一个锁对象
// 同步执行的代码块
}
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
// 线程安全问题
Runnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
// 票数
private int ticket = 10;
private Object o = new Object();
@Override
public void run() {
// 卖票
while(true){
synchronized(o) { // 锁对象 - 任意对象都行,但必须是同一个对象
// 里边是同步代码块
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在准备卖票");
try {
Thread.sleep(1000); //让发生问题的错误几率加大
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + ticket);
} else {
break;
}
}
}
}
}
结果:
Thread-0正在准备卖票
Thread-0出票成功,余票:9
Thread-0正在准备卖票
Thread-0出票成功,余票:8
Thread-0正在准备卖票
Thread-0出票成功,余票:7
Thread-2正在准备卖票
Thread-2出票成功,余票:6
Thread-2正在准备卖票
Thread-2出票成功,余票:5
Thread-1正在准备卖票
Thread-1出票成功,余票:4
Thread-1正在准备卖票
Thread-1出票成功,余票:3
Thread-1正在准备卖票
Thread-1出票成功,余票:2
Thread-1正在准备卖票
Thread-1出票成功,余票:1
Thread-1正在准备卖票
Thread-1出票成功,余票:0
2、同步方法
就是在方法加上synchronized
锁对象:方法的this,如果是静态的方法,就是类名.class 如:MyRunnable.class
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
// 线程安全问题
Runnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
// 票数
private int ticket = 10;
private Object o = new Object();
@Override
public void run() {
// 卖票
while(true){
if (!sale()) break;
}
}
//卖票方法
public synchronized boolean sale(){
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在准备卖票");
try {
Thread.sleep(1000); //让发生问题的错误几率加大
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + ticket);
return true;
} else {
return false;
}
}
}
结果:
Thread-0正在准备卖票
Thread-0出票成功,余票:9
Thread-0正在准备卖票
Thread-0出票成功,余票:8
Thread-0正在准备卖票
Thread-0出票成功,余票:7
Thread-0正在准备卖票
Thread-0出票成功,余票:6
Thread-0正在准备卖票
Thread-0出票成功,余票:5
Thread-0正在准备卖票
Thread-0出票成功,余票:4
Thread-2正在准备卖票
Thread-2出票成功,余票:3
Thread-1正在准备卖票
Thread-1出票成功,余票:2
Thread-1正在准备卖票
Thread-1出票成功,余票:1
Thread-1正在准备卖票
Thread-1出票成功,余票:0
Process finished with exit code 0
3、显示锁
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
// 线程安全问题
Runnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
// 票数
private int ticket = 10;
// 显示锁 lock
private Lock lock = new ReentrantLock();
@Override
public void run() {
// 卖票
while(true){
// 上锁
lock.lock();
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在准备卖票");
try {
Thread.sleep(1000); //让发生问题的错误几率加大
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + ticket);
} else {
break;
}
// 解锁
lock.unlock();
}
}
}
显示锁与隐式锁的区别
所谓的显示和隐式就是在使用的时候,使用者要不要手动写代码去获取锁和释放锁的操作。
我们大家都知道,在使用sync关键字的时候,我们使用者根本不用写其他的代码,然后程序就能够获取锁和释放锁了。那是因为当sync代码块执行完成之后,系统会自动的让程序释放占用的锁。Sync是由系统维护的,如果非逻辑问题的话话,是不会出现死锁的。
在使用lock的时候,我们使用者需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。手动获取锁方法:lock.lock()。释放锁:unlock方法。需要配合tyr/finaly语句块来完成
4、公平锁与非公平锁
公平锁:先来先用
非公平锁:大家一块抢
以上三种都是非公平锁~
公平锁定义:
private Lock lock = new ReentrantLock(true);
指定 true 就是公平锁,
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
// 线程安全问题
Runnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable implements Runnable{
// 票数
private int ticket = 10;
// 显示锁 lock --公平锁
private Lock lock = new ReentrantLock(true);
@Override
public void run() {
// 卖票
while(true){
// 上锁
lock.lock();
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+"正在准备卖票");
try {
Thread.sleep(1000); //让发生问题的错误几率加大
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + ticket);
} else {
break;
}
// 解锁
lock.unlock();
}
}
}
结果:(线程0,线程1,线程2有序执行)
Thread-0正在准备卖票
Thread-0出票成功,余票:9
Thread-1正在准备卖票
Thread-1出票成功,余票:8
Thread-2正在准备卖票
Thread-2出票成功,余票:7
Thread-0正在准备卖票
Thread-0出票成功,余票:6
Thread-1正在准备卖票
Thread-1出票成功,余票:5
Thread-2正在准备卖票
Thread-2出票成功,余票:4
Thread-0正在准备卖票
Thread-0出票成功,余票:3
Thread-1正在准备卖票
Thread-1出票成功,余票:2
Thread-2正在准备卖票
Thread-2出票成功,余票:1
Thread-0正在准备卖票
Thread-0出票成功,余票:0