这一章说的是线程的同步问题
先来个例子
MyThread.java
class MyThread implements Runnable{
int i = 100;
public void run(){
while(true){
//Thread.currentThread(),代表当前运行的线程
System.out.println(Thread.currentThread().getName() + i);
i --;
//使当前运行的线程让出CPU,再次就绪。
Thread.yield();
if(i < 0){
break;
}
}
}
}
Test.java
class Test{
public static void main(String args []){
MyThread myThread = new MyThread();
//生成两个Thread对象,但是两个Thread对象共用同一个线程体
Thread t1 = new Thread(myThread);
Thread t2 = new Thread(myThread);
//每一个线程都有名字,可以通过Thread对象的setName()方法设置线程名字,
//也可以使用getName()方法获取线程的名字。
t1.setName("线程a");
t2.setName("线程b");
//分别启动两个线程
t1.start();
t2.start();
}
}
运行部分结果如下图:
从上面的结果看到,线程b1和线程a1,没有3,没有0,显然,线程出错了
线程a在执行完输出线程a1之后,轮到线程b1执行,两线程都有自己的线程体,这时就是
两个线程都打印了一次1.所以,这个是线程操作不对,也是线程抢占的无规律性产生的数据访问安全问题。
这里介绍同步代码块
MyThread.java
class MyThread implements Runnable{
int i = 100;
public void run(){
while(true){
synchronized(this){
//Thread.currentThread(),代表当前运行的线程
System.out.println(Thread.currentThread().getName() + i);
i --;
//使当前运行的线程让出CPU,再次就绪。
Thread.yield();
if(i < 0){
break;
}
}
}
}
}
synchronized相当于同步锁,线程运行同步锁,就相当于拿了钥匙,可以执行下面的代码,而其他线程就要等待,等待这个线程运行完所有代码,
即使这个线程运行了代码的一半后,被其他线程抢占了,但是其他线程没有得到这个同步锁,也只能等待,直到这个线程运行完所有代码,释放出同步锁。
这就保护了数据的安全性。
编译运行就可以发现 ,刚刚的重复数字没有了,但是最后却有一个-1. 可能是因为两线程,一个线程break了,还有一个线程没有break。下面代码测试一下
MyThread.java
class MyThread implements Runnable{
int i = 100;
public void run(){
while(true){
synchronized(this){
//Thread.currentThread(),代表当前运行的线程
System.out.println(Thread.currentThread().getName() + i);
i --;
//使当前运行的线程让出CPU,再次就绪。
Thread.yield();
if(i < 0){
System.out.println(Thread.currentThread().getName() + "out");
break;
}
}
}
}
}
在break的前面加一句打印语句,把break的线程名+out打印出来。下面编译运行看结果。只看最后一段
显然,0的时候,线程b就break了,但是线程a还在运行,运行打印-1后,线程a也break了。程序停了。