(多线程二)多线程安全问题

一、什么是多线程安全问题

当多个线程共享同一个全局或者静态变量,做写的操作,可能会发生数据冲突问题,也就是线程安全问题。读操作和局部变量是不会发生的。
二、线程安全的解决方法:该资源只能让当前线程操作,操作完成之后,再让其他线程执行

  1. 多线程之间同步:synchronized
  2. 使用锁:lock

三、synchronized作用于同步代码块

  1. 语法:
synchronized(obj){//obj就是同步锁,要想执行下面代码,就要先获得此锁。做个锁是任意的,可以是你想指定的obj,也可以是一个任意obj.
	//这里就是同步代码块
}

实例代码

  public void sale() {
        //同步代码块 synchronized  包裹需要线程安全的问题。
        synchronized (oj) {
            if(train1Count>0){
                System.out.println(Thread.currentThread().getName()+ ",出售第" + (100 - train1Count + 1) + "票");
                train1Count--;
            }
        }
    }

四、synchronized作用(非静态)方法

同步方法,被synchronized修饰的方法,这种形式不用显示指定锁,因为this就是锁。比如两个线程同时操作一个对象,那么就要在(该对象对应的类中)需同步的地方加锁


代码实例

public synchronized void sale() {
			if (trainCount > 0) { 
try {
					Thread.sleep(40);
				} catch (Exception e) {
				}
				System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
				trainCount--;
			}
	}

ps:当作用于静态方法的时候,锁为字节码文件,比如A.class
五、什么时候会释放锁

  • 跳出加锁区域(正常执行完或者异常)
  • 调用同步监听对象的wait方法

六、什么时候加锁

当出现线程安全问题的时候。

七、死锁

当两个线程,互相等待对方释放锁(至少两把锁)时,就会发生死锁。
代码实例:



/** 

* 一个简单的死锁类 

* 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒 

* 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒 

* td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定; 

* td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定; 

* td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。 

*/ 

public class DeadLock implements Runnable {  

    public int flag = 1;  

    //静态对象是类的所有对象共享的  

    private static Object o1 = new Object(), o2 = new Object();  

    @Override 

    public void run() {  

        System.out.println("flag=" + flag);  

        if (flag == 1) {  

            synchronized (o1) {  

                try {  

                    Thread.sleep(500);  //要有这个,才能够有效果发生

                } catch (Exception e) {  

                    e.printStackTrace();  

                }  

                synchronized (o2) {  

                    System.out.println("1");  

                }  

            }  

        }  

        if (flag == 0) {  

            synchronized (o2) {  

                try {  

                    Thread.sleep(500);  

                } catch (Exception e) {  

                    e.printStackTrace();  

                }  

                synchronized (o1) {  

                    System.out.println("0");  

                }  

            }  

        }  

    }  

   

    public static void main(String[] args) {  
        DeadLock td1 = new DeadLock();  
        DeadLock td2 = new DeadLock(); 
        td1.flag = 1;  
        td2.flag = 0;  

        //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。  

        //td2的run()可能在td1的run()之前运行  

        new Thread(td1).start();  

        new Thread(td2).start();  

   

    }  

}

八、多线程的三大特性:原子性,可见性,有序性

多线程编程要确保并发程序正确地执行,必须要保证原子性、可见性以及有序性,缺一不可,不然就可能导致结果执行不正确。

  • 原子性:和事务一样,即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
  • 可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值,可以使用volatile关键字修饰变量,就可见。(JMM java内存模型 在工作内存中修改后,里面刷入主内存,让其他线程看见)。ps:Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,粗略的理解,主内存和工作内存就可以了。
  • 有序性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值