线程的基本方法
- 2020 年 3 月 10 日
- 筆記
1. 进程与线程
进程:资源分配的基本单位
线程:资源调度的基本单位
1.1 有了进程为什么还需要线程呢?
为了进程能进行并发操作
1.2 线程的生命周期
2. 创建进程
创建进程有两种方法(一般推荐第二种,因为单继承问题)
先来看看线程的构造函数
2.1 继承Thread类,重写run()
public class Threadtest extends Thread { //设置名字 public Threadtest(String name) { super(name); } //重写方法 public void run(){ for(int i = 0;i < 100;i++){ System.out.println(Thread.currentThread().getName() + "-----" + i); } } public static void main(String[] args) { Threadtest t1 = new Threadtest("线程1"); Threadtest t2 = new Threadtest("线程2"); t1.start(); t2.start(); } }
2.2 实现Runnable接口,重写run()
public class Runnabletest implements Runnable { @Override public void run() { for(int i = 0;i < 100;i++){ System.out.println(Thread.currentThread().getName() + ":::" + i); } } public static void main(String[] args) { Thread t1 = new Thread(new Runnabletest(),"线程1"); Thread t2 = new Thread(new Runnabletest(),"线程2"); t1.start(); t2.start(); } }
// 上面两种方法都是线程交替进行 线程1:::0 线程1:::1 线程1:::2 线程1:::3 线程1:::4 线程2:::0 线程1:::5 线程1:::6 线程1:::7 线程1:::8 线程1:::9 线程1:::10 线程2:::1
3.线程的方法
3.1 命名 getName()
- 该构造函数里的init方法的第三个参数是线程名
- 第三个参数是个函数,该函数同步地维护了threadInitNumber,是一个数字
- 可想而知,线程名字是
Thread-Number
,eg:Thread-0
- 从2.1/2.2可知构造方法里面提供了命名线程的方式
/** * Allocates a new {@code Thread} object. This constructor has the same * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} * {@code (null, null, gname)}, where {@code gname} is a newly generated * name. Automatically generated names are of the form * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer. */ public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } /* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
- 也可以通过 setName(String name) 来命名,不过该方法调用native方法,笔者水平有限不做深究
Thread thread = new Thread(() -> { System.out.println(Thread.currentThread().getName()); }); thread.setName("新线程名");
新线程名
3.1 守护线程(setDaemon)
- 守护进程是为其他线程服务的线程,存在于后台,一旦有线程就存在,线程全部消失而结束,eg: 垃圾回收线程
- 守护线程中产生的新线程也是守护线程
- 需要在进程启动前调用Thread.setDaemon(true),会用native方法检测,非法则抛出异常
public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; //设置守护线程为真 } /* Whether or not the thread is a daemon thread. */ // private boolean daemon = false;
3.2 优先级(setPriority)
- 设置获取CPU时间片的几率,分0—10等级,默认为5
// MAX_PRIORITY 最大级别10 // MIN_PRIORITY 最小0 // NORM_PRIORITY 普通5 public final void setPriority(int newPriority) { ThreadGroup g; //线程组 checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); //设置级别,后面遇到native方法不说明了 } }
public static void main(String[] args) { Threadtest t1 = new Threadtest("线程1"); Threadtest t2 = new Threadtest("线程2"); //设置优先级 t1.setPriority(10); t1.start(); t2.start(); }
线程1:::97 线程1:::98 线程1:::99 线程2:::3 线程2:::4 线程2:::5
在前面实例中调用该函数,发现t1线程cpu执行时间片多于t2线程,t1完成了t2还在开头
3.3 sleep
- 让该线程休眠,不释放锁
- 结束重回就绪状态
- t1.sleep(),不是t1睡眠,而是当前线程睡眠
- 哪个线程调用sleep方法,哪个线程就睡眠
public class Threadtest extends Thread { //设置名字 public Threadtest(String name) { super(name); } //重写方法 public void run(){ for(int i=0; i < 100;i++){ System.out.println(Thread.currentThread().getName() + "--" + i); } } public static void main(String[] args) throws InterruptedException { Threadtest t2 = new Threadtest("线程2"); t2.start(); t2.sleep(10000); System.out.println("========="); } }
线程2--96 线程2--97 线程2--98 线程2--99 //10秒后,main线程睡眠 =======
3.4 join
- 使当前线程停下来等待,直到调用join()的线程结束,才恢复执行
- 它可以使得线程之间的并行执行变为串行执行
- 在start之后才执行的
- 底层还是调用wait方法
public class Threadtest extends Thread { //设置名字 public Threadtest(String name) { super(name); } //重写方法 public void run(){ for(int i=0; i < 100;i++){ System.out.println(Thread.currentThread().getName() + "--" + i); } } public static void main(String[] args) throws InterruptedException { Threadtest t1 = new Threadtest("线程1"); Threadtest t2 = new Threadtest("线程2"); t2.setPriority(10); t1.start(); t1.join(); //main线程停下来,等t1执行完才继续往下执行,所以先输入t1,再输出t2 t2.start(); } }
3.5 wait和notify
- wait使当前线程挂起,notify随机唤醒一个同享对象锁的线程,notifyAll唤醒所有
- wait必须在同步代码块或同步方法中调用,先要有锁才能释放锁
- wait方法释放锁,并处于阻塞状态
- notify不是立即释放锁,要执行完同步操作才释放锁
- 被其他线程唤醒后处于就绪状态
thread.wait(); thread.notify();
3.6 yield
使当前线程从运行状态转为就绪状态,即可能让别的线程执行,也可能自己再次执行
3.7 interrupt
- 该方法不是即时中断线程,而是仅仅设置一个中断信号量,然后中断操作由我们自己实现
Thread t1 = new Thread(new Runnable(()->{ // 若未发生中断,就正常执行任务 while(!Thread.currentThread.isInterrupted()){ // 正常任务代码…… } // 中断的处理代码…… doSomething(); }).start();
- 当线程在活动之前或活动期间处于阻塞状态(正在等待、休眠或占用状态)且该线程被中断时,抛出InterruptedException
- 阻塞线程调用interrupt()方法,会抛出异常,设置标志位为false,同时该线程会退出阻塞
3.8 exit
退出当前线程(或者当run方法结束也是线程结束)
3.9 start和run区别
- run():仅仅是封装被线程执行的代码,直接调用是普通方法
- start():创建线程,jvm调用线程的run()方法,所以start方法执行完,不代表run方法执行完,线程也不一定销毁!
3.10 currentThread()
获取当先运行的线程,Thread thread = Thread.currentThread(),属于静态方法