线程安全(synchronized)
第一种:synchronized同步代码块
原理:使用了一个锁对象,也叫同步锁,也叫对象锁,也叫对象监视器
多个线程一起抢夺CPU的执行权,先抢到的线程会获得锁对象,执行完毕后归还锁对象,其他线程才能获得这个锁对象,进行代码执行。
也就是说同步中的线程,没有执行完毕不会释放锁,同步外的线程没有获得锁进不去同步,保证只能有一个线程在同步中执行共享数据,保证了安全,但是降低了效率,需要不停地判断锁,释放锁。
package 线程安全问题;
public class MyRunnable implements Runnable {
//
private int ticket = 100;
//创建一个锁对象
Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "票");
ticket = ticket - 1;
}
}
}
}
}
package 线程安全问题;
/*
* 解决线程安全的一种方法:使用同步代码块
* 格式:
* synchronized(锁对象){
* 可能会出现线程安全的代码(访问了共享数据的代码块)
* }
*
* 注意:
* 1.锁对象可以是任意对象
* 2.但必须保证多个线程使用的锁对象是同一个
* 3.锁对象的作用:
* 把同步代码块锁住,只让一个线程在同步代码块中执行
* */
public class Demo02 {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t0 = new Thread(r);
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t0.start();
t1.start();
t2.start();
}
}
####################运行结果#############
不会出现重复买票或者卖不存在的票
第二种:使用同步方法
public class MyRunnable implements Runnable {
//
private int ticket = 100;
//创建一个锁对象
//Object obj = new Object();
@Override
public void run() {
while (true) {
payTicket();
}
}
public synchronized void payTicket() {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "票");
ticket = ticket - 1;
}
}
}
package 线程安全问题;
/*
*
* 解决线程安全的第二种方法:使用同步方法
* 使用步骤:
* 1.把访问共享数据的代码抽取出来,放到一个方法中
* 2.在方法上添加synchronized修饰符
*
* 格式:就是定义方法的格式
* 修饰符 synchronized 返回值类型 方法名(参数列表){
* 访问共享数据的代码
* }
*
*
* */
public class Dmo03 {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t0 = new Thread(r);
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t0.start();
t1.start();
t2.start();
}
}
第三种方法:使用Lock/unLock
java.util.concurrent.locks 的方法Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性
package 线程安全问题;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
*
*
* 使用步骤:
* 1.在成员变量位置创建一个ReentrantLock对象
* 2.在可能会出现线程安全的代码前调用Lock接口中的方法Lock获取锁
* 3.在可能会出现线程安全的代码后调用Lock接口中的方法Lock释放锁
*
* */
public class MyRunnable implements Runnable {
//
private int ticket = 100;
//1.在成员变量位置创建一个ReentrantLock对象
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
//2.在可能会出现线程安全的代码前调用Lock接口中的方法Lock获取锁
l.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
//卖票的提示
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "票");
ticket = ticket - 1;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
l.unlock();
}
}
//3.在可能会出现线程安全的代码后调用Lock接口中的方法Lock释放锁
//l.unlock(); 提高程序效率,最好放到finally代码块中
}
}
}