Java 多线程

一、Java多线程

1. 进程和线程

进程:进程是正在运行的程序的实例,是系统资源分配的最小单位。
线程:线程可以理解为进程中独立运行的子任务,是CPU调度的最小单位。

2. 使用多线程

实现多线程编程的方式主要有两种,一种是继承 Thread 类,另一种是实现 Runnable 接口。

Runnable接口:

定义:
public interface Runnable {
    public abstract void run();
}

Thread类:

构造函数:
Thread() 分配新的 Thread 对象。
Thread(String name) 分配新的 Thread 对象。
Thread(Runnable target) 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象。

APIlong getId() 返回该线程的标识符。
String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

String getName() 返回该线程的名称。
void setName(String name) 改变线程名称,使之与参数 name 相同。

int getPriority() 返回线程的优先级。
void setPriority(int newPriority) 更改线程的优先级,优先级的范围是110,默认的优先级是5,优先级最高为10boolean isDaemon() 测试该线程是否为守护线程。
void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。

static Thread currentThread() 返回对当前正在执行的线程对象的引用。
boolean isAlive() 测试线程是否处于活动状态。

void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

Thread.State getState() 返回该线程的状态。

void interrupt() 中断线程,仅仅是在当前线程中打了一个停止标记,并不是真的停止线程。
boolean isInterrupted() 测试线程是否已经中断,但不清除状态标志。
static boolean interrupted() 测试当前线程是否已经中断,执行后具有将状态标志清除为false的功能。

static void yield() 暂停当前正在执行的线程对象,并执行其他线程。

static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),
此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),
此操作受到系统计时器和调度程序精度和准确性的影响。
注:sleep不释放锁。

void join() 等待该线程终止。
void join(long millis) 等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos) 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
注:join释放锁。

void notify() 唤醒在此对象监视器上等待的单个线程。
void notifyAll() 唤醒在此对象监视器上等待的所有线程。

void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void wait(long timeout) 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
void wait(long timeout, int nanos) 
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
注:wait释放锁。

线程的状态:
初始-NEW、可运行-RUNNABLE(运行中-RUNNING 和 就绪-READY)、阻塞-BLOCKED、等待-WAITING 、超时等待-TIMED_WAITING、终止-TERMINATED

线程状态间的转换:
在这里插入图片描述

3. 线程对其他线程可以有共享数据与不共享数据之分

public static void main(String[] args) {
    // 不共享数据
    Thread a = new Thread("A");
    Thread b = new Thread("B");
    // 共享数据,c和d线程可以共享x对象的成员变量
    Thread x = new Thread();
    Thread c = new Thread(x,"c");
    Thread d = new Thread(x,"d");
}

4. 线程的优先级

线程优先级具有继承性:在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。

线程优先级具有规则性:高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完。

线程优先级具有随机性:优先级较高的线程不一定每一次都先执行完。

5. 守护线程

在Java线程中有两种线程,一种是用户线程,另一种是守护线程。

守护线程是一种特殊的线程,当进程中不存在非守护线程时,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程时,则垃圾回收线程也就没有存在的必要,自动销毁。

二、synchronized

1、synchronized获取到的锁都是对象锁,而不是把一段代码或方法当作锁。

2、synchronized是可重入锁,拥有锁重入的功能,在一个synchronized方法/块的内部调用其所属对象的其他synchronized方法/块时可以再次得到该对象的锁。
可重入锁:自己可以再次获取自己的内部锁。可重入锁支持在父子类继承的环境中,当存在父子类继承关系时,子类同步方法可以通过“可重入锁”调用父类的同步方法。

3、当一个线程执行的代码出现异常时,synchronized所持有的锁会自动释放。

4、同步不可以继承,父类方法添加了synchronized关键字,子类的重写方法如果没有添加synchronized关键字则不能实现同步。

5、静态synchronized方法与synchronized(类.class)代码块取得的是Class锁,Class锁对类的所有对象实例起作用;而synchronized关键字加到非静态方法上取得的是对象锁。

三、ThreadLocal

1. 简介

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,是当前线程独有的变量。

ThreadLocal变量通常被private static修饰。当一个线程结束时,线程中所有在ThreadLocal填充的变量都可被回收。

示例:

public class ThreadLocaDemo {
 
    private static ThreadLocal<String> localVar = new ThreadLocal<String>();
 
    static void print(String str) {
        //打印当前线程中本地内存中本地变量的值
        System.out.println(str + " :" + localVar.get());
        //清除本地内存中的本地变量
        localVar.remove();
    }
    public static void main(String[] args) throws InterruptedException {
 
        new Thread(new Runnable() {
            public void run() {
                ThreadLocaDemo.localVar.set("local_A");
                print("A");
                //打印本地变量
                System.out.println("after remove : " + localVar.get());
               
            }
        },"A").start();
 
        Thread.sleep(1000);
 
        new Thread(new Runnable() {
            public void run() {
                ThreadLocaDemo.localVar.set("local_B");
                print("B");
                System.out.println("after remove : " + localVar.get());
              
            }
        },"B").start();
    }
}
 
A :local_A
after remove : null
B :local_B
after remove : null
 

从这个示例中我们可以看到,两个线程分表获取了自己线程存放的变量,他们之间变量的获取并不会错乱。

2. ThreadLocal的原理

ThreadLocal的set、get、remove方法:

public class ThreadLocal<T> {
	// ---------set-----------
 	public void set(T value) {
        //1、获取当前线程
        Thread t = Thread.currentThread();
        //2、获取线程中的属性 threadLocalMap ,如果threadLocalMap 不为空,
        //则直接更新要保存的变量值,否则创建threadLocalMap,并赋值
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            // 初始化thradLocalMap 并赋值
            createMap(t, value);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
    	// this指向当前 ThreadLocal对象,此对象作为 key 来创建 ThreadLocalMap 对象。
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    
	// ---------get-----------
    public T get() {
        //1、获取当前线程
        Thread t = Thread.currentThread();
        //2、获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //3、如果map数据不为空,
        if (map != null) {
            //3.1、获取threalLocalMap中存储的值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //如果是数据为null,则初始化,初始化的结果,TheralLocalMap中存放key值为threadLocal,值为null
        return setInitialValue();
    }
 
	private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

	// ---------remove-----------
	public void remove() {
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            m.remove(this);
    }
    

3. ThreadLocal与synchronized的作用对比

ThreadLocal和synchonized都用于解决多线程并发访问,但synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
synchonized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而synchonized却正好相反,它用于在多个线程间通信时能够获得数据共享。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值