并发——关于线程

线程的创建:

常用的有三种方法:
方法一:直接使用Tread,重写run方法

// 创建线程对象 
Thread t = new Thread(){    
public void run() { 
        // 要执行的任务    
        } 
}; 
// 启动线程
 t.start();

方法二:使用 Runnable 配合 Thread

  • Thread 代表线程
  • Runnable 可运行的任务(线程要执行的代码)
    这种方法将线程和任务分离开
Runnable runnable = new Runnable() {   
	 public void run(){        // 要执行的任务    
	 } 
 }; 
 // 创建线程对象 
 Thread t = new Thread( runnable,"t2" );
  // 启动线程
 t.start();

Java 8 以后可以使用 lambda 精简代码

方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了
用 Runnable 更容易与线程池等高级 API 配合
用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

new Thread(()->{
      System.out.println("ok");
  },"t2").start();

因为Runnable接口只有一个抽象方法:

//Runnable源码
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

方法三:FutureTask 配合 Thread

FutureTask<Integer> task=new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return 1;
    }
});
new Thread(task).start();
int res=task.get();

线程的常用API:

1.start()

启动一个新线 程,在新的线程 运行 run 方法 中的代码。
注意:
start 方法只是让线程进入就绪,里面代码不一定立刻 运行(CPU 的时间片还没分给它)。每个线程对象的 start方法只能调用一次,如果调用了多次会出现 IllegalThreadStateException

注意:
启动线程必须使用此方法,直接调用run()方法是同步的,不能启动线程

2.join()

等待线程运行结束

  1. join(long n)

等待线程运行结 束,多等待 n 毫秒

4.isInterrupted()

判断是否被打断,不会清楚打断标记

5.interrupted()

判断当前线程是 否被打断 会清除打断标记

6.currentThread()

获取当前正在执 行的线程

7.sleep(long n)

让当前执行的线 程休眠n毫秒, 休眠时让出 cpu 的时间片给其它 线程

8.yield()

提示线程调度器 让出当前线程对 CPU的使用,
调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程 。
但是不能保证有效果,因为优先级的缘故

9.interrupt()

打断线程

10.线程优先级

  • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它 如果
  • cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
 t1.setPriority(Thread.MIN_PRIORITY); 
 t2.setPriority(Thread.MAX_PRIORITY);

补充:

对比:
sleep

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException ,有一个特点 (如果被打断线程正在 sleep,wait,join 会导致被打断 的线程抛出 InterruptedException,并清除打断标记,park方法是不会清楚打断标记的而且如果标记为true的话park会失效;如果打断的正在运行的线程,则会设置打断标记 )
  3. 睡眠结束后的线程未必会立刻得到执行
  4. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

yield

  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器

守护线程的使用:

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守 护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

log.debug("开始运行..."); 
Thread t1 = new Thread(() -> {   
	 log.debug("开始运行...");    
	 sleep(2);   
	 log.debug("运行结束..."); 
 }, "daemon");
 // 设置该线程为守护线程 
 t1.setDaemon(true);
 t1.start();
 
 sleep(1); 
 log.debug("运行结束...");

输出

08:26:38.123 [main] c.TestDaemon - 开始运行…
08:26:38.213 [daemon]c.TestDaemon - 开始运行…
08:26:39.215 [main] c.TestDaemon - 运行结束…

注意

  • 垃圾回收器线程就是一种守护线程
  • Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等 待它们处理完当前请求

有一些过时方法已经不再使用:

//以下代码都容易破坏同步代码块,造成锁得不到释放
//阻塞唤醒方法我们使用更好的wait和notify来解决
stop();
suspend();
resume();

线程的状态:

Java 线程6种状态
1. 新建(NEW):创建后尚未启动。
2.运行(RUNNABLE):该状态包含了操作系统线程状态中的 Running 和 Ready;即可能正在运行,也可能正在等待 CPU 时间片。
3.无限期等待(WAITING):等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。
4.限期等待(TIMED_WAITING):无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。
5.阻塞(BLOCKED):等待获取一个排它锁,如果其线程释放了锁就会结束此状态。
6.结束(TERMINATED):线程结束任务之后自己结束,或者产生了异常而结束。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值