Java之多线程安全性问题
小结:
同步方式:
a, 同步代码块锁对象可以是任意的对象
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
b, 同步方法 锁对象是this
public synchronized void method(){
可能会产生线程安全问题的代码
}
bb 静态同步方法 锁对象是本类名.class
public static synchronized void method(){
可能会产生线程安全问题的代码
}
c. Lock接口
//创建Lock锁对象
Lock ck = new ReentrantLock();
//上锁
ck.lock();
添加可能会产生线程安全问题的代码
//解锁
ck.unlock();
}
sleep: 不释放锁对象, 在休眠的时间内,不能唤醒
wait(): 释放锁对象, 在等待的时间内,能唤醒
多线程安全问题
案例:
电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “功夫熊猫3”,本次电影的座位共100个(本场电影只能卖100张票)。
我们来模拟电影院的售票窗口,实现多个窗口同时卖 “功夫熊猫3”这场电影票(多个窗口一起卖这100张票)
public class ThreadDemo {
public static void main(String[] args) {
//创建票对象
Ticket ticket = new Ticket();
//创建3个窗口
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
public class Ticket implements Runnable {
//共100票
int ticket = 100;
@Override
public void run() {
//模拟卖票
while(true){
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
问题:
t0,t1,t2线程都可能出现:当各自的线程在执行完if(ticket >0)的判断后,cpu资源被抢占,线程处于阻塞状态进入等待。但是当三个线程各自拿到cpu资源时,已经不需要做if(ticket >0) 的判断,这样就可能导致t0在执行完出票操作后,ticket = 0 ,而当t1,t2线程去执行的时候,ticket会出现负数的情况。
线程同步
同步代码块:
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
public class Ticket implements Runnable {
//共100票
int ticket = 100;
//定义锁对象
Object lock = new Object();
@Override
public void run() {
//模拟卖票
while(true){
//同步代码块
synchronized (lock){
if (ticket > 0) {
//模拟电影选坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
}
}
public synchronized void method(){
可能会产生线程安全问题的代码
}
public class Ticket implements Runnable {
//共100票
int ticket = 100;
//定义锁对象
Object lock = new Object();
@Override
public void run() {
//模拟卖票
while(true){
//同步方法
method();
}
}
//同步方法,锁对象this
public synchronized void method(){
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
}
}
同步方法之静态同步:
public static synchronized void method(){
可能会产生线程安全问题的代码
}
Q1:同步方法有锁吗?
有的,同步方法中的锁对象为本类对象引用this
Q2:静态同步方法有锁吗?
有的,静态同步方法中的锁对象为本类类名.class
Q3:采用同步还有弊端吗?
有的,如上例中,线程睡眠一旦产生异常,去捕获异常,同步方法不能结束,同步锁就不能被释放。因此JDK1.5后,采用Lock接口可替代synchronized
Lock接口:(强烈推荐使用)
使用Lock接口,以及其中的lock()方法和unlock()方法替代同步
public class Ticket implements Runnable {
//共100票
int ticket = 100;
//创建Lock锁对象
Lock ck = new ReentrantLock();
@Override
public void run() {
//模拟卖票
while(true){
//synchronized (lock){
ck.lock();
if (ticket > 0) {
//模拟选坐的操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
}
ck.unlock();
//}
}
}
}
死锁
当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。
synchronzied(A锁){
synchronized(B锁){
}
}
Q1:sleep()和wait()方法的区别?
sleep: 不释放锁对象, 释放CPU使用权
在休眠的时间内,不能唤醒
wait(): 释放锁对象, 释放CPU使用权
在等待的时间内,能唤醒
Q2:为什么wait(),notify(),notifyAll()等方法都定义在Object类中?
锁对象可以是任意类型的对象