上一篇:迅速读懂Java线程的交通规则 | 带你学《Java语言高级特性》之八
【本节目标】
通过阅读本节内容,你将了解到线程同步问题出现的原因,并学会使用synchronized关键字实现多个线程同时只有一个能进行调用的限制,解决线程同步问题。
在多线程的处理之中,可以利用Runnable描述多个线程操作的资源,而Thread描述每一个线程对象,当然多个线程访问同一资源时如果处理不当就会产生数据的错误操作。
同步问题的引出
下面编写一个简单的卖票程序,将若干个线程对象实现卖票的处理操作。
范例:实现卖票操作
class MyThread implements Runnable {
private int ticket = 10 ; //总票数为10张
@Override
public void run() {
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 mt=new MyThread() ;
new Thread(mt,"票贩子A").start() ;
new Thread(mt,"票贩子B").start() ;
new Thread(mt,"票贩子C").start() ;
}
}
图一 卖票程序执行
此时的程序将创建3个线程对象,并且这三个线程将进行10张票的出售。此时的程序在进行卖票处理的时候,并没有任何的问题(假象),下面可以模拟一下卖票中的延迟操作。
class MyThread implements Runnable {
private int ticket = 10; //总票数为10张
@Override
public void run() {
while (true) {
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;
}
}
}
}
图二 卖票问题执行操作
这个时候追加了延迟,问题就暴露出来了,而实际上这个问题一直都在。
图三 卖票处理
线程同步处理
经过分析之后已经可以确定同步问题产生的主要原因了,那么下面就需要进行同步问题的解决,但是解决同步问题的关键是锁,指的是当某一个线程执行操作的时候,其他线程外面等待;
图四 问题的解决
如果要想程序中实现这把锁功能,就可以使用synchronized关键字来实现,利用此关键字可以定义同步方法或同步代码块,在同步代码块的操作中的代码只允许一个线程执行。
1、利用同步代码块进行处理:
synchronized (同步对象){
同步代码操作;
}
一般要进行同步对象处理的时候可以采用当前对象this进行同步。
范例:利用同步代码块解决数据同步访问问题
class MyThread implements Runnable {
private int ticket = 10 ; //总票数为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 ThreadDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
加入同步处理之后,程序的整体性能下降了。同步实际上会造成性能的降低。
2、利用同步方法解决:只需要在方法定义上使用synchronized关键字即可。
class MyThread implements Runnable {
private int ticket = 10; //总票数为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 ThreadDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
图五 解决同步问题
在日后学习Java类库的时候,系统中许多的类上使用的同步处理采用的都是同步方法。
想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
本内容视频来源于阿里云大学