同时满足以下两个条件时:
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
例子:四个线程卖100张票
public class ThreadTest {
public static void main(String[] args) {
synchronizeThread st = new synchronizeThread();
new Thread(st,"1").start();
new Thread(st,"2").start();
new Thread(st,"3").start();
new Thread(st,"4").start();
}
}
class synchronizeThread implements Runnable {
private int ticketNumber = 100;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (ticketNumber > 0) {
System.out.println("线程【" + Thread.currentThread().getName() + "】卖出了一张票,现在剩余了【" + ticketNumber + "】张票");
ticketNumber--;
} else {
break;
}
}
}
}
运行结果:发现会有多个线程卖同一张票的情况发生,这就是线程安全问题。
解决这样的问题就是线程同步的方式来实现。
同步就是协同步调,按预定的先后次序进行运行。这里的同步千万不要理解成那个同时进行,应是指协同、协助、互相配合。线程同步是指多线程通过特定的设置来控制线程之间`的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的
即用synchronized关键字修饰的语句块。
被该关键字修饰的语句块会自动被加上内置锁,被保护的语句代码所在的线程要执行,需要获得内置锁,否则就处于阻塞状态。
synchronized(object){
}
括号里的这个对象可以是任意对象,这个对象一般称为同步锁。
同步中必须有多个线程并使用同一个锁。
解决了线程的安全问题。
同步是一种高开销的操作,因此应该尽量减少同步的内容。 通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
package cn.hyl.test1;
public class ThreadTest {
public static void main(String[] args) {
synchronizeThread st = new synchronizeThread();
new Thread(st,"1").start();
new Thread(st,"2").start();
new Thread(st,"3").start();
new Thread(st,"4").start();
}
}
class synchronizeThread implements Runnable {
private Integer ticketNumber = 100;
private Object object = new Object();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (ticketNumber){
if (ticketNumber > 0) {
System.out.println("线程【" + Thread.currentThread().getName() + "】卖出了一张票,现在剩余了【" + ticketNumber + "】张票");
ticketNumber--;
} else {
break;
}
}
}
}
}
即用synchronized关键字修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
public synchronized void save(){}