java的并发机制及并发机制下数据安全问题的解决方案

  • 说在前面:线程和进程
    在提及多线程的时候,必须引入线程这个概念,而线程往往是和进程联系在一起的。线程和进程的关系,举一个例子,一家公司是一个进程,网景和京东是不同的两家公司,他们就是两个不同的进程,本质上来说,进程实际上指的是一个应用程序。而线程可以将其看做是刘强东和奶茶妹妹,他们是两个线程,在京东这个进程里面有两个线程。
    进程(网景)和进程(京东)之间的资源是不会共享的,而同一个进程下的线程之间是会共享一部分资源的,当然并不是全部的资源都会共享。(刘强东和奶茶妹妹是有一部分的秘密可以共享的,但是他们也有各自的小秘密。)
  • 并发机制及存在价值
    如果把一个火车站看做是一个进程的话,那么多条检票窗口就是不同的线程,检票窗口多,检票效率就高。多线程的存在也同样是为了提高效率。
    一个线程一个栈,多个栈并行,工作效率就高。
  • 对于单核的CPU来说,多线程的本质其实是单线程
    一个CPU就像一个大脑,一个大脑只能同时执行一个任务,对于多核的CPU来说,确实可以做到真正的并行,但对于单核CPU来说,一个时间只能同时进行一个任务,所以,对于单核的CPU来说,多线程其实本质上是单线程。
    那么问题来了,既然单核的CPU的多线程其实本质是单线程,我们为何还称之为多线程呢?再举个栗子,小明准备写作业,可他有两个作业要写,但是他不想一个写完了再写另外一个,所以他想了一个特别的方法,一个作业写一个字,之后再写另一个作业,也只写一个字,这样轮流写,最终,我们可以看做他的两个作业是同时写完的。我们可以把小明看做是一个CPU,同时只能写一个作业(一个线程),但是,由于他在两个作业之间来回切换(抢夺时间片,获得执行权),总的来看,其实就是实现了同时完成两个作业(多线程并发)。
  • 多线程的实现方式
    java已经将多线程实现了,我们只需要继承就行了。
    第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
// 定义线程类
 public class MyThread extends Thread{
 	public void run(){
 	
 	}
 }
 // 创建线程对象
 MyThread t = new MyThread();
 // 启动线程。
 t.start();

第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。

// 定义一个可运行的类
 public class MyRunnable implements Runnable {
 	public void run(){
 	
 	}
 }
 // 创建线程对象
 Thread t = new Thread(new MyRunnable());
 // 启动线程
 t.start();

注意:第二种方式实现了接口,比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。(java支持单继承,支持多实现)因此,在使用多线程的时候,常常采用第二种方式。

  • 多并发下数据安全问题
    如果两个线程同时对一个数据进行修改,在一个线程获取数据后,还未将修改的数据返回,而另一个线程在此时获取的数据,那么,就会出现数据安全问题。
    多线程并发的数据安全问题的存在有三个条件:存在对数据的修改、多线程、共享数据。
  • 多并发环境下追求数据安全
    首先需要明确,虽然我们追求高效率,但是,数据的安全永远是需要放在第一位的,因此,在两个需要作出选择的时候,我们要首选数据安全,也正因此,我们在具备上述三个条件的时候,就不能采取多线程机制,只能采取单线程机制(线程同步机制)。
  • 多并发环境下数据安全的解决方案
    java的数据存在于三种变量里:

属性

位置

位置个数

是否满足上述三个条件

是否存在线程安全问题

实例变量

堆中

1 个

有可能


静态变量

方法区

1个

有可能


局部变量


多个

不满足


以后,存储数据常常采用数据结构的方式进行存储数据。如果使用局部变量的话:建议使用:StringBuilder。因为局部变量不存在线程安全问题,选择StringBuilder。StringBuffer效率比较低。

ArrayList是非线程安全的。
Vector是线程安全的。
HashMap HashSet是非线程安全的。
Hashtable是线程安全的。

synchronized是一种锁机制,synchronized有三种写法:

第一种:同步代码块:灵活
		synchronized(线程共享对象){
			同步代码块;
		}

	第二种:在实例方法上使用synchronized
		表示共享对象一定是this
		并且同步代码块是整个方法体。
	
	第三种:在静态方法上使用synchronized
		表示找类锁。
		类锁永远只有1把。
		就算创建了100个对象,那类锁也只有一把。
	
	对象锁:1个对象1把锁,100个对象100把锁。
	类锁:100个对象,也可能只是1把类锁。

我们以后开发中应该怎么解决线程安全问题?是一上来就选择线程同步synchronize吗?不是,synchronized会让程序的执行效率降低,用户体验不好。系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择线程同步机制。

第一种方案:尽量使用局部变量代替“实例变量和静态变量”。

第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样
实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,
对象不共享,就没有数据安全问题了。)

第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候
就只能选择synchronized了。线程同步机制。
  • 多并发的使用场景
    多并发也叫做高并发,高并发的应用场景有很多,比如:12306的秒杀、淘宝双十一的抢购。对于不同的并发量,可以有不同的解决方案…