小结:
同步方式:
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类中?
锁对象可以是任意类型的对象