1. 同步代码块解决数据安全问题【应用】
- 1安全问题出现的条件
- 是多线程环境
- 有共享数据
- 有多条语句操作共享数据
1.2如何解决多线程安全问题呢?
- 基本思想:让程序没有安全问题的环境
2.3怎么实现呢?
- 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
- Java提供了同步代码块synchronized的方式来解决。
3.4同步代码块格式:
synchronized(任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁。
4.5同步的好处和弊端
好处:解决了多线程的数据安全问题。
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
代码块同步代码展示:
package com.yexianglun;
public class SynchronizedTest implements Runnable {
private int tickets = 100;
//实列化一个obj对象
private Object obj = new Object();
@Override
public void run() {
//死循环
while (true) {
//解决代码块安全问题
synchronized (obj) {
//可自加代码休眠 Thread.sleep(100)(100毫秒)
if (tickets <= 0) {
System.out.println(Thread.currentThread().getName() + "票卖完了");
break;
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
tickets--;
System.out.println("还剩" + tickets + "张票");
}
}
}
}
2.同步方法解决数据安全问题【应用】
2.1同步方法的格式
同步方法:就是把synchronized关键字加到方法上。
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
2.2同步方法的锁对象是什么呢?
this
2.3静态同步方法
同步静态方法:就是把synchronized关键字加到静态方法上。
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
2.4同步静态方法的锁对象是什么呢?
类名.class
静态同步代码展示:
public class MyRunnable implements Runnable {
private static int ticketCount = 100;
@Override
public void run() {
while (true) {
if ("窗口一".equals(Thread.currentThread().getName())) {
//同步方法
boolean result = synchronizedMthod();
if (result) {
break;
}
}
if ("窗口二".equals(Thread.currentThread().getName())) {
//同步代码块
synchronized (MyRunnable.class) {
if (ticketCount == 0) {
break;
} else {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount‐‐;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
}
}
}
}
}
private static synchronized boolean synchronizedMthod() {
if (ticketCount == 0) {
return true;
} else {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount‐‐;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
return false;
}
}
}
方法同步代码展示:
package com.yexianglun;
public class SynchronizedMethodTest implements Runnable {
private int tickets = 0;
@Override
public void run() {
while (true) {
boolean b = syn();
if (!b) {
break;
}
}
}
//方法同步
public synchronized boolean syn() {
//没票情况下
if (tickets == 100) {
System.out.println(Thread.currentThread().getName() + "售罄");
return false;//没票了
}
tickets++;
System.out.println(Thread.currentThread().getName() + "出售第" + tickets + "票");
// System.out.println("还剩" + (tickets + 1) + "张票");
return true;
}
}
代码测试展示
package com.yexianglun;
public class Test {
public static void main(String[] args) {
//1.使用同代码块同步
// //实列化一个SynchronizedTest类对象
// SynchronizedTest syn = new SynchronizedTest();
//
// //实列化三个窗口对象(线程)
// Thread thread1 = new Thread(syn, "一号窗口");
// Thread thread2 = new Thread(syn, "二号窗口");
// Thread thread3 = new Thread(syn, "三号窗口");
//
// thread1.start();
// thread2.start();
// thread3.start();
//2.使用方法同步
// SynchronizedMethodTest sm = new SynchronizedMethodTest();
//
// Thread thread1 = new Thread(sm, "一号窗口");
// Thread thread2 = new Thread(sm, "二号窗口");
// Thread thread3 = new Thread(sm, "三号窗口");
//
// thread1.start();
// thread2.start();
// thread3.start();
//使用Lock锁
LockTest lockTest = new LockTest();
Thread thread1 = new Thread(lockTest, "一号窗口");
Thread thread2 = new Thread(lockTest, "二号窗口");
Thread thread3 = new Thread(lockTest, "三号窗口");
thread1.start();
thread2.start();
thread3.start();
}
}
3.Lock锁【应用】
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化。
3.1ReentrantLock构造方法
方法名 | 说明 |
ReentrantLock() | 创建一个ReentrantLock的实例。 |
3.2加锁解锁方法
方法名 | 说明 |
void lock() | 获得锁。 |
void unlock() | 释放锁。 |
Lock锁代码展示:
package com.yexianglun;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest implements Runnable {
private Lock lock = new ReentrantLock();
private int ticket = 0;
@Override
public void run() {
while (true) {
try {
lock.lock();
if (ticket >= 100) {
//卖完
System.out.println("以售完");
break;
} else {
Thread.sleep(10);
ticket++;
System.out.println(Thread.currentThread().getName() + "出售" + ticket + "张");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
}
测试代码同上
4.死锁【理解】
- 概述
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
- 什么情况下会产生死锁
- 资源有限
- 同步嵌套
死锁代码展示:
public class Demo {
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();
new Thread(()‐ > {
while (true) {
synchronized (objA) {
//线程一
synchronized (objB) {
System.out.println("小康同学正在走路");
}
}
}
}).start();
new Thread(()‐ > {
while (true) {
synchronized (objB) {
//线程二
synchronized (objA) {
System.out.println("小薇同学正在走路");
}
}
}
}).start();
}
}