今天看了个java面试题,提到了线程安全与不安全的问题,脑子中没有一点概念,刚好今天有时间,就把这个问题理清楚,分享出来,让更多和我一样的小白了解下这个知识点。每天进步一点点哦~
1、线程到底是什么呢?
进程是资源调度的最小单位,线程是cpu调度的最小单位。
所有与进程有关的资源都被记录在pcb中(进程控制块)
(1)进程
每个进程独占内存空间,保存各自运行状态,相互间不干扰且可以互相切换,为并发处理任务提供可能性。
每天玩电脑的人,大家应该都清楚,进程是什么,鼠标右击任务栏,点击任务管理器的第一个sheet页,展示了电脑目前运行的所有进程。
其实每个单独的程序就是一个进程。
liunx也一样,启动一个tomcat就是一个进程。
(2)线程与多线程
进程若要执行命令就必须依赖于线程,也就是说,一个进程必须至少有一个线程。而线程和多线程的区别,就是比如下载游戏,下载文档,如果是一个线程进行的话,就需要串行顺序,按照顺序进行下载。而并行就相当于你开了三个窗口同时进行下载,并行就是多线程。也就是一个进程运行产生的多个线程。
例:
2、怎样理解线程安全呢?
百度百科这样解释:
线程安全是多线程编程时的计算机程序代码中的一个概念。
在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
简单的用ArrayList举个反例,由于ArrayList就是线程不安全的。
代码如下:
public static void main(String[] args) throws InterruptedException {
final List<Integer> list = new ArrayList<>();
for (int a = 0;a <3 ; a++) {
// 线程A将0-1000添加到list
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 1000 ; i++) {
list.add(i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
Thread.sleep(1000*30);
// 打印所有结果
System.out.println("共有" + list.size()+"个元素。");
for (int i = 0; i < list.size(); i++) {
if(list.get(i)==null){
System.out.println("第" + (i + 1) + "个元素为:null");
}
}
}
运行,可能出现的执行结果如下:
而是用线程安全的Vector类运行,即
List<Integer> list = new Vector<>();
运行后结果如下:
由此可以说明,使用ArrayList插入数字时,可能出现下标越界或者插入数据为null,由此可以说明,arraylist是线程不安全的。
再举个简单的例子:
public static void main(String[] args){
for (int i = 0; i <3 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(getcountNum(30));
}
}).start();
}
}
public static int getcountNum(int j){
int i =1;
j=j+3;
return j;
}
这个线程安全吗?
答案是肯定的,因为这段代码是没有任何状态的,也就是说,这段代码不包含任何作用域,也没有取引用其他类中的域,他所执行的结果只存在在它当前运行的这条贤臣的局部变量中,并只由当前线程去访问,不会影响到其他线程。
多条线程同时访问时,无共享数据,所以线程是安全的。
那如果添加一个共享变量呢,结果如何?
public static void main(String[] args){
for (int i = 0; i <3 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(getcountNum(30));
System.out.println("count为:"+count);
try{
Thread.sleep(1);
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}
public static int getcountNum(int j){
int i =1;
count++;
j=j+3;
return j;
}
结果:
由此可见不同的线程,访问到的count数据和预想的也不一致。
这就导致了线程的不安全。
后续会继续解释,如何实现线程安全。