标签:
前段时间做了一个利用多线程解决查询大数据的功能,之前是用.NET写的,因工作需要,开始了解学习Java。
于是,开始去了解Java里面多线程是怎么个写法,线程同步是怎么进行的?
之前.NET里面写多线程程序,也是一知半解,并没有用到很多线程特性,只是很简单的Thread.start();
lock(obj)
{
}
来保证多个线程数据同步,甚至多个线程判断执行完毕否,还是采用 主线程
while(计数器!=0){
Thread.Sleep(100);
}
期间,线程同步问题相当让我头痛,对象集合以引用的方式在不同方法中,不同线程中的操作,更是让我心惊胆战。
最后,成功解决了大数据分表查询功能。(日后考虑贴出来,让大伙指教下)。
根据.NET版本的的代码,现在打算转换为Java版本的代码,第一直观的映像就是Java开发动不动就extends继承。
对匿名写法支持不是很好,语法确实不优雅,比较臃肿和繁琐。(8支持 拉姆达表达式了)
回到 多线程,首先我得知道是怎么调用的,贴一段代码(代码就找了网上的):
/**
* 使用继承java.lang.Thread类的方式创建一个线程
*
* @author DreamSea 2011-12-29 20:17:06
*/
public class ThreadTest extends Thread {
/**
* 重写(Override)run()方法 JVM会自动调用该方法
*/
public void run() {
System.out.println("I‘m running!");
}
}
第二种,实现接口。
/**
* 通过实现Runnable接口创建一个线程
* @author DreamSea
*/
public class ThreadTest2 implements Runnable {
public void run() {
System.out.println("I‘m running!");
}
}
需要注意下,上面ThreadTest2 类只是简单的实现了一个接口的普通类。
如果你 这样使用 new ThreadTest2().run();
根本不会在单独线程中跑,不要被它 Thread名称所迷惑,它和线程一毛关系都没有,只是一个实现普通接口的类而已。
而 ThreadTest不一样的,它继承了 Thread类,它的基类已经实现了相关线程处理方法。
new ThreadTest().start() 会以多线程的方式执行。
那么实现接口的类,如果作为多线程方式执行呢?如下:
ThreadTest2 tt = new ThreadTest2();
Thread t = new Thread(tt);
t.start();
好,上面了解了怎么使用。
那么多线程是个什么东西呢?和单线程有什么区别吗?
比如我们看电影,它是电脑不断重绘画面,让我们看到一种实时动画的效果。
而且,我们可以一边看电影,一边打开聊天软件聊天。感觉这些软件都是同步进行的。
这里我的理解是这样的,
每个程序占一条流水线,不同程序的指令可以同时执行。
CPU每次只能一条条的执行指令,机器周期内最多能执行 N条指令。
多核计算机机器周期内可以处理的指令会增加很多。
如果不是多线程的话,只会在CPU某核心上面一条条指令的运行。
而实际上,现在多核cpu利用率并不充分,也就是说,机器周期内可以处理的CPU指令其实可以增加很多。
单线程执行100条指令,只能一条条的执行,假设每个周期能够处理 5条指令,4核心。
100/5=20个周期时间。
如果用多线程处理,除去资源竞争等情况,周期时间内可以处理 4*5=20条指令。
100/20=5个周期时间。
这就是多核CPU用多线程程序的好处了,至于单核CPU,多线程没有任何帮助。
这里找了一线程状态图,感谢(http://www.cnblogs.com/DreamSea/archive/2012/01/11/JavaThread.html)
可以看到,线程的start()并不一定代表已经执行多线程了,start()之后,状态为 runnable(就绪状态),
然后在被 JVM调用之后,线程才会被运行。
运行过程中,可以通过 sleep(),join(),yiled(),blocked方法改变线程运行状态,又可以让线程由运行状态,改变为“暂停”状态,
然后通过相关方法,改变暂停状态为 就绪状态,等待被JVM调用继续执行。
这里主要了解了 object.wait()方法,和Thread.Sleep()方法。
都是将线程状态改变为暂停状态,有什么区别呢?
首先上图明确 给出, sleep方法 是 计时器来决定是否回到就绪状态以待执行的。
wairt方法 需要给接受 nofity()通知才可以继续执行。
要深入了解这些,必须要了解线程同步的含义了,才能明白为什么要用这些方法。
线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。
线程同步的方法
(1)wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
(2)sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉
InterruptedException异常。
(3)notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的
唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
(4)notityAll ():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,
而是让它们竞争
object.wait()方法,和Thread.Sleep()
注意,这些方法都是针对线程同步的。
- private void sched(TimerTask task, long time, long period) {
- synchronized(queue) {
- ...
- queue.add(task);
- }
- }
如上,如果调用 wait()方法,实际就是说,当前这个线程不再阻止其他线程继续执行了,因为在 synchronized代码块中,必须代码块里面的方法全部执行完毕,才能执行下一个线程。
wait恰恰是说,好了,我还需要某些条件,你们继续执行,你们执行好了通知我就好了。主要是让出执行的权利,退出阻塞。(wait 是 object对象的基本方法,也就是作为对象锁的对象,对象锁稍后解释。)
Sleep()方法,会一直等待在那里,阻塞其他线程执行。
好了知道这个区别很重要,下面再回头来解释下 synchronized中的对象锁。
1、对于同步的方法或者代码块来说,必须获得对象锁才能够进入同步方法或者代码块进行操作;
2、如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的
Class对象(唯一);
3、对于代码块,对象锁即指synchronized(abc)中的abc;
那么我们看看这些 锁对象:
String lock=new String(“”);
Threada{
private String lock;
PUBLIC (String lock){lock=lock;}
run(){
synchronized(lock){
}
}
}
main(){
String lockObj=new String(“lock”);
new Threada(lockObj).start();
new Threada(lockObj).start();
new Threada(lockObj).start();
}
在java 中,lockObj是值引用,而且这里是 主线程中的对象,所以各个 线程实际上锁对象是同一个,是main方法里面的 lockObj对象。
static synchronized run(){
}
这里 synchronized 引用的是这个方法的对象本身,因为设置了 static,所以这里针对的不是 该类的对象,而是该类本身。
好了,下班了,基本概念厘清了。
标签:
原文地址:http://www.cnblogs.com/mophes/p/4448303.html