当我们买电脑时,常常会格外关注电脑的cpu有几核,几线程。因为这往往决定了电脑的运行速度
,而这一切可以在任务管理器中找到。
这时,尽管已经接触了多线程操作,也许你会对线程到底是什么产生疑问。
根据百度百科:线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
那么我们什么时候需要用到多线程呢?
这样做可以使每个线程最高效利用起来。
那么线程并发安全是什么呢?
处理并发式任务时,由于多个线程操作同一资源,操作时间并未错开,而使得内存中的操作重复,从而数据错乱,造成安全问题。
例如:我们给每个线程都安排了任务,即对num加1000次。
线程栈操作步骤:从堆内存得到num——>>复制给线程栈的num副本——>>进行num++操作
——>>将num返回,赋值给堆内存中的num。
这样看来是不是井然有序,但是我们值得注意到并不一定同时只有一个线程从堆内存中得到num。
如果出现几个线程得到时,num++操作便会重复进行,使得最后少于3000次++,而数据错误。
常见情况:
而我们该如何解决这个问题呢?
毕竟如果这份数据代表的是你的银行金额,一定不会想要它出现错误吧!!!
利用锁(synchronized):
同步代码块,锁住资源,只让当前持有监视器锁的线程访问资源
可以理解为:当一个线程复制了堆内存中的num时,堆内存暂时被“上锁”,无法访问。
只有当线程完成了num的操作时,将num赋值给堆内存时,才会“开锁”,使得其他线程继续竞争访问堆内存。
这样既能保证了操作不会重复,数据不会错误,同时由于竞争访问机制,使得该任务速度大大加快。
例子如下:
Task类:
public class Task{
static int num=0;//静态变量,处于堆内存中
public static void main(String[] args) {
Task tt = new Task();
Thread t1=new Thread(new Threadsy(tt));
t1.start();
Thread t2=new Thread(new Threadsy(tt));
t2.start();
Thread t3=new Thread(new Threadsy(tt));
t3.start();//创建并启动三个线程
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
throw new RuntimeException (e);
}//阻塞,直到线程全部完成,便于得到最终结果
System.out.println(tt.num);//检验结果
}
}
Threadsy类:
public class Threadsy implements Runnable {
Task tt = new Task();
public Threadsy(Task tt) {
this.tt = tt;
}//构析方法,便于取堆内存中的num。
static Object object = new Object();//值得注意的是,Object要保证是静态变量,存在堆内存中,才可以通过它“锁住”堆内存的num
public void run() {
for (int i = 0; i < 1000; i++) {
synchronized (object/*只能“锁”对象*/) {
tt.num++;
}//给堆内存上锁,保证线程安全
}
}//线程操作方式
}
结果都为:
一切正常!!!
而如果我们去掉了资源锁:
即数据错误:
并发式任务时,使用多线程能大大提高效率,但是要十分注意线程安全问题!