您的位置:首页 > 其它

多线程资源共享

2015-09-25 14:35 337 查看


一、线程的创建和启动

定义线程类实现Runnable接口:

Thread myThread = new Thread(target) //target为Runnable接口类型

而 Runnable中只有一个方法:

public void run();用以定义线程运行体

Thread 类是一个具体的类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从 Thread 类导出的新类。程序员必须覆盖 Thread 的 run() 函数来完成有用的工作。用户并不直接调用此函数;而是必须调用 Thread 的 start() 函数,该函数再调用 run()。
class threadTest implements Runnable {

public threadTest(String numberID) {
this.numberID = numberID;
}

public void run() {
for (int i = 0; i < 4; i++) {
System.out.println(numberID + " is running " + i);
}
}

public static void main(String[] args) {
threadTest h1=new threadTest("线程A");
Thread demo= new Thread(h1);
threadTest h2=new threadTest("线程B");
Thread demo1=new Thread(h2);
demo.start();
demo1.start();
}

private String numberID;
}


二、线程状态装换

线程在它的生命周期中会处于各种不同的状态:

新建、等待、就绪、运行、阻塞、死亡。

当调用线程start()方法时,线程进入就绪状态,Cpu分配时间片,线程进入运行状态,时间片结束,run()方法未执行完,线程进入阻塞状态。

线程转换详细过程可参见下图:





三、线程控制基本方法

由于java线程调度不是分时的,如果程序希望干预java虚拟机对线程的调度过程,从而明确地让一个线程给另外一个线程运行的机会,可以采用以下的方法进行干预:

1. 调整各个线程的优先级,可用的方法有

getPriority() //获得线程的优先级数值

setPriority() //设置线程的优先级指数

2. 让处于运行状态的线程调用Thread.sleep(long time)方法 放弃CPU 进入阻塞状态

Thread.sleep() //静态方法,将当前线程睡眠指定毫秒数,sleep方法可能抛出InterruptedException异常。

3. 让处于运行状态的线程调用Thread.yield()方法让出CPU,当前线程进入就绪状态等待调度,这个只会对同优先级让步或更高优先级让步

4. 让处于运行状态的线程调用另一个线程的join()方法,调用某线程的该方法,将当前线程与该线程合并,当前运行的线程将转到阻塞状态,直至另一个线程运行结束,它才会转到就绪状态,从而有机会恢复运行。

5. 使用interrupt方法中断线程。

interrupt() //中断线程


四、多线程资源共享

多线程编程的一个重要原因是实现资源的共享,一个进程中的所有线程都会共享这个进程中的资源,当超过一个线程会同时修改同一个数据,或有修改有访问的话,就会产生同步问题,这时可以用以下几种方式解决:

1.同步方法,用synchronized关键字修饰的方法

每一个java对象中有一个内置锁,如果方法使用了synchronized进行修饰的话,内置锁会保护整个方法,也就是在调用方法之前,需要、获得内置锁,否则就会处于阻塞状态,当然这个关键字也可以修饰静态方法,如果调用静态方法,就会锁住整个类实现同步。
public synchronized void balance(int money){
balance += money;
}


其实:balance += money;

的执行是分3步运行的,读取变量balance的当前值,对当前值增加money,将新的值写入变量balance,在多线程中,有可能两个线程同时读取balance的值,这样就会少计算一次money的值。

2.使用特殊域变量实现同步

可以使用volatile关键字来确保多线程之间的变量读写的可见性,如:

private volatile int balance = 100;

将可能会被多线程访问到的变量使用关键字volatile设置为特殊域变量。volatile提供了一种免锁的机制,使用这个关键字修饰的域相当于告诉虚拟机,这个域可能会被其他的线程跟新,因此每次读取这个域的时候都需要重新计算,而不是使用寄存器中的值,这个关键字不会提供任何的原子操作,也不能用来修饰final类型的变量、

3.ThreadLoacal

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
private static ThreadLocal<Integer> balance = new ThreadLocal<Integer>(){
protected Integer initialValue(){
return 100;
}
}


概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响,最适合的是按线程多实例的情况.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: