前面的章节介绍到,当有两个线程去操作一个数据时,会出现安全隐患。在这一节中我们将来解决这个问题。
需要用到线程的同步。即synchronized关键字。
使用synchronized关键字有两个前提:
1.有两个以上的线程。
2.使用同一个锁。
1.同步代码块
下面针对前面的问题对代码进行一些改进,加入同步代码块。
public class RunnableDemo1 implements Runnable{
private int i;
Object obj = new Object();
@Override
public void run() {
synchronized (obj){
while (i<600){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"实现Runnable接口的线程类运行:"+i++);
}
}
}
}
同步的代码块必须要有一个锁,这里使用的是obj这个对象。
再次运行测试代码就可以看见程序正确的输出了。
2.同步方法
synchronized关键字用在方法上也是同样的效果。同步方法使用的锁是this。
下面是使用同步方法实现的解决方案
public class RunnableDemo1 implements Runnable{
private int i;
@Override
public void run() {
while (i<600){
print();
}
}
public synchronized void print(){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"实现Runnable接口的线程类运行:"+i++);
}
}
3.类锁(static修饰的共享数据)
当共享数据被static关键字修饰时这时我们这个类的所有实例共享这个数据。
将i改写成类变量
public class RunnableDemo1 implements Runnable{
private static int i;
Object obj = new Object();
@Override
public void run() {
while (i<600){
synchronized (obj){
//print();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"实现Runnable接口的线程类运行:"+i++);
}
}
}
}
编写测试类:
public class Test {
public static void main(String[] args){
RunnableDemo1 demo1 = new RunnableDemo1();
RunnableDemo1 demo2 = new RunnableDemo1();
Thread t1 = new Thread(demo1);
Thread t2 = new Thread(demo2);
t1.start();
t2.start();
}
}
这里是使用两个实例来开启的两个线程。而锁还是一个简单的对象锁。
运行程序。
可以看到出现了大量的重复数据。
这是因为我们操作的是类变量,这里需要使用类锁。
只需将同步的锁由obj改成当前的Class即可