这一篇博客,是我学习完线程基础一后突然觉得要写下来记录一下,也方便自己以后查找学习!
本博客仅供参考,大神请绕路,有说的不对的地方望大家多指教!
其他不多说了,先上个例子吧!
public class study1 extends Thread{
private int count=5;
public void run() {
count--;
System.out.println(this.currentThread().getName()+":count="+count);
}
public static void main(String[] args) {
study1 s=new study1();
Thread t1=new Thread(s,"t1");
Thread t2=new Thread(s,"t2");
Thread t3=new Thread(s,"t3");
Thread t4=new Thread(s,"t4");
Thread t5=new Thread(s,"t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
上面的例子,在没有锁的情况下,五个线程都可以访问,本来我们是想要输出的是 4 3 2 1 0,这样的结果,可是事实却与我们想要的不一样,运行上面的代码输出的结果如下:
t2:count=3
t4:count=0
t5:count=0
t3:count=2
t1:count=3
这显然不是我要的结果,这时线程是不安全的,这是为什么呢,原因就是五个线程同时去操作了同一个方法,这时,显然count的值是一起使用的,这就可能会造成,第一个和第二个同时操作完成。
比如,上面的例子,我们假设只有两个线程运行,当count的值等于5时,t1和t2同时执行run方法里的代码,这时它们拿到的值都是count=5,然后两个线程t1,t2都同时对count进行减一的操作,再进行输出,这时的结果可能t1的输出结果是count=4,t2的结果是count=3;或者两个的输出结果都是count=3,因为这两个线程同时执行run方法,看哪个线程拿到了cpu的资源,(假如t1执行减一操作和输出操作的时候都拿到了cpu的资源,这时t1的输出结果就一定是4,t2的输出结果是3。)所以造成了这样的结果。
为了避免以上的情况,并拿到我们想要的结果,这时使用synchronized关键字就显得非常有必要了,如:
//synchronized加锁
public synchronized void run() {
count--;
System.out.println(this.currentThread().getName()+":count="+count);
}
我们将上面的例子的run方法来修饰,这时再去执行上面的例子结果将是:
t1:count=4
t4:count=3
t2:count=2
t5:count=1
t3:count=0
现在结果是我们想要的了,这时,线程是安全的了,因为使用synchronized关键字修饰了run方法后,就像给run方法上了一把锁,就像我们上厕所,多人去上一个厕所,先去的那个进去了把门锁住了,其他人就进不去上厕所了,必须等前面上厕所那个人出来才能下一个人去上厕所,上面的例子也是同样的道理,总之synchronized关键字就相当于一把锁一样,给run方法上了锁。因此线程是安全的。
当多个线程访问s对象的run方法时,以排队的方式进行处理(这里的排队是按照cpu分配的先后顺序而定)一个线程先要执行synchronized修饰的方法里的代码:
1.尝试获得锁
2.如果拿到锁,执行synchronized代码体内容;拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止, 而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)。
我们再仔细看上面的输出结果,不是按照t1,t2,t3,t4,t5的顺序来输出的,这时由于排队是按照cpu分配的先后顺序而定。由于很多个线程同时去争夺同一把锁,看谁先拿到那把锁,谁就可以先执行,上面的输出结果也就是因为这样而出现的。
关于锁竞争问题,后面有时间再解释一下,这里就不多说了。