九、Java多线程机制

1. 线程的基本概念
线程是一个程序内部的顺序控制流。
线程的状态 (创建 / 就绪 / 运行 / 阻塞 / 中止)
线程和进程的区别
[quote]每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。
线程可以看成时轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
多进程: 在操作系统中能同时运行多个任务(程序)
多线程: 在同一应用程序中有多个顺序流同时执行[/quote]
Java的线程是通过java.lang.Thread类来实现的。
VM 启动时会有一个由主方法(public static void main() {})所定义的线程。
可以通过创建 Thread 的实例来创建新的线程。
每个线程都是通过某个特定Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体。
通过调用Thead类的start()方法来启动一个线程。
2. 线程的创建和启动
[quote]可以有两种方式创建新的线程。
第一种
定义线程类实现Runnable接口
Thread myThread = new Thead(target)//target为Runnable接口类型。
Runnable中只有一个方法:
public void run(); 用以定义线程运行体。
使用Runnable接口可以为多个线程提供共享的数据。
在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:
public static Thread currentThread() 获取当前线程的引用。
第二种
可以定义一个Thread的子类并重写其run方法如:
class MyThread extends Thead {
public void run(){…}
}
然后生成该类的对象:
MyThread myThread=new MyThead(…)
[/quote]
3. 线程控制基本方法
[table]
|isAlive() |判断线程是否还“活”着,即线程是否还未终止。
|
|getPriority() |获得线程的优先级数值
|
|setPriority() |设置线程的优先级数值
|
|Thread.sleep() |将当前线程睡眠指定毫秒数
|
|join() |调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行。
|
|yield() |让出CPU,当前线程进入就绪队列等待调度。
|
|wait() |当前线程进入对象的wait pool。
|
|notify()/
notifyAll() |唤醒对象的wait pool中的一个/所有等待线程。|
[/table]
4. sleep / join / yield 方法
[quote]sleep方法
可以调用Thread的静态方法:
public static void sleep(long millis) throws InterruptedException
使得当前线程休眠(暂时停止执行millis毫秒)。
由于是静态方法,sleep可以由类名直接调用:
Thread.sleep(…)
join方法
合并某个线程
yield方法
让出CPU,给其他线程执行的机会
[/quote]
5. 线程模式
两种线程模式:
协作式:一个线程保留对处理器的控制直到它自己决定放弃
速度快、代价低
用户编程非常麻烦
抢先式。系统可以任意的从线程中夺回对CPU的控制权,再把控制权分给其它的线程 。
两次切换之间的时间间隔就叫做时间片
效率不如协作式高 ,OS核心必须负责管理线程
简化编程,而且使程序更加可靠
多数线程的调度是抢先式的。
6. 线程的优先级别
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。
线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
使用下述线方法获得或设置线程对象的优先级。
int getPriority();
void setPriority(int newPriority);
不同平台上的优先级
Solaris:相同优先级的线程不能相互抢占对方的cpu时间。
windows:可以抢占相同甚至更高优先级的线程的cpu时间
7. 线程同步
 在Java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
 关键字synchronized 来与对象的互斥锁联系。当某个对象synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
 通常情况下(不包括调用了wait方法),只有synchronized修饰的代码全执行完,才会释放这把锁。
 synchronized可以修饰
 修饰非静态的成员方法,或者方法里面的一部分代码
 修饰参数
 修饰成员变量
 修饰static方法,那么锁加到了Test.class上。
 修饰类
 如果调用没有被synchronized修饰的普通方法,由于不需要得到一把锁。所以肯定不需要排队
 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
 定义private 的成员变量,所有访问到关键成员变量的方法都要认真考虑是不是同步。
public class Test implements Runnable {
Timer timer = new Timer();
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.setName("t1"); t2.setName("t2");
t1.start(); t2.start();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
}
class Timer{
private static int num = 0;
public void add(String name){
num ++;
try {Thread.sleep(1);}
catch (InterruptedException e) {}
System.out.println(name+", 你是第"+num+"个使用timer的线程");
}
}

8. wait sleep 区别
[quote] 来源不同
 Sleep是Thread提供的方法
 Wait继承自Object
 代码位置不同
 Wait需要写在Synchronize语句块里面
 是否释放锁定对象
 调用wait方法,释放锁定该对象
 Sleep时别的线程也不可以访问锁定对象[/quote]
9. Synchronized总结
[quote]无论synchronized关键字加在方法上还是对象上,它取得的锁都是锁在了对象上,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
每个对象只有一个锁(lock)与之相关联。
实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。
还有一些技巧可以让我们对共享资源的同步访问更加安全:
定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。
如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。[/quote]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值