8、线程

在项目中使用多线程的场景并不是很多,我想是一般项目对线程要求不高,但是线程在实际开发中带来的好处是很多的:
1.一件事:用单线程可以做,但是使用多线程可以更快。
2.典型应用tomcat,tomcat内部采用的就是多线程,上百个客户端访问同一个web应用,tomcat接入后都是把后续的处理扔给一个新的线程来处理,这个新的线程最后调用到我们的servlet程序,比如doGet或者doPost方法。
迅雷多任务下载,word, 浏览器,
3.后台线程:比如定期执行一些特殊任务,如定期更新配置文件,任务调度(如quartz),一些监控用于定期信息采集等。
4.数据库连接,每个session是一个线程
例子:原来售票系统只有一个窗口,现在5个窗口同时售票,缩短了排队时间。

需要考虑的事情:
(1)、多线程与多对象的关系
(2)、多线程与单例的关系
(3)、线程锁的应用与场景

1、为什么不创建多个实例(为什么使用单例)?
(1)、java 中,单例的使用往往是源于某些 bean 被频繁的使用,而且,创建它们的成本消耗很高。 例如:hibernate 中 SessionFactory ,单例模式就是为了保证它们不被重复创建,因为这是没有必要的。
(2)、控制实例产生的数量,达到节约资源的目的。
(3)、成员变量的共享,只属于单个实例享用,其他实例不能操作该变量,实现线程之间的资源共享。
减少系统资源的消耗.因为这种工具类基本上贯穿程序始终,必然会频繁调用,如果每一次调用都要重新生成实例,带来的就是在内存堆中存在大量此类实例,.所以这种模式会提高程序的运行速度,减少资源消耗;


2、为什么要使用多线程(多线程的优点)?

一、线程安全与不安全
简单demo:

public static void main(String[] args) {
//构造方法
Runnable runnable = new Runnable() {
Count count = new Count();
public void run() {
count.count();
System.out.println(count);
}
};
for(int i = 0; i < 10; i++) {
//构造方法
new Thread(runnable).start();
}
}
}
class Count {
private int num;
public void count() {
for(int i = 1; i <= 10; i++) {
num += i;
}
System.out.println(Thread.currentThread().getName() + "-" + num);
}
}

分析上面的线程是否安全:
1、变量num是成员变量,可以供所有线程使用,当一个线程此时通过运算得到num=46,而仅仅被下个线程执行后变成了47,再执行++时就变成了48,就造成num的不确定性,故不是安全的。
变量定义在方法内也就是局部变量是线程安全的。
2、看上去每个线程里都有一个new关键字,但从打印信息上看,他们是同一个实例,这样的易读性不好。应当将count拿出去, final Count count = new Count();

二、同步
Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。上个列子中,在多个线程之间共享了Count类的一个对象,这个对象是被创建在主内存(堆内存)中,[color=red]每个线程都有自己的工作内存(线程栈),[/color]工作内存存储了主内存Count对象的一个副本,当线程操作Count对象时,首先从主内存复制Count对象到工作内存中,然后执行代码count.count(),改变了num值,[color=green]最后用工作内存Count刷新主内存Count。[/color]当一个对象在多个内存中都存在副本时,如果一个内存修改了共享变量,其它线程也应该能够看到被修改后的值,此为可见性。多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性。

Java中使用synchronized保证一段代码在多线程执行时是互斥的,有两种用法:
1、锁定对象;public void output(String name) {
synchronized (this) {

2、锁定方法:public synchronized void output(String name) {
使用synchronized修饰的方法或者代码块可以看成是一个原子操作,一个线程执行互斥代码过程如下:
1. 获得同步锁;
2. 清空工作内存;
3. 从主内存拷贝对象副本到工作内存;
4. 执行代码(计算或者输出等);
5. 刷新主内存数据;
6. 释放同步锁。

public static void main(String[] args) {
final Outputter output = new Outputter();
new Thread() {
public void run() {
output.output("zhangsan");
};
}.start();
new Thread() {
public void run() {
output.output("lisi");
};
}.start();
}
}
class Outputter {
public synchronized void output(String name) {
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


三、线程协作


四、死锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值