java线程问题——线程锁synchronized和Lock
Posted Code_BinBin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程问题——线程锁synchronized和Lock相关的知识,希望对你有一定的参考价值。
什么是线程锁
多线程可以同时执行多个任务,但是当多个线程同时访问同一个数据时,可能导致数据不同步,甚至错误!
打个比方,你在银行里面存了一百万,这个时候你需要从里面取走九十万,你女朋友也要从里面取五十万,如果没有用线程锁,那么你们两个人同时取钱,就有可能导致线程错误,你们总共从银行取走一百四十万元,那么银行就会亏本,所以要用线程锁。
synchronized和Lock的区别
线程锁分synchronized和Lock,那么他们之间有什么区别呢?
区别 | synchronized | Lock |
---|---|---|
锁的释放 | 自动释放 | 必须使用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和Lock
java多线程——锁机制synchronized(同步方法)