Java多线程系列(一)—多线程基础

Java多线程系列(一)—多线程基础

线程是CPU调度的最小单元,单核时代多线程可以在IO密集型操作时有更高的效率,在如今这个多核为主的时代,多线程更能有效的发挥多核的优势,充分利用CPU资源;

个人主页:tuzhenyu’s page
原文地址:Java多线程系列(一)—多线程基础

1. 线程基础

(1) 线程和进程

  • 进程是系统资源分配的基本单位,线程是CPU资源调度的最小单元,一个进程下可以有多个线程;

  • 每个进程都有各自独立的地址空间,所有进程共享父进程的地址空间;

  • 进程上下文切换的开销远远大于线程的上下文切换

(2) 线程的创建

  • 调用Java程序入口的public void static main(){}的是一个由JVM默认创建的名称叫做main的线程

  • 继承Tread类创建线程,线程的启动需要通过Thread.start()调用,会通过本独方法的调用让操作系分配CPU资源,JVM中的Thread对象只是线程的外壳;


Public class MyThread extends Tread{

    @override

    pulic void run(){

        super.run();    //继承Thread类的run()方法

    }

}

Public class Run {

    public static void main(){

        MyThread myThread = new  MyThread();

        myThread.start();

    }

}
  • 实现Runnabale接口创建线程,实现Runnable的实例化对象作为参数传入Thread中设置到一个名为”target”的属性上,Thread默认的Run是调用这个target的run()方法来完成;

Public class MyRunnable implements Runnable{

    pulic void run(){

        System.out.println("hello world")

    }

}



Public class Run {

    public static void main(){

        MyRunnable myRunnable = new MyRunnable();

        MyThread myThread = new  MyThread(myRunnable);

        myThread.start();

    }

}
  • 通过Thead创建线程和通过Runable创建线程区别

    • 通过Thead创建线程需要继承Thread类重写run()方法,线程运行时直接执行继承类的run方法;

    • 通过Runnable创建线程需要实现Runnable接口,并将实现类当做参数注入到Thread类的target中,线程运行时会运行Thread类的run()方法查看对应target是否为空,如果不为空则调用target的run()方法;




@Override
public void run() {
    if (target != null) {
        target.run();
    }
}
- Thread实现只能继承一个类,Runnable接口可以实现多个接口,在需要多继承的情境下使用Runnable接口实现

(3) 线程的状态

  • NEW(新建状态):new了一个新的线程对象,还未调用start()

  • Runnable(可运行状态):包含Ready就绪状态和Running运行状态

    • 就绪状态:其他线程调用了该对象的start()方法,等待获取CPU的使用权

    • 运行状态:获取了CPU的使用权,执行run()程序

  • Block(阻塞状态):通常是因为等待锁造成线程挂起,JVM会把该线程置为阻塞状态

  • Waiting(等待状态):执行Object.wait()方法后所处的等待状态,需要其他线程通知或者中断

  • Time_Waiting(超时等待状态):执行Thread.sleep(n)线程睡眠挂起,进入超时等待状态;

  • Terminated(终止态):执行完run()方法后线程的状态;

(4) 线程的优先级

  • Java中优先级分为1~10等级,默认main的等级为5

  • 线程优先级具有继承性,若B的优先级未明确设置,A线程启动B线程,则B线程的优先级与A是一样的

2. 线程常用方法

(1) Thread类的常用方法

静态方法
  • Thread.currentThread()方法:获取当前线程

Thread.currentThread().getName()    // 获取线程名

Thread.currentThread().getId()    // 获取线程唯一标识
  • sleep():让该正在执行的线程休眠指定毫秒数

this.currentThread().sleap(2000)    //休眠2秒
  • yield()线程让步

Thread.yield();    //当前线程进行让步,放弃CPU资源后重新竞争,如果竞争失败则从运行态度转换为阻塞态;
  • intercepted(),isIntercepted()线程停止判断
实例方法
  • start():启动线程,进入就绪状态等待获取CPU资源

  • run():mian线程调用Thread.run()方法,代码还是同步顺序执行

  • isAlive()方法:判断当前的线程是否处于活动状态(就绪状态,运行状态)


myThread.isAlive()    //判断线程是否处于存活状态
  • intercept()线程停止

myThread.intercept();    //将myThread线程标记为停止态
  • join()线程等待

myThread.join();    //当前线程等待myThread线程执行完后再执行

