多线程的安全问题(代码同步问题)
通过分析,发现,打印出0,-1,-2的错票。
多线程出现安全问题!!!!
- tick只有一份,多个线程共用这个tick;
- 当多条语句在操作一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完另一个线程已经开始执行共享代码块!!
- 解决办法:将共享代码块加上一个锁,也叫同步代码块;——— cynchronized
- 格式:synchronized(对象){ 代码块 };对象:只要是对象就可以,可以用上帝(Object obj)
- 锁原理:就是在同步代码块前设置一个标志位,代码块被线程执行时,标志位置0,代码块未被执行时,标志位置1.
- 持有锁的线程可以在代码块中执行,没有锁的线程及时获取到了CPU资源也不能在同步中中执行!
- 锁的弊端:未执行代码块的线程要不断重复的判断标志位,这会消耗资源,拖慢进程速度。
-
- 两个或两个以上的线程。
- 必须时多个线程同时使用一个锁。
- 必须保证同步代码只有一个线程在运行。
-
- 方法:明确哪些代码块是多线程运行。
- 明确共享数据。
- 明确多线程运行代码中哪些语句是操作共享数据的。
- 函数也可以封装代码,格式:public synchronized void add( **** ){ } ;
作为修饰符,放在前面。
注意:在任何时候,你都可以用Thread.currentThread().getName()来获取当前线程的名字
/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
private int sum;
public void add(int n){
sum = sum + n;
try{Thread.sleep(10);
}catch(Exception e){}
System.out.println("sum = "+sum);
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x=0;
x<3;
x++){
b.add(100);
}
}
}
class BankDemo{
public static void main(String[] args){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
-
结果:
文章图片
image.png - 解决问题:
/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
private int sum;
Object obj = new Object();
public void add(int n){
synchronized(obj){
sum = sum + n;
try{Thread.sleep(10);
}catch(Exception e){}
System.out.println("sum = "+sum);
}
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x=0;
x<3;
x++){
b.add(100);
}
}
}
class BankDemo{
public static void main(String[] args){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
- 【多线程的安全问题(代码同步问题)】结果:
-
-
文章图片
image.png
-
-
- 同步函数用的是哪一个锁?
- 函数需要被对象调用,那么函数就都有一个所属对引用。就是this。
- 所以同步函数使用的锁事this。
/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次
*/
class Bank{
private int sum;
//Object obj = new Object();
public synchronized void add(int n){//同步函数!!
//synchronized(obj){
sum = sum + n;
try{Thread.sleep(10);
}catch(Exception e){}
System.out.println("sum = "+sum);
//}
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x=0;
x<3;
x++){
b.add(100);
}
}
}
class BankDemo{
public static void main(String[] args){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
一个小练习:
* /*
* 需求:现有两个线程:
* 一个线程在同步代码块中运行;
* 一个线程在同步函数中运行;
* 两个线程都在执行买票程序。
* */
* class Ticket implements Runnable{
*private int tick = 1000;
*Object obj = new Object();
*boolean flag = true ;
*public void run(){
*if(flag){
*while(true){
*synchronized(obj){//synchronized( this ),解决0号票问题。
*if(tick >0){
*try{Thread.sleep(10);
}catch(Exception e){}
*System.out.println(Thread.currentThread().getName()+"code.....sale "+ tick--);
*}
*}
*}
*}
*else{
*while(true){
*this.show();
*}
*}
*}
*public synchronized void show(){
*if(tick >0){
*try{Thread.sleep(10);
}catch(Exception e){}
*System.out.println(Thread.currentThread().getName()+"show.....sale "+ tick--);
*}
*}
* }
* class TickDemo{
*public static void main(String[] args){
*Ticket t = new Ticket();
*Thread t1 = new Thread(t);
*Thread t2 = new Thread(t);
*t1.start();
*try{Thread.sleep(10);
}catch(Exception e){}//解决办法
*t.flag = false;
//问题的渊源!原因:主线程执行太快,这三条语句瞬间就执行完成;flag的作用完全没有体现出来,flag=true时同步代码块还没来的及执行,flag就等于false了,紧接着执行show( )函数;之后就一直为false了。
*t2.start();
*}
* }
- 代码看似没问题,其实问题可大!所有线程都是由show( )同步函数执行完成的。如下:
-
文章图片
image.png - 解决办法在代码中!!!
- 之后有出现0号票!!!可怕!!
-
-
文章图片
image.png - 解决:把同步代码块中obj对象换成this就OK了!
-
- 如果同步函数被静态修饰时,使用的锁是什么呢?
-
- 通过验证,发现不再是this,因为静态方法中也不可以定义this。
-
- 原因:静态进内存时没有对象,只有类,也就是该类对应的字节码文件对象。
- 类名.class 该对象的类型是class。
- 静态的同步方法,使用的锁事该方法所在类的字节码文件对象。类名.class。
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量