线程的同步与锁死
同步问题
在多线程处理之中,可以利用Runnable描述多个线程操作的资源,而Thread描述每一个线程对象,于是当多个线程访问同一资源的时候,如果处理不当就会产生数据错误;
同步问题的引出
下面编写一个简单的买票程序,将创建若干个线程对象实现卖票的操作;
范例:实现买票操作
class Mythread implements Runnable{
private int ticket=10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
{
if(this.ticket>0)
{
System.out.println(Thread.currentThread().getName()+"卖票、ticket="+this.ticket--);
}
else {
System.out.println("票已经抢光");
break;
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Mythread my=new Mythread();
new Thread(my,"线程A").start();
new Thread(my,"线程B").start();
new Thread(my,"线程C").start();
}
}
此时线程的三个对象将进行10张票的出售
程序执行到目前为止还算正常,但是当我们增加休眠操作用来模拟现实中的网络延迟时就会发现问题
class Mythread implements Runnable{
private int ticket=10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
{
if(this.ticket>0)
{
try {
Thread.sleep(100);//增加休眠操作用来模拟网络延迟效果
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票、ticket="+this.ticket--);
}
else {
System.out.println("票已经抢光");
break;
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Mythread my=new Mythread();
new Thread(my,"线程A").start();
new Thread(my,"线程B").start();
new Thread(my,"线程C").start();
}
}
可以发现在这个时候居然出现了票数为0,-1的情况,这就是同步问题的引出
对同步问题进行分析
三个票贩子(三个线程)都对同一块数据进行操作(为方便我们假设这个时候就只有最后一张票了),我们一步一部观察
第一步
当线程进入时先进行判段是否有票,现在就剩一张票所以第一个线程可以进入,这个时候可能有第二个线程进入但这个时候ticket还是1;
第二步
用sleep()对网络延迟进行模拟,当第一个线程进入后开始休眠,休眠后开始执行ticket–操作,这个时候可能第二个线程也进入,但是第一个线程可能已经进入第三步;
第三步
当第一个线程ticket–完成时,第二个线程的已经通过判断也就是说它也可以进入第三步,这个时候票数为0,再进行–操作就变为-1了
线程同步处理
经过分析我们已经发现了同步问题产生的主要原因了,那么接下来就需要进行同步问题的解决,解决同步问题的关键就是锁
锁:指的就是当某个线程执行操作的时候其他线程外面等待;
如果想要实现锁的功能,就可以使用synchronized关键字来实现,利用此关键字可以定义同步方法或同步代码块,再同步代码块的操作里面的代码只允许一个线程执行
1.利用同步代码块进行处理
synchronized(同步对象){
同步操作;
}
一般进行同步对象处理的时候可以采取当前对象this进行同步
范例:利用同步代码块解决数据同步访问问题
class Mythread implements Runnable{
private int ticket=10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
{
synchronized (this) {//每一次只允许一个线程通过
if(this.ticket>0)
{
try {
Thread.sleep(100);//增加休眠操作用来模拟网络延迟效果
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票、ticket="+this.ticket--);
}
else {
System.out.println("票已经抢光");
break;
}
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Mythread my=new Mythread();
new Thread(my,"线程A").start();
new Thread(my,"线程B").start();
new Thread(my,"线程C").start();
}
}
程序可以正常执行,但是加入同步之后程序的整体执行性能下降了。同步实际上会造成性能的降低
2.利用同步方法解决:只需要在方法定义上使用sychronized关键字即可
范例:
class Mythread implements Runnable{
private int ticket=10;
public synchronized boolean sale()
{
if(this.ticket>0)
{
try {
Thread.sleep(100);//增加休眠操作用来模拟网络延迟效果
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票、ticket="+this.ticket--);
return true;
}
else {
System.out.println("票已经抢光");
return false;
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(this.sale())
{
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Mythread my=new Mythread();
new Thread(my,"线程A").start();
new Thread(my,"线程B").start();
new Thread(my,"线程C").start();
}
}
同样可以达到同步的效果
线程锁死
造成线程死锁的主要原因是因为彼此都在互相等待着,等待着对方让出资源
范例:线程死锁
public class Deadlock implements Runnable{
A a=new A();
B b=new B();
@Override
public void run() {
// TODO Auto-generated method stub
a.say(b);
}
public Deadlock(){
new Thread(this).start();
b.say(a);
}
public static void main(String[] args) {
new Deadlock();
}
}
class A{
public synchronized void say(B b) {
System.out.println("A:想要通过就要先给钱");
b.get();
}
public synchronized void get() {
System.out.println("A:你给我钱了,可以让你通过");
}
}
class B{
public synchronized void say(A a)
{
System.out.println("B:我必须要先过去才能给你钱");
a.get();
}
public synchronized void get()
{
System.out.println("B:我通过了,可以给你钱");
}
}
发现程序一直锁死在这里一直运行但没有输出get()内的语句