死锁
死锁产生的四个必要条件(只需要避免其中一个就行)
1,互斥条件:一个组组员每次只能被一个进程使用
2,请求于保持条件:一个进程因请求资源而阻塞时已获得的资源保持不放
3,不剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺
4,循环等条件:若干进程之间形成一种头尾相接的循环等待资源
package TestSynchronized;
public class DeadLock {
public static void main(String[] args) {
Makeup g1=new Makeup(0,"灰姑凉");
Makeup g2=new Makeup(1,"白雪公主");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread{
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choose;
String girlName; //使用化妆品的人
Makeup(int choose,String girlName){
this.choose=choose;
this.girlName=girlName;
}
@Override
public void run() {
try {
Makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆,互相持有对方的锁,需要拿到对方的资源
private void Makeup() throws InterruptedException {
if(choose==0){
synchronized (lipstick) { //获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror){
System.out.println(this.girlName+"一秒钟后你获得了镜子的锁");
}
}else {
synchronized (mirror) { //获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick){
System.out.println(this.girlName+"二秒钟后你获得了口红的锁");
}
}
}
}
synchronized
synchronized(obj)称之为同步监视器
需要锁的对象为需要增删改的对象
锁的执行过程
1,第一个线程,锁定同步监视器,执行其中的代码
2,第二个线程,发现同步监视器被锁定,无法访问
3,第三个线程,线程访问完毕,解锁同步监视器
4,第四个线程,发现同步监视器没有锁,让那后锁定并访问
锁会导致的问题:
1,一个线程持有锁会导致其他所有需要此锁的线程挂起
2,在多线程的竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
3,如果优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引起性能问题
如果有多个对象要锁的解决方法:
可以将多个对象放到一个类中,然后锁这个类
package TestSynchronized;
//不安全的买票
//会出现负数
//加一个syncronized
public class testTickets implements Runnable{
public static void main(String[] args) {
// TODO Auto-generated method stub
testTickets testTickets =new testTickets();
Thread thread =new Thread(testTickets,"小红");
Thread thread2 =new Thread(testTickets,"小刘");
Thread thread3 =new Thread(testTickets,"小蓝");
thread.start();
thread2.start();
thread3.start();
}
class buyTickets implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
}
}
private int ticketNumbers=10;
private boolean flag =true; //外部停止方式
@Override
public void run() {
// TODO Auto-generated method stub
//买票
while (flag) {
buy();
//模拟延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//synchronized 同步方法,锁的时this
//锁的对象就是变化的量 增删改
private synchronized void buy(){
//判断是否有票
if (ticketNumbers<=0) {
flag=false;
return ;
}
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNumbers--);
}
}
// Lock锁
使用方法:通常将锁放在try finally方法里
1,Lock是显示锁(手动开启和关闭锁)Synchrocized是隐式锁,出了作用域自动释放
2,Lock只有代码锁,Synchrocized有代码块锁和方法锁
优先使用顺序:Lock>同步代码块(已经进入方法体,分配了相应的资源)>同步方法(在方法体之外)
package TestSynchronized;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TestTicket testLock=new TestTicket();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
}
}
class TestTicket implements Runnable{
int ticketNums=10;
//定义Lock锁
private final ReentrantLock lock=new ReentrantLock(); //定义lock锁
@Override
public void run() {
while(true){
try{
lock.lock(); //加锁
if(ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else{
break;
}
}finally { //通常将锁放在try finally方法里
//解锁
lock.unlock();
}
}
}
}