应用场景:多用户对同一银行账户进行取款
取款线程类,每个用户对应一个线程对象
多个用户共享同一个银行账户,使用Runnable解决
对于具体的实现过程中出现冲突问题时,是由于当一个账户判断银行账户中的数目大于给定值可以取款时,这时将cpu释放出去,去执行另一个线程,另一个线程执行完后,回到该线程继续执行时,实际账户中的数目已经不够但是因为之前已经判断,所以还会接着执行下去,这时就会出错。为了解决这个问题
同步代码块
synchronized(同步监视器){ * 必须是引用数据类型,不能是基本数据类型 * 在同步代码块中可以改变同步监视对象的值,但是不能改变其引用 * //account=new account * 建议使用final来修饰同步监视器 * 尽量不要使用String和包装类来做同步监视器 * 可以创建一个专门的同步监视器没有任何含义 * 共享资源用来做锁
将一定不能分开的放在synchronized(){}里面
synchronized (account){}
第一个线程来到同步代码块,发现同步监视器是open状态,关闭,然后执行其中的代码
第一个线程执行过程中,发生了线程切换(阻塞 就绪),第一个线程失去cpu,但是没有开锁
第二个线程获取cpu,来到同步代码块,发现同步监视器是close状态,无法执行其中的代码,第二个线程也进入阻塞状态
第一个线程再次获取cpu,来到同步代码块,继续执行后续代码,同步代码执行完毕,释放锁open
第二线程发现同步监视器是open状态,拿到锁并将锁变为close状态,有阻塞态进入就绪态,在进入运行态
优点:安全
缺点:效率低 可能出现死锁
同步方法
定义的方法上面加synchronized()
public void run() {
//取款
withdraw();
}
public synchronized void withdraw() { //同步方法的锁
if (account.getBalance() >= 400) { //临界代码
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.withDraw(400);
System.out.println(Thread.currentThread().getName()
+ "取款成功,当前余额:" + account.getBalance());
}
else{
//余额不足
System.out.println(Thread.currentThread().getName()
+ "取款失败,当前余额:" + account.getBalance());
}
}
public synchronized static void method1() {
//静态的同步方法的锁是类名.class 类对象AccountRunnable
Class clazz=AccountRunnable.class;
}
public synchronized static void method2() {
}
注意:
*非静态同步方法的锁:this
* 静态同步方法的锁:类名.class
*3.同步方法和同步代码块谁的效率高 : 同步代码块效率高
Lock锁
注意:如果同步代码有异常,要将unlock()写入finally语句块
Reentrantlock 可重入锁 会做一个计数
首先需要创建锁
private Lock lock = new ReentrantLock();
try {
if (account.getBalance() >= 400) { //临界代码
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.withDraw(400);
System.out.println(Thread.currentThread().getName()
+ "取款成功,当前余额:" + account.getBalance());
}
else{
//余额不足
System.out.println(Thread.currentThread().getName()
+ "取款失败,当前余额:" + account.getBalance());
}
} finally {
//解锁
lock.unlock();
}
线程同步练习:4个窗口卖100张票
使用同步代码块,不能够将while都放在同步代码块中,这样会使得一个线程全部执行完
锁的只能是执行一次
package synch4;
public class TicketRunnalble1 implements Runnable{
private int ticketnum = 100;
@Override
public void run() {
/**
* 加锁不能对整个循环加锁,只能够对买票的一个环节加锁
*/
while (true) {
synchronized (this) {
if (ticketnum <= 0) {
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第"
+ (100 - ticketnum+1) + "张票");
ticketnum--;
}
}
}
public static void main(String[] args) {
Runnable runnable = new TicketRunnalble1();
//创建4个线程模拟4个窗口
Thread thread1 = new Thread(runnable);
Thread thread2= new Thread(runnable);
Thread thread3 = new Thread(runnable);
Thread thread4 = new Thread(runnable);
thread1.setName("thread1");
thread2.setName("thread2");
thread3.setName("thread3");
thread4.setName("thread4");
//窗口开始买票(启动线程)
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
同步方法
public class TicketRunnalble2 implements Runnable{
private int ticketnum = 100;
@Override
public void run() {
while (ticketnum>0) {
sellOne();
}
}
public synchronized void sellOne() {
if (ticketnum <= 0) {
return;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第" + (100 - ticketnum+1) + "张票");
ticketnum--;
}
lock锁
private int ticketnum = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//上锁
lock.lock();
try{
if (ticketnum <= 0) {
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第" + (100 - ticketnum + 1) + "张票");
ticketnum--;
}finally {
lock.unlock();
}
}
}