java多线程——同步与锁

一、线程同步
java多线程的同步是为了防止多个线程对数据对象进行操作时,损坏数据。
那么什么时候用到同步呢?在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。
那什么叫同步方法呢?当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
比如:
银行里有一1000元钱,有两个人同时取900元,来模拟线程的同步。

public class ThreadTest1 {

    public static void main(String[] args) {
        Thread01 r = new Thread01();
        Thread ta = new Thread(r, "A客户来取钱");
        Thread tb = new Thread(r, "B客户来取钱");
        ta.start();
        tb.start();
    }
}

// 测试锁和同步
class Money {
    int x = 1000;

    public  int fun(int y) {
        if (x < 0) {
            return -1;
        }else if(x<y){
            return -2;
        } else {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            x -= y;
            return x;
        }
    }
}

class Thread01 implements Runnable {
    Money money = new Money();
    @Override
    public void run() {
        int y=900;
        int z=money.fun(y);
        if(z==-1||z==-2){
        System.out.println(Thread.currentThread().getName() );
            System.out.println("钱不够了或没钱了。");
        }else{
            System.out.println(Thread.currentThread().getName() +" 。取了"+y+"元。");
            System.out.println("剩余的钱为: " + z);
        }
    }
}
/*
运行结果为:
B客户来取钱 。取了900元。
剩余的钱为: 100
A客户来取钱 。取了900元。
剩余的钱为: 100
*/

从运行结果来看
A和B同时取钱,并且都取走了900元钱,但是银行里只有1000元钱,因此,上面的代码有问题。那代码有什么问题呢?当第一个线程进入else语句块后,有一个简短的休眠,那么在第一个线程休眠的过程中,第二个线程也成功进入了这个else语句块(因为存款的钱还没有取走),当两个线程结束休眠后,不再进行逻辑判断而是直接将钱取走,所以两个线程都取到了900元钱,并且这里的两个线程不知道它们的先后关系。
怎么才能保证只有一个线程访问呢,这里需要用到synchronized关键字,即线程锁。
二、锁
1.锁的原理
java中每个对象都会有一个内置的锁,如果一个线程A获得这把锁的话,其他线程就没法获得了,直到线程A释放这把锁,其他线程才能获得该锁。也就是说任何线程都不能进入某一对象上的synchronized代码块,直到该锁释放,才能进入,而锁的释放是指有这把锁的线程退出了synchronized代码块。
2.锁与同步
(1)锁只能同步方法,不能同步变量和类;
(2)一个类中可以同时拥有同步和非同步方法;
(3)如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制;
(4)线程sleep时也不会释放锁;
(5)一个线程可以获得多个锁;
(6)同步应尽可能缩小范围,同步不但可以同步整个方法,也可以同步方法中部分代码;
(7)在使用同步代码块时,要指定要获取的对象;
(8)要同步静态方法,需要一个用于整个类对象的锁;
(9)静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
针对上面举得那个例子,这里进行改进,让它更贴近生活。

public class ThreadTest1 {

    public static void main(String[] args) {
        Thread01 r = new Thread01();
        Thread ta = new Thread(r, "A客户来取钱");
        Thread tb = new Thread(r, "B客户来取钱");
        ta.start();
        tb.start();
    }
}

// 测试锁和同步
class Money {
    int x = 1000;

    public synchronized int fun(int y) {
        if (x < 0) {
            return -1;
        }else if(x<y){
            return -2;
        } else {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            x -= y;
            return x;
        }
    }
}

class Thread01 implements Runnable {
    Money money = new Money();
    @Override
    public void run() {
        int y=900;
        int z=money.fun(y);
        if(z==-1||z==-2){
            System.out.println(Thread.currentThread().getName() );
            System.out.println("钱不够了或没钱了。");
        }else{
            System.out.println(Thread.currentThread().getName() +" 。取了"+y+"元。");
            System.out.println("剩余的钱为: " + z);
        }
    }
}
/*
运行结果为:
A客户来取钱 。取了900元。
剩余的钱为: 100
B客户来取钱
钱不够了或没钱了。
*/

从运行结果可以看出来,A取了900元后,银行里剩了100元,B来取900元的时候,钱不够了,不能再取钱了。也就是说,一个线程开始执行一个锁定的方法后就阻止了其他线程再去执行这个方法,直到本线程结束后,其他线程才能继续执行这个方法。
三、死锁
死锁的定义: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
产生死锁的条件:
(1)互斥条件:所谓互斥就是进程在某一时间内独占资源。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
以两个线程想互锁为例:

public class ThreadDeadLock implements Runnable{
    int flag=0;
    static Test1 t1=new Test1();
    static Test2 t2=new Test2();
    public static void main(String[] args) {
        ThreadDeadLock tdl1=new ThreadDeadLock();
        ThreadDeadLock tdl2=new ThreadDeadLock();
        tdl1.flag=0;
        tdl2.flag=1;
        Thread td1=new Thread(tdl1);
        Thread td2=new Thread(tdl2);
        td1.start();
        td2.start();

    }

    @Override
    public void run() {
        System.out.println("flag= "+flag);
        if(flag==0){
            synchronized(t1){
                System.out.println("锁定了Test1.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                synchronized(t2){
                    System.out.println("锁定了Test2.");
                }
            }
        }
        if(flag==1){
            synchronized(t2){
                System.out.println("锁定了Test2.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                synchronized(t1){
                    System.out.println("锁定了Test1.");
                }
            }
        }
    }
}

class Test1{

}

class Test2{

}
/*运行结果为;
flag= 0
锁定了Test1.
flag= 1
锁定了Test2.
*/

运行结果为,程序一直在运行,不会结束。
原因是:两个线程都先锁住了自己,但又想去锁住对方,这样就造成了死锁现象。
那怎么解决死锁问题呢?要解决死锁的话,可以从死锁的四个条件去解决,只要破坏了一个必要条件,那么死锁问题就解决了。

猜你喜欢

转载自blog.csdn.net/levi_moon/article/details/51588165