线程:
分时系统的概念就是计算机系统按时间轮流执行每个进程。在这个过程中保持当前进程的状态并且切换到下一个进程。
线程拓展了这个概念,把不同进程之间的切换改换为单个进程内部的不同模块之间的切换。
操作系统在不同进程之间切换的开销一般会比jvm在不同线程之间的开销大,所以,一般来说线程可以使程序更快,此外,多线程还可以使得交互程序不至于被锁定在等待用户动作上。
获得新线程:
在java中,有两种方法可以创建线程,扩展Thread类或者实现java.lang.Runnable接口,然后在Thread构造函数中使用。实际上Thread类本身已经实现了Runnable接口了。
对于第一种方法,声明了一个继承了Thread类的对象就可以得到一个新的线程,不过创建并不等于开始执行,线程的执行通过调用Thread.start()方法触发(实际上,继承Thread的类自己没有这个方法,是调用Thread类的这个方法),而线程执行的内容是从run()开始的。基本上使用时常常把声明和启动线程一起写了:new ThreadExtendObject().start();
对于第二种方法,Runnable接口的定义如下:public interfae Runnable{
public void run();
}
所以实现的类必须实现run方法。而运行的时候则通过把这个实现了Runnable接口的类的对象传递给Thread构造函数来实现。
例如:class Testtt implements Runnable{
public void run() {}
}
Thread test1 = new Thread(new Testtt());
然后在调用这个test1的Thread方法,来处理这个线程。通常上面最后一句也习惯同时开始执行,因此这样写:new Thread(new Testtt()).start();
此外还有需要注意的一点是,Thread类的方法只有在Thread类对象存在的时候才能调用,为了得到Thread对象,必须调用Thread.currentThread()方法,它返回当前正在运行的线程,
线程的优先级:
java中可以改变线程的优先级,使得线程可以调整执行顺序,java线程可以采用时间片机制,也可以不采用,因此,正在运行的线程可以选择是否与同优先级的线程共享处理器。
java的优先权分配是1(最低),10(最高)。线程一开始的时候和它的父线程有相同的优先级,
如果不采用java时间片分配,由于不能确保时间片的分配,因此,一旦线程开始执行,其他所有具有相同优先权的线程可能会被锁死,因此最好让线程频繁的让出对cpu的控制,一边其他线程执行,比如让比较占用cpu时间的线程按一定的时间间隔调用yield()方法,
如果采用java标准的时间片分配则不必担心这个。
在java中,设定线程的优先级使用:threadobj.setPriority(int newPriority);
线程的编程:
要协调线程需要用到同步技术,不同线程需要不同的同步方式,大致分为四类,且难度随序号上升:
- 不相关的线程
- 相关但不需要同步的线程
- 互斥线程
- 交互式互斥线程
第二种类型的线程处理,一般只需要注意不要让两个不同的线程作用于同一个数据。另外还有一个例子和这种类型类似,就是daemon线程,也就是守护线程,它在后台运行,为其他程序或者线程提供服务,可以使用Threadobj.setDemon(true);来把一个线程设定为守护线程。
第三种和第四种是较高级的线程,于前两种比起来,也复杂得多。
互斥线程:
一个简单的互斥线程的应用:就是当各个线程都会读取同一个数据并且修改它的时候,如果不使用互斥线程,这种情况下,会产生所谓的“数据竞赛”,最后造成数据的错误。因此,我们需要做一些处理以避免数据竞赛的产生:
Java中,线程的互斥是在对象数据的基础之上的。系统中的每个对象都有自己的信号标识,仅当用到时它才能分配,它用于同步。使用synchronized关键字来标记某一个Object对象,runtime将会控制在一个给定时刻最多只有一个线程能够锁住标记的Object对象,在这个对象的所有同步方法中一次只有一个线程能够运行。
- 无论两个线程怎样访问同一个数据,都用互斥的方法处理
- 读写操作不能同时访问同一数据
synchronized可以用于一个类的所有方法或者一个方法,或者一个代码块。必须得到某个指定对象的互斥锁,然后代码才能执行,执行之后才会释放锁,如果锁被其他线程占用,那么申请使用锁的线程就会等待锁的释放。
比如,需要把整个类指定为互斥的,就把synchronized应用到static方法中。需要把代码块定义为互斥的,只需要把synchronized加到代码块的前面,不过必须指定一个用于控制的对象(因为前面提到了,java的线程互斥是在对象的基础上的),这个对象将被用来进行同步。
最后,当把单个方法定义为互斥的时候,直接在方法前面加synchronized就可以了。之所以能这样是因为这个时候互斥锁隐式使用了this对象。
最后的最后,这个东西一定要谨记的是,同步互斥的线程只能使用同一个对象,所以当有n个线程调用同一个对象的方法时,方法的同步是有用的,这样在任意时间内,同一个对象的所有需要同步的方法最多只能有一个方法被调用。
第四种交互式的互斥线程是最复杂的。也是最困难的。因此也就是所谓的高级线程处理了。
典型的情况是,当一个线程等待其他线程提供数据,但数据尚未就绪时,它将因等待数据而暂停执行,就是所谓的wait/notify线程同步问题。这种机制是一种复杂的独立于语言的协议。所以,在有需要的时候我再来学习它,不过我还是了解了一下它的原理:
这种情况下有两个队列,一个是被封锁的队列,等待获得互斥锁后才开始运行,另一个是等待队列,当当前线程发现它需要的数据尚未就绪的和时候它就调用wait()方法进入等待队列。
当一个线程进入等待队列后,封锁队列中的线程可以开始运行。之后,线程会产生数据,然后调用notify()方法,如果这些数据是等待队列中的线程所需的,它将唤醒等待队列中,否则把等待队列中的线程移动到封锁队列中。这是,调用notify()结束,一轮循环完成
没有评论:
发表评论