线程的同步,首先要了解一下线程的生命周期。
jdk中用Thread.State枚举来表示线程的几种状态。要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态:
1.新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态;
2.就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件;
3.运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态,run()方法定义了线程的操作和功能;
4.阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态;
5.死亡:线程完成了它的全部工作或线程被提前强制性地中止。
线程在运行阶段,如果多个线程有共享资源,就会很容易产生同步问题。例如窗口出售车票,如果按照下面线程和客户端写法:
<span style="font-family:KaiTi_GB2312;font-size:18px;">class Window1 implements Runnable{
int ticket = 100;
public void run(){
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"售出票号是:"+ ticket--);
}else{
break;
}
}
}
}</span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">public class TestWindow1 {
public static void main(String[] args){
Window1 w = new Window1();
Thread t1 =new Thread(w);
Thread t2 =new Thread(w);
Thread t3 =new Thread(w);
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.start();
t2.start();
t3.start();
}
}</span>
通过结果可以看出,100号的车票被两个窗口售出肯定是不符合逻辑的。这就出现了线程安全的问题。
问题的原因 :当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。也就是线程1在准备打印车票时此时还未ticket--,而cpu交给了线程2,线程2执行完打印车票打印出100后交给了线程1继续执行,就产生上面这个问题。
java中实现线程的安全,通过通过对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。java中实现线程的安全同步机制主要有两种:
1.同步代码块
<span style="font-family:KaiTi_GB2312;font-size:18px;"> synchronized(同步监视器){
需要被同步的代码块(即为操作共享数据)
}</span>
上面的代码我们可以更改为:
class Window2 implements Runnable{
int ticket = 100;
public void run(){
while(true){
// synchronized(obj){
synchronized(this){ // this在这里充当锁
if(ticket > 0){
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"票号为Ϊ:"+ ticket--);
}
}
}
}
}
2.同步方法
synchronized可以放在方法声明中,表示整个方法为同步方法。例如:
public synchronized void show(String name){
....
}
因此上面的方法我们还可以改为:
class Window4 implements Runnable {
int ticket = 100;
public void run() {
while (true) {
show();
}
}
public synchronized void show() {
if (ticket > 0) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售出车票:" + ticket--);
}
}
}
这里的共享数据就是多个线程共同操作的同一个数据(变量)
同步监视器是由一个类的对象来充当。哪个线程获取此监视器,谁就执synchronized()方法中被同步的代码,俗称锁。在线程同步中所有的线程必须共用同一把锁,在实现的方式中,考虑同步的话,使用this来充当所,但是在继承的方式中要慎用this。