又是忙碌的一天,Java结束了复习,老师也开始讲新的课程了。下午下课,很任性的没有去吃饭,后来与老师讨论时就说到了线程同步的问题。有了点自己的感触,想给大家分享一下,希望能让像我一样的程序员能有所收获。
我们都知道,多线程编程为程序开发带来了很多的便利,但是也带来了一些问题,这些问题时在程序开发过程中必须进行处理的。这些问题的核心是,如果多个线程同时访问一个资源,如变量、文件等,如何保证访问安全?
举个例子,假设有两个线程:线程A和线程B,并且程序为这两个线程开辟了一个公共的内存空间,情况则是线程B写一次,线程A读一次。那么则会出现如下几种情况:
1.在某个时候线程B运行速度比较快,在线程A未读取上一个数据之前,B就写了第二次数据,造成数据遗漏。
2.在某个时候线程
A
运行速度比较快,它读完一次数据之后,线程
B
还没来得及写,线程
A
又来读第二次。结果线程
A
读不到数据,导致运行出错。
3.线程
B
正在写数据时,线程
A
也来读取数据,这时可能线程
B
还没将数据写完,线程
A
将数据读走,导致程序出错。
接下来,就让我用代码来演示同步产生的错误的情况:
利用线程来模拟售票系统:
票类:
public class Tickets {
public int total;//在票这个类中定义票的数量
public Tickets(int total){//初始化
this.total=total;
}
public void action(String threadName){
System.out.println(threadName+"卖票之前"+total);
total--;
System.out.println(threadName+"卖票之后"+total);
}
}
线程类:
public class TicketsThread extends Thread{
private Tickets tickets;//票的对象
public TicketsThread(Tickets tickets) {//初始化票的对象
this.tickets=tickets;
}
@Override
public void run() {
while(true){
if(tickets.total<=1){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tickets.action(Thread.currentThread().getName());
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Tickets t=new Tickets(10);
TicketsThread tt1=new TicketsThread(t);
tt1.start();
TicketsThread tt2=new TicketsThread(t);
tt2.start();
}
}
通过测试类的运行结果为:
从结果来看,明显在我用荧光笔标识的地方有错误,明显这里应该是“Thread-0卖票之前3”,也就是读到了“脏数据”(就是不该读到的数据),再把测试类反复运行,总难免会有一些读错数据的情况,那么我们就不难发现线程在同步中也会产生一些无法预料的错误,那么究竟该怎么解决呢?
<注:在这里,我并没有说每次测试都是错误的,但是完全正确的几率十分低>
在多线程编程中,这种被多个线程同时访问的资源叫做临界资源,那么解决临界资源问题,最基本、最简单的思路就是使用同步关键字synchronized。
因为我们同时访问的所谓临界资源就是票这个类,所以只需要改正这里即可,以下是代码:
public class Tickets {
public int total;//在票这个类中定义票的数量
public Tickets(int total){//初始化
this.total=total;
}
public synchronized void action(String threadName){//定义同步化,即线程A访问时,线程B必须等待;线程B访问时,线程A必须等待
System.out.println(threadName+"卖票之前"+total);
total--;
System.out.println(threadName+"卖票之后"+total);
}
}
再让我们来看看输出的结果:
那么,这个关键字的作用到底是什么呢?让我们再回到我一开始举的例子上,我们可以这样理解,当有此关键字存在时“定义同步化,即线程A访问时,线程B必须等待;线程B访问时,线程A必须等待”。
希望能给一些和我一样的初学者一些帮助,也希望技术大牛们多多指点。