(2) 线程的停止

  • 线程停止的方法:

    • 当run()方法完成后线程终止

    • 使用Thread.stop()方法强行终止当前线程

    • 使用Thread.interrupt()方法中断当前线程

      • interrupt()+interruptted+break

      • interrupt()+interruptted+return

      • interrupt()+interruptted+throw Exception

    • sleep()和wait()方法+interrupt()抛出InterruptedException异常

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("run:"+ System.currentTimeMillis());
        super.run();
        for (int i=0;i<200;i++){
            if(this.interruptted){
                break;
            }
            System.out.println("i:"+i);
        }

    }
}
public class ThreadStopTest {
    public static void main(String[] args) throws InterruptedException{
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("start:"+System.currentTimeMillis());
        Thread.sleep(2000);    //main主线程休眠,让出CPU使用权给myThread线程,不然会执行完main再让出使用权
        System.out.println("sleep:"+System.currentTimeMillis());
        myThread.interrupt();
        System.out.println("interrupt:"+System.currentTimeMillis());

    }
}
  • 判断线程停止状态:

    • this.interrupted():测试当前线程是否已经是中断状态,执行后清除状态标志

    • this.isInterrupted():测试当先线程对象是否已经中断,但不清除状态标志

  • suspend()和resume():可能会出现独占公共同步对象,也会出现因为暂停造成的不同步现象

(3) 线程的让步

  • 线程的让步可以通过yield(),sleep(0),sleep(1)实现

  • sleep(0),sleep(1)和yelid()实现线程让步区别

    • sleep(0)线程进入就绪状态,但是只允许优先级更高的线程使用CPU,如果没有合适的线程该线程会重新获取到时间片

    • sleep(1)线程睡眠1ms后进入就绪状态,各个线程公平抢占CPU

    • yelid()线程让出CPU进入就绪状态,并且和其他线程公平竞争CPU

  • 线程让步Thread.yield()是通过本地方法(native)实现的

(4) 线程的等待

  • Thread.join()的使用:一个线程A执行了thread.join()表式线程A要等待线程thread运行结束后才能从thread.join()中返回继续执行后面的代码。
//全部开启线程后执行线程等待

public static void main(String[] args) throws Exception{
   Thread[] threads = new Thread[5];
   for (int i=0;i<5;i++){
      threads[i] = new Thread(new Runnable() {
         @Override
         public void run() {
            System.out.println(Thread.currentThread().getId()+" is run");
         }
      });
   }
   for (int i=0;i<5;i++){
      threads[i].start();
   }
   for (int i=0;i<5;i++){
      threads[i].join();
   }
   System.out.println("finish");
}
线程等待join()方法实现原理
  • join()方法加有对象锁,锁的对象就是被等待的线程;在方法内循环判断被等待线程是否存活,如果存活则调用wait()方法进入阻塞状态等待被通知;

  • 当被等待线程结束时会做清理工作,其中包括一项就是通知唤醒等待该线程对象锁的线程;因此join()方法是通过对象锁实现的;

public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

(4) 线程的wait/notify通信机制


class Thread1 extends Thread{
    private Object object;

    public Thread1(Object object){
        this.object = object;
    }

    @Override
    public void run() {
        try{
            synchronized (object){
                System.out.println("begin to wait:"+Thread.currentThread().getName());
                object.wait();
                System.out.println("wait end:"+Thread.currentThread().getName());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
class Thread2 extends Thread{
    private Object object;

    public Thread2(Object object){
        this.object = object;
    }

    @Override
    public void run() {
        synchronized (object){
            System.out.println("begin to notify:"+Thread.currentThread().getName());
            object.notify();
            System.out.println("notify end:"+Thread.currentThread().getName());
        }
    }
}
public class WaitNotifyTest {
    public static void main(String[] args) throws InterruptedException{
        Object o = new Object();
        Thread1 thread1 = new Thread1(o);
        thread1.start();
        Thread.sleep(2000);
        Thread2 thread2 = new Thread2(o);
        thread2.start();
    }
}
  • wait()和notify()必须在同步块中调用,wait()和notify()必须持有同一对象锁,这样才能wait()释放对象锁后notify()持有该对象锁;wait()应该先持有对象锁,执行wait()方法后释放锁,其他线程竞争该对象锁;

  • 调用wait()的线程将进入Waiting状态,只有等待其他线程通知或者被中断才返回;wait()方法底层调用wait(0)表示一直等待直到被其他线程唤醒;

  • wait(long)方法:等待某一时间内是否有线程进行唤醒,超出这个时间会自动唤醒,如果未竞争得到CPU资源则进入阻塞Blocked状态;

  • notify()方法用来通知wait状态的线程,如果有多个持有相同对象锁的线程,随机挑选一个发出通知notify

  • 执行完notify()方法之后,当前线程不会马上释放该对象锁,要执行完synchronized代码块内的程序后才能释放

  • notifyAll()方法:唤醒所有处于当前对象锁的wait状态的线程

总结

线程的方法一般是通过调用本地方法将具体操作交由操作系统执行,线程让步join()方法的实现是通过对象锁;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值