java线程问题——线程锁synchronized和Lock

Posted Code_BinBin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程问题——线程锁synchronized和Lock相关的知识,希望对你有一定的参考价值。

什么是线程锁

多线程可以同时执行多个任务,但是当多个线程同时访问同一个数据时,可能导致数据不同步,甚至错误!
打个比方,你在银行里面存了一百万,这个时候你需要从里面取走九十万,你女朋友也要从里面取五十万,如果没有用线程锁,那么你们两个人同时取钱,就有可能导致线程错误,你们总共从银行取走一百四十万元,那么银行就会亏本,所以要用线程锁。

synchronized和Lock的区别

线程锁分synchronized和Lock,那么他们之间有什么区别呢?

区别synchronizedLock
锁的释放自动释放必须使用finally中必须释放,不然可能线程锁死
锁的状态无法判断可以判断
存在层次java的关键字一个类
性能少量同步多量同步
范围不但可以锁代码块,他还可以方法只有代码块

首先我们先不加锁写一个程序

package Thread;
public class mytest03 {
    public static void main(String[] args) {
        Money money=new Money(300);
        new Thread(money,"t1").start();
        new Thread(money,"t2").start();
    }
}
class Money implements Runnable
{
    int count;
    public Money(int count) {
        this.count = count;
    }

    @Override
    public  void run() {
        while (true)
        {
                if(count<=0)
                    break;
                else
                {
                    System.out.println(Thread.currentThread().getName()+"取出了1万元,----->还剩"+(count-1)+"万元");
                    count=count-1;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
}

看上去好像没有什么问题,那么我们来看一下结果

由于结果太多,我只截屏了部分,但是大家可以看到,数据有明显的错误,这个是为什么呢?

原来,线程是由cpu调度时间的,每个线程每次抢到的时间都是不一样的,这个就完全看cpu的性能和自己的造化了,当线程t1抢到时间准备对count进行计算的时候,线程t2也抢到了时间,并且在这个时候,t1还未对线程进行运算。
就在这个时候,两个线程同时对count进行了运算,那么打个比方,count从300开始,这个时候,两个线程就会同时输出299,但是,经过了两个线程的运算,每次减一,那么count就变成了298,所以下一次就会输出298,由于每次抢到的时间都不一样,所以输出的答案就不一样,但是无非都是错的答案,当然,如果你运气及其逆天,说不定可以碰对,不过我是没有碰对过,所以,我们就要应用线程锁。

线程锁synchronized

我们知道synchronized不仅可以锁代码块还可以锁方法,那么我们来看一下他是怎么锁方法的

package Thread;
public class mytest03 {
    public static void main(String[] args) {
        Money money=new Money(300);
        new Thread(money,"t1").start();
        new Thread(money,"t2").start();
    }
}
class Money implements Runnable
{
    int count;
    public Money(int count) {
        this.count = count;
    }

    @Override
    public synchronized void run() {
        while (true)
        {
                if(count<=0)
                    break;
                else
                {
                    System.out.println(Thread.currentThread().getName()+"取出了1万元,----->还剩"+(count-1)+"万元");
                    count=count-1;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
}

我们在上一个代码的基础上面给run()方法加了一个synchronized ,那么我们得到理想的结果了吗?我们不妨来看看

在这里插入图片描述
由于结果太长,我只截图了部分,但是大家运行后会发现,全是对线程t1进行的调度,t2变成了没人要的孤儿,这又是为什么呢?

原来,我们用synchronized把run()方法给锁住了,这个时候,t1开始被调用,t1使用run方法由于里面的while循环一直在持续,线程就是一直锁着的,t2无法被调用,那么就是t1在不断的被调用直至线程结束,打个比方,a和b要去卫生间上厕所,但是a先进去了,并且把门反锁了,这个时候b怎么样也进不去,除非a上完厕所自己出来。

那么我们要怎么写呢?请看代码

package Thread;
public class mytest03 {
    public static void main(String[] args) {
        Money money=new Money(300);
        new Thread(money,"t1").start();
        new Thread(money,"t2").start();
    }
}
class Money implements Runnable
{
    int count;
    public Money(int count) {
        this.count = count;
    }

    @Override
    public  void run() {
        while (true)
        {
            synchronized (this)
            {
                if(count<=0)
                    break;
                else
                {
                    System.out.println(Thread.currentThread().getName()+"取出了1万元,----->还剩"+(count-1)+"万元");
                    count=count-1;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }
}

在这里插入图片描述
这一次结果就对了,这是为什么呢?我们可以看到,我们在while里面给代码块加了一个锁,这样当某一个线程使用完里面的方法后,synchronized 就会解锁,这个时候另一个线程就可以进去了,同时线程就会锁死,那么这样就是真确的了,所以在我们上锁的时候,要考虑到是什么使结果发生了变化,就锁哪里。

线程锁Lock

package Thread;
import java.util.concurrent.locks.ReentrantLock;
public class testLock02 {
    public static void main(String[] args) {
        lock1 ll=new lock1();
        new Thread(ll,"小斌").start();
        new Thread(ll,"小龙").start();
        new Thread(ll,"小宇").start();
        new Thread(ll,"小俊").start();
    }
}

class lock1 implements  Runnable
{
     int a=600;
    private final ReentrantLock locking=new ReentrantLock();
    @Override
    public  void run() {
        while (true)
        {
            try {
                locking.lock();
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                 if(a>0)
                     System.out.println(Thread.currentThread().getName()+"获的第"+(a--)+"张票");

            }finally {
                locking.unlock();
            }
            if(a<=0)
                break;
        }
    }
}

我们可以看到,大致和synchronized 差不多,只不过是需要用 ReentrantLock方法new手动解锁,需要我们写一个try/cath,在finally处写“锁名.unlock”其余情况和synchronized 差不多,这里大家就自行思考比较好

以上是关于java线程问题——线程锁synchronized和Lock的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程之synchronized线程锁

java线程问题——线程锁synchronized和Lock

java多线程——锁机制synchronized(同步方法)

Java多线程5:synchronized锁方法块

Java 中线程同步机制synchronized,互斥锁,死锁,释放锁的详解

Java Synchronized 锁的实现原理详解及偏向锁-轻量锁-重量锁