①多个线程执行的不确定性引起执行结果不稳定
②多线程对数据的共享,造成操作不完整性,破坏数据
某个执行中的线程操作过程中,尚未完成相关操作时,其他线程参与进来也执行相同的操作,这时就会出现线程安全问题
当一个线程a在操作共享数据时,使其他线程不能参与进来,直到a的相关操作完成时,其他线程才可以操作,即使a被阻塞,也不能被改变
在java中通过同步机制来解决线程安全问题
同步代码块
Synchronized(同步监视器){
//需被同步的代码
}
说明:
操作共享数据的代码即为需被同步的代码,中间不能包多了,也不能包少了,包多包少都会造成线程安全问题
共享数据:
多个线程共同操作的变量
同步监视器:
即锁,任何一个类的对象都可以充当锁,在Runnable方式中用this代替
助解:
锁对象可以是任何对象,但对于多线程来说必须是同一个对象
在对象中有一个对象头的区域,在对象投中有一个标志位用来存放锁的状态.在对象进入后锁状态由无锁装换为有锁,使得其他对象不能再进入,在所需代码执行完毕后,锁状态又由有锁转换为无锁
要求:
①多个线程必须用同一把锁
②使用同步代码块的方式解决继承Thread类的线程安全问题:
注意:
Thread类中有多个对象,要慎用this充当同步监视器
1.使用static修饰锁
2.synchronized(Window.class)
栗子:
static class Windows implements Runnable {
@Override
public void run() {
while(true){
synchronized (this){ //采用同步代码块的方式解决线程安全问题
if (ticket>0){
System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
ticket--;
}else {
break;
}
}
}
}
}
synchronized修饰方法时,并没有显示的让我们添加锁标注对象,在修饰非静态方法时,锁标志对象默认为this,在修饰静态方法时,锁标志对象是Class类的对象
每个类被加载进入内存时,都会为该类创建一个Class类的对象,用于封装类的信息
一个类即使创建多个对象,Class类的对象只有一个
同步方法
①若操作共享数据的代码完整声明在一个方法中,不妨将此方法声明为公共的
这时我们就可以将操作共享数据的代码提取出来,定义在一个方法中,并使用synchronized修饰方法
②使用共享数据的方法解决继承Thread类的线程安全问题
此时需用static修饰方法
pubic static synchronized void show(){
}
同步方法总结:
1.同步方法仍涉及同步监视器,只是不需要显示声明
2.非静态同步方法的同步监视器为: this
静态方法同步方法的同步监视器为: 当前类本身
栗子:
static class Windows implements Runnable {
@Override
public synchronized void run() { //采用同步方法的方式解决线程安全问题
while(true){
if (ticket>0){
System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
ticket--;
}else {
break;
}
}
}
}
接下来还有第三种解决线程安全的方式Lock(锁),在我的下篇博客中单独介绍...