多线程资源共享

本文详细介绍了Java中多线程的创建、状态转换、线程控制方法,重点讨论了多线程资源共享的问题,包括同步方法、volatile关键字和ThreadLocal的应用,旨在解决并发访问时的数据一致性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线程的创建和启动

定义线程类实现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()方法未执行完,线程进入阻塞状态。
线程转换详细过程可参见下图:

thread

三、线程控制基本方法

由于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采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响,最适合的是按线程多实例的情况.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值