本文小结关于synchronized线程同步机制的学习。首先需要了解什么是同步和异步,继承Thread类与实现Runnable接口的区别。
(一)简单的理解一下同步和异步:
同步就是指一个线程要等待上一个线程执行完之后才开始执行当前的线程,
异步是指一个线程去执行,它的下一个线程不必等待它执行完就开始执行。
(二)继承Thread类与实现Runnable接口的区别:
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;他们都是在java.lang包中定义的。
先看一个继承Thread类的例子:
public class Data {
private int x = 0;
public int getX(){
return this.x;
}
/**
* 对X进行加法运算
* @param y:加数
* @return:返回加法的结果
*/
public int plus(int y){
return this.x + y;
}
}
public class MySyn extends Thread {
private Data data = new Data();
public static void main(String args[]){
MySyn my1 = new MySyn();
MySyn my2 = new MySyn();
MySyn my3 = new MySyn();
//启动这三个线程对象
my1.start();
my2.start();
my3.start();
}
public void run(){
for(int i = 0; i < 3; i++){
this.data.plus(10);//给Data的x加上10
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "对应的Data对象的值是:" + this.data.getX());
}
}
}
这是继承Thread类实现的多线程,运行结果是:
Thread-2对应的Data对象的值是:10
Thread-1对应的Data对象的值是:10
Thread-0对应的Data对象的值是:20
Thread-2对应的Data对象的值是:20
Thread-1对应的Data对象的值是:20
Thread-0对应的Data对象的值是:30
Thread-1对应的Data对象的值是:30
Thread-2对应的Data对象的值是:30
显然,每个线程都各自执行自己的方法,都有自己的Data对象,不共享资源,互不干扰。
再来看看用Runnable实现的,简单修改部分代码:
public class MySyn implements Runnable {
private Data data = new Data();
public static void main(String args[]){
MySyn my = new MySyn();
Thread my1 = new Thread(my, "A");
Thread my2 = new Thread(my, "B");
my1.start();
my2.start();
}
······
运行结果有些令人不解:
A对应的Data对象的值是:20
B对应的Data对象的值是:20
B对应的Data对象的值是:40
A对应的Data对象的值是:40
A对应的Data对象的值是:60
B对应的Data对象的值是:60
虽然这两个线程对象是并发的,但是结果确不是我们预想的。因为,这两个线程共享着同一个Data对象,为了出线这样的效果,在打印结果之前让线程对象休眠了10毫秒,给另一个线程充分的时间去修改数据。但是,事实上,我们在实际运用中并不希望出现这种情况,这样会引起线程的不安全。正是这种不安全,引出了主题:线程同步机制。
那么这种实现多线程的方法的关联:
1.用Thread类实现多线程,没有用Runnable接口实现的灵活;另外, 查看源码Thread类实现了Runnable接口;
2.一个类只能有一个父类,但是可以有多个接口,继承了Thread类就不 能继承其他的类了;
3.Runnable接口适合资源的共享。
(三)线程的启动
一个线程大致可以分为 个状态:就绪(准备运行),运行,休眠(暂停),死亡。
线程的启动必须用start()方法启动,当一个线程对象调用start()方法时,就进入了就绪状态,等待系统的调度,不会阻塞程序,代码继续向下执行。不可以用run()方法来启动程序,当调用run()方法时,程序会把run()方法当做普通方法对待,直到执行完run()方法,程序才可以向下继续执行;相反,调用start()方法,JVM会自动调用线程中的run()方法。当调用run方法时:
public static void main(String args[]){
MySyn my1 = new MySyn();
MySyn my2 = new MySyn();
MySyn my3 = new MySyn();
my1.run();
my2.run();
my3.run();
}
的结果是:
main对应的Data对象的值是:20
main对应的Data对象的值是:30
main对应的Data对象的值是:10
main对应的Data对象的值是:20
main对应的Data对象的值是:30
main对应的Data对象的值是:10
main对应的Data对象的值是:20
main对应的Data对象的值是:30
此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。在JDK的文档中可以发现,一旦调用start()方法,则会通过JVM找到run()方法。