线程的同步与死锁
在多线程的处理之中,可以利用Runnable描述多个线程操作的资源,而Thread描述每一个线程对象,于是当多个线程访问同一资源的时候如果处理不当就会产生数据的错误操作。
同步问题引出
编写一个简单的卖票程序,创建若干个线程对象实现卖票的处理操作。
class MyThreadTic 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 TicketDemo {
public static void main(String[] args) {
MyThreadTic mt = new MyThreadTic();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
此时的程序将创建三个线程对象,并且这三个线程对象将进行10张票的出售。此时程序进行卖票处理时没有任何问题(假象),追加延时后问题会暴露出来(出现票数为负)。
此时要保证线程数据同步。
线程同步处理
解决同步问题的关键是“锁”,即当某一个线程执行操作的时候,其他线程外面等待;
实现这把锁,可以使用synchronized关键字来实现,利用此关键字可以定义同步方法或同步代码块,在同步代码块中的代码只允许一个线程执行。
synchronized(同步对象){
同步代码操作;
}
范例:一般进行同步对象处理的时候可以采用当前对象this进行同步。
class MyThreadTic implements Runnable{
private int ticket = 10; //总票数
@Override
public void run() {
while (true) {
synchronized (this) {
//每一次只允许一个线程进行访问
if(this.ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买票,ticket="+ this.ticket--);
} else {
System.out.println("票已卖完!");
break;
}
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
MyThreadTic mt = new MyThreadTic();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
加入同步处理之后,程序的整体性能下降了,同步实际上会造成性能的降低。
范例:利用同步方法解决,只需要在方法定义上使用synchronized关键字即可。
class MyThreadTic implements Runnable{
private int ticket = 10; //总票数
public synchronized boolean sale() {
if(this.ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买票,ticket="+ this.ticket--);
return true;
} else {
System.out.println("票已卖完!");
return false;
}
}
@Override
public void run() {
while (this.sale()) {
;
}
}
}
public class TicketDemo {
public static void main(String[] args) {
MyThreadTic mt = new MyThreadTic();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
在日后学习系统类库时,发现系统的中许多类上使用的同步处理采用的都是同步方法。
线程死锁
死锁是在进行多线程同步的处理中可能产生的问题。所谓的死锁指的是若干个线程彼此等待的状态。
观察死锁
class Person{
public synchronized void say(Dog dog) {
System.out.println("out!");
dog.get();
}
public synchronized void get() {
System.out.println("Nice!");
}
}
class Dog{
public synchronized void say(Person per) {
System.out.println("wangwang!");
per.get();
}
public synchronized void get() {
System.out.println("shit!");
}
}
public class DeadLock implements Runnable {
private Person per = new Person();
private Dog dog = new Dog();
@Override
public void run() {
per.say(dog);
}
public DeadLock() {
new Thread(this).start();
dog.say(per);
}
public static void main(String[] args) {
new DeadLock();
}
}
造成死锁的主要原因是彼此等待,等待对方让出资源。死锁实际上是一种开发中出现的不确定的状态,有的时候代码处理不当则会不定期出现死锁,属于正常开发中的调试问题。
若干个线程访问同一资源时,一定要进行同步处理,而过多的同步会造成死锁。