线程安全,同步方法和锁-11月18日讲课内容
线程安全
出现的原因
线程的抢占发生在任意时期
当多个线程操作同一个数据源的时候吗,并且都涉及到了修改,那么就有可能发生数据重复的问题,这种现象就成为线程安全的问题。
案例代码
public class Demo10 {
public static void main(String[] args) {
Thread thread1 = new Thread(new SellTicket(), "淘票票");
Thread thread2 = new Thread(new SellTicket(), "猫眼");
Thread thread3 = new Thread(new SellTicket(), "美团");
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket implements Runnable {
// 100张票
private static int ticket = 100;
@Override
public void run() {
// 循环卖票
while (true) {
// 如果当前票数不为0,就卖一张,票数-1,并睡一会儿
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 如果当前票数为0,说明票卖完了,需要退出循环
} else {
System.out.println(Thread.currentThread().getName() + "已售罄!!");
break;
}
}
}
}
导致线程安全问题的因素
1、多个线程
2、同时操作临界资源 共享资源
如何解决线程安全的问题(加锁)
Synchronized
同步代码块
格式:
synchronized (锁对象) {
// 需要被同步的代码;
}
【注意】同步代码块的锁对象可以是任意对象,但必须是同一个对象
public class Demo11 {
public static void main(String[] args) {
Thread thread1 = new Thread(new SellTicket1(), "淘票票");
Thread thread2 = new Thread(new SellTicket1(), "猫眼");
Thread thread3 = new Thread(new SellTicket1(), "美团");
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket1 implements Runnable {
private static int ticket = 100;
private static final Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + "已售罄!!");
break;
}
}
}
}
}
同步方法
格式:
synchronized 返回值 方法名(参数列表){
// 需要被同步的代码;
}
【注意】同步方法的锁对象是 this
public class Demo12 {
public static void main(String[] args) {
SellTicket2 sellTicket2 = new SellTicket2();
Thread thread1 = new Thread(sellTicket2, "淘票票");
Thread thread2 = new Thread(sellTicket2, "猫眼");
Thread thread3 = new Thread(sellTicket2, "美团");
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket2 implements Runnable {
private static int ticket = 100;
private synchronized boolean sell() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
} else {
System.out.println(Thread.currentThread().getName() + "已售罄!!");
return false;
}
}
@Override
public void run() {
while (true) {
if (!sell()) {
break;
}
}
}
}
静态同步方法
格式:
static synchronized 返回值 方法名(参数列表){
// 需要被同步的代码;
}
【注意】同步静态方法的锁对象是当前类的字节码文件对象,保证唯一性
public class Demo13 {
public static void main(String[] args) {
Thread thread1 = new Thread(new SellTicket3(), "淘票票");
Thread thread2 = new Thread(new SellTicket3(), "猫眼");
Thread thread3 = new Thread(new SellTicket3(), "美团");
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket3 implements Runnable {
private static int ticket = 100;
private static synchronized boolean sell() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
} else {
System.out.println(Thread.currentThread().getName() + "已售罄!!");
return false;
}
}
@Override
public void run() {
while (true) {
if (!sell()) {
break;
}
}
}
}
【注意】
1、锁修饰的内容只能是引用类型对象,一般都是使用this,但是一定保证是同一个类对象,否则this不唯一
2、锁的位置很关键,一般都是锁定是引起线程安全的部分
3、锁会自动释放,内部代码执行完毕之后
4、多个线程必须使用同一把锁
Synchronized 用法的区别
1、同步代码块 锁的粒度可以很小
锁的粒度来讲:同步代码块 < 同步方法 < 同步静态方法
粒度 也就是锁的范围 粒度越小越好
2、同步代码块 锁的对象可以是:引用类型属性、this、字节码对象
同步方法 锁的对象只能是当前类的实例 this
3、同步静态方法 锁的对象是当前类的字节码对象
Lock
【注意】Lock锁的性能要比 synchronized 高,但是lock要求必须手动释放锁
格式:
private final Lock lock = new ReentrantLock();
public void method() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
案例代码
public class Demo14 {
public static void main(String[] args) {
SellTicket4 sellTicket4 = new SellTicket4();
Thread thread1 = new Thread(sellTicket4, "淘票票");
Thread thread2 = new Thread(sellTicket4, "猫眼");
Thread thread3 = new Thread(sellTicket4, "美团");
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket4 implements Runnable {
private static int ticket = 100;
private final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + "已售罄!!");
break;
}
} finally {
lock.unlock();
}
}
}
}
案例代码2
public class Demo15 {
public static void main(String[] args) {
Thread thread1 = new Thread(new SellTicket5(), "淘票票");
Thread thread2 = new Thread(new SellTicket5(), "猫眼");
Thread thread3 = new Thread(new SellTicket5(), "美团");
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket5 implements Runnable {
private static int ticket = 100;
private static final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + "已售罄!!");
break;
}
} finally {
lock.unlock();
}
}
}
}
死锁
两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象
产生的原因
1、2个以上的线程
2、2个以上的锁
3、同步代码块嵌套同步代码块
自定义锁对象
class MyLock {
// 第一把锁
public static Object LOCK1 = new Object();
// 第二把锁
public static Object LOCK2 = new Object();
}
死锁类
public class DeadLock implements Runnable {
boolean flag;
public DeadLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.LOCK1) {
System.out.println("if LOCK1");
synchronized (MyLock.LOCK2) {
System.out.println("if LOCK2");
}
}
} else {
synchronized (MyLock.LOCK2) {
System.out.println("else LOCK2");
synchronized (MyLock.LOCK1) {
System.out.println("else LOCK1");
}
}
}
};
}
测试类
public class Test {
public static void main(String[] args) {
Thread th1 = new Thread(new DeadLock(false));
Thread th2 = new Thread(new DeadLock(true));
th1.start();
th2.start();
}
}