线程安全问题
三种解决方案
同步代码块
多线程售票程序
/*
* 卖票案例出现了线程安全问题
* 卖出了不存在的票和重复的票
* 解决线程安全问题的一种方案:使用同步代码块
* 格式:
* synchronized(锁对象){
* 可能会出现线程安全的代码(访问了共享数据的代码)
* }
* 注意:
* 通过代码块中的锁对象,可以使用任意的对象
* 但是必须保证多个线程使用的锁对象是同一个
* 锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
*/
public class SynCodeBlock implements Runnable {
// 定义一个多线程共享的票源
private Integer ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true) { // 此处设置ticket>=1的话 三个进程可能都满足此条件
// 提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (obj) {
if (ticket >= 1) {
System.out.println(Thread.currentThread().getName()
+ "正在售卖第" + (100 - (--ticket)) + "张票");
}
}
}
// TODO Auto-generated method stub
}
}
执行多线程程序
public class SynCodeBlockMain {
public static void main(String []args){
SynCodeBlock run=new SynCodeBlock();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
同步方法
多线程售票程序
public class SynMethod implements Runnable {
private Integer ticket=100;
Object obj=new Object();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
showTicket_1();
}
}
//同步方法一
public synchronized void showTicket(){
if(ticket>=1){
System.out.println(Thread.currentThread().getName()+"正在售卖第"+(100-(--ticket))+"张票");
}
}
//同步方法二
public void showTicket_1(){
synchronized(this){
if(ticket>=1){
System.out.println(Thread.currentThread().getName()+"正在售卖的第"+(100-(--ticket))+"张票");
}
}
}
}
执行多线程程序
/*
* 模拟卖票的案例
* 创建3个线程,同时开启,对共享的票进行出售
*/
public class SynMethodMain {
public static void main(String []args){
SynMethod run=new SynMethod();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
静态同步方法
多线程售票程序
public class SynStatic implements Runnable {
private static Integer ticket = 100;
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
showTicket_1();
}
}
//方法一
public synchronized static void showTicket() {
if (ticket >= 1) {
System.out.println(Thread.currentThread().getName() + "正在售卖第"
+ (100 - (--ticket)) + "张票");
}
}
/*方法二
静态同步方法
锁对象是谁?
不能是this
this是创建对象后产生的,静态方法优先于对象
静态方法的锁对象是本类的class属性-->class文件对象(反射)
*/
public static void showTicket_1(){
synchronized(Runnable.class){
if (ticket >= 1) {
System.out.println(Thread.currentThread().getName() + "正在售卖d第"
+ (100 - (--ticket)) + "张票");
}
}
}
}
执行多线程程序
/*
Java.util.concurrent.locks.Lock接口
Lock实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作。
Lock接口中的方法:
Void lock() 获取锁
Void unlock() 释放锁
Java.util.concurrent.locks.ReentrantLock lock接口
使用步骤:
1、 在成员位置创建一个ReentrantLock对象
2、 在可能会出现安全问题的代码前调用Lock接口中的方法Lock获取锁
3、 在可能会出现安全问题的代码后调用Lock接口中的方法Lock释放锁
*/
public class SynStaticMain {
public static void main(String []args){
SynStatic run=new SynStatic();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
Lock锁
多线程售票程序
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockRunnable implements Runnable{
private Integer ticket=100;
// 1、在成员位置创建一个ReentrantLock对象
Lock lock=new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
// 2、在可能出现安全问题的代码前调用lock接口中的方法lock获取锁
lock.lock();
if (ticket > =1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally { // 无论是否会出现异常 锁都会被释放掉
System.out.println(Thread.currentThread().getName()+"正在售卖第"+(100-(--ticket))+"张票");
// 3、在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁
lock.unlock();
}
}
}
}
}
执行多线程程序
public class LockMain {
public static void main(String []args){
LockRunnable run=new LockRunnable();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}