Synchronized
线程同步机制简介
可以理解为某人进厕所后把门关上(上锁),然后出来的时候剩下的人才能进去。
售票问题的解决
class AA implements Runnable{
public static int TicketNums = 100; //共享票数
private boolean loop = true;
public synchronized void sell(){
//把售票设为一个同步方法,这样每次只有一个线程访问
if(TicketNums <=0){
System.out.println("售票结束");
loop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
TicketNums--; //
System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票"
+ " 剩余票数:" + TicketNums);
}
@Override
public void run() {
while(loop){ //持续贩卖
sell();
}
}
}
public static void main(String[] args) throws InterruptedException {
AA aa = new AA();
new Thread(aa).start();
new Thread(aa).start();
new Thread(aa).start(); //一个对象创造三个线程
}
互斥锁
当一个方法设为synchronized,就相当于加了一把锁(如果锁在对象上那就是对象锁),t1,t2,t3会争夺这把锁。
当一个线程争夺到时,锁会被打开,然后执行方法。
当方法执行完毕后,继续锁上,然后线程继续争抢。
public synchronized void sell() {} 就是一个同步方法,此时锁在this对象上。
当然也可以在代码块上写synchronize,同步代码块,互斥锁还是this对象。
public void sell(){
synchronized(this){ //同步代码块
if(TicketNums <=0){
System.out.println("售票结束");
loop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
TicketNums--; //
System.out.println("窗口 " + Thread.currentThread().getName() + "售出一张票"
+ " 剩余票数:" + TicketNums);
}
}
5. 非静态同步方法的锁可以是当前对象,也可以是其它对象(要求是同一个对象,比如Object)。
Object object = new Object();
AA aa = new AA();
BB bb = new BB();
public void sell(){
synchronized (object){ //只有object可以,父类(bb)和自己(aa)都不行
//多个线程访问同一个对象
if(TicketNums <=0){
System.out.println("售票结束");
loop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
TicketNums--;
System.out.println("窗口 " + Thread.currentThread().getName() +
"售出一张票" + " 剩余票数:" + TicketNums);
}
}
6. 静态的同步方法的锁为当前类本身。
public static void m2(){
synchronized (Test.class){ //不能写this了,要写 当前类名.class
System.out.println("静态方法中的同步代码块");
}
}
有一个重点要说明的: 多个线程的锁对象应为同一个!
synchronized (this){...} //同步代码块
public synchronized void sell(){...} //同步方法
对于以上两种同步写法,所使用的对象都是this(也就是给this加了把锁),这样的写法对下面的主函数是有用的: 因为它们对应的都是一个对象,对应的方法也是同一个。
AA aa = new AA();
new Thread(aa).start();
new Thread(aa).start();
new Thread(aa).start(); //一个对象创造三个线程
但是对于下面这种写法就不管用了:
AA aa1 = new AA();
AA aa2 = new AA();
AA aa3 = new AA();
new Thread(aa1).start();
new Thread(aa2).start();
new Thread(aa3).start();
这里创建了三个不同的对象,因此它们的锁各管各的,相当于有三把锁,每个线程每次都能抢到锁。因此就不管用了。
线程死锁
比如: 妈妈:你先完成作业,才让你玩手机。 小明:你先让我玩手机,我才完成作业。
释放锁
释放锁:从 线程调度器中出来(线程状态图的绿色块)就释放了锁,如果没有出来就是没有释放