并发编程笔记二:线程

二、线程

现代操作系统中,线程是被操作系统调度和执行的单位。进程是拥有资源所有权的单位。Java中的线程Thread是对操作系统的线程的一种抽象,他与操作系统的线程有些不一样。

2.1、线程的特点

1)一个进程中包含一个或多个进程;

2)同一个进程中的线程可以共享进程中的资源,包括内存资源、IO资源等等;

3)线程创建、切换、销毁成本比进程小;

4)线程间通信方便、效率高,因为可以共享进程中的资源,所以可以使用共享内存进行通信。

 

2.2、java中创建线程的方法

(1)Runnable接口

       编写一个类实现Runnable接口,并实现run()方法。创建该类的对象来创建一个任务。再新建一个Thread类的对象,将这个任务对象通过Thread的构造函数传入,并执行Thread类对象的start()方法。

public class Demo001 {

    public static void main(String[] args) {

       Task01 task = new Task01();

       Thread thread = new Thread(task);

       thread.start();

    }

}

class Task01 implements Runnable{

    @Override

    public void run() {

       System.out.println(Thread.currentThread().getName() + ":任务正在执行");

    }

}

 

(2)Thead类

       因为Thread类本身已经实现类Runnable接口。所以我们可以编写一个类继承Thread类,并覆盖Thread的run()方法。再创建这个类的对象,然后执行对象的start()方法。

public class Demo002 {

    public static void main(String[] args) {

       Task002 task = new Task002();

       task.start();

    }

}

class Task002 extends Thread{

    @Override

    public void run() {

       System.out.println(Thread.currentThread().getName() + ":任务正在执行");

    }

}

 

(3)Callable接口

       编写一个类,实现Callable接口,覆写call方法,创建Callable实现类对象,再使用FutureTask包装此对象,将包装后的对象传入Thread的构造函数中,并执行Thread对象的start()方法。

       关于Callable和Future详见2.6

import java.util.concurrent.Callable;

import java.util.concurrent.FutureTask;

public class Demo003 {

public static void main(String[] args) throws Exception{

     Task003 task003 = new Task003();

     FutureTask<Integer> futureTask = new FutureTask<>(task003);

     Thread thread = new Thread(futureTask);

     thread.start();

     System.out.println("线程已经开始");

     Integer result = futureTask.get();

     System.out.println("线程结束,结果为 = " + result);

}

}

class Task003 implements Callable<Integer> {

@Override

public Integer call() throws Exception {

     Thread.sleep(5000L);

     return 100;

}

}

 

2.3、Thread类的相关重要方法

1)static void sleep(long millis) throws InterruptedException

       该方法可以让当前执行的线程睡眠指定毫秒数,这是一个阻塞操作,执行该方法会将当前线程放入阻塞队列中,到达指定时间后在从阻塞队列中出来,进入就绪队列;

       该方法抛出InterruptedException,当阻塞过程中收到中断信号,会抛出异常。

2)static Thread currentThread()

       获取当前执行本段代码的线程对象。

3)void setDeamon(boolean on)

       设置一个线程为守护线程,只有线程在未启动的时候才可以设置,如果已经启动线程在调用本方法会抛出一个运行时异常IllegalThreadStateException;

       守护线程又叫后台线程,当所有其他非守护线程全部结束后,守护线程自动结束;

       默认情况,从非守护线程上创建的线程是非守护线程,从守护线程上创建的线程是守护线程,除非手动指定。

4)static void yield()

       当前线程让出执行时间片,不一定有用。

5)void join()、void join(long millis)

       当前线程等待执行该方法的线程结束后才能继续执行。这是非静态方法,需要一个线程对象的引用。当前线程会进入阻塞状态,直到执行该方法的线程结束,才能继续执行;

       带参数的方法,会设置一个阻塞时间,时间过去后,执行该方法的线程还没结束,当前线程也会退出阻塞状态。

2.4、线程中断

2.4.1、线程中断和检测中断状态的方法的方法

Thread类中有3个方法和线程中断相关。

执行代码的线程称为当前线程,调用方法的线程称为执行线程

(1)void interrupt()

       当前线程向执行线程发送一个中断信号,且设置执行线程的中断状态为true

(2)boolean isInterrupted()

       返回执行线程的中断状态

(3)static boolean interrupted()

       静态方法,返回当前线程的中断状态,且清除当前线程的中断状态,也就是将中断状态只为false,相当于处理了这个中断信号

2.4.2、线程中断异常

JDK中有很多方法会抛出InterruptedException线程中断异常。在这些方法的执行过程中,会一直检测当前线程的中断状态,如果收到中断信号,也就是中断状态变为了true,那么这个方法就会立即返回,并且抛出一个InterruptedException线程中断异常,并且会清除当前线程的中断状态。

Thread中会抛出InterruptedException线程中断异常的方法:

(1)public static native void sleep(long millis) throws InterruptedException;

(2)public final void join() throws InterruptedException

2.5、线程通信wait/notify机制

1)等待/唤醒机制的使用场景

等待唤醒机制存在于多线程之间的通信。一个线程在执行过程中,如果发现执行条件不满足,它应该释放当前持有的锁,并进入当前锁的等待队列中去等待(wait),直到有另外一个线程完成了指定条件的操作,它会去唤醒(notify)等待队列中的等待的进程,将他们放入到同步队列中去,到了同步队列中后,这些线程又拥有了去获取锁的权利,等他们获取到锁之后,就可以继续执行他们的代码了。

java中每一个锁都对应了一个等待队列和同步队列。

2)通用模式

一般会有两个以上线程,分别是等待线程和通知线程

等待线程的通用模式

       synchronized(锁对象){   

              逻辑1

              while(条件不满足){

                     锁对象.wait();

              }

              逻辑2

       }

 

       通知线程的通用模式

       synchronized(锁对象){

              完成条件

              锁对象.notifyAll();

       }

 

注:

       a)锁对象要是同一把锁;

       b)wait和notify方法必须写在同步代码块之内,否则会报错。

       c)等待线程判断条件时,需用while,而不是if

3)wait和sleep的区别

       a)wait是object类的示例方法,sleep是Thread类的静态方法;

       b)wait方法会释放锁对象,sleep方法不会释放;

       c)wait停止后可以notify来唤醒,而sleep必须设置时间;

2.6、Callable接口和Future

(0)代码示例

import java.util.concurrent.Callable;

import java.util.concurrent.FutureTask;

public class Demo003 {

public static void main(String[] args) throws Exception{

     Task003 task003 = new Task003();

     FutureTask<Integer> futureTask = new FutureTask<>(task003);

     Thread thread = new Thread(futureTask);

     thread.start();

     System.out.println("线程已经开始");

     Integer result = futureTask.get();

     System.out.println("线程结束,结果为 = " + result);

}

}

class Task003 implements Callable<Integer> {

@Override

public Integer call() throws Exception {

     Thread.sleep(5000L);

     return 100;

}

}

 

(1)Callable接口

@FunctionalInterface

public interface Callable<V> {

    /**

     * Computes a result, or throws an exception if unable to do so.

     *

     * @return computed result

     * @throws Exception if unable to compute a result

     */

    V call() throws Exception;

}

 

(2)Future接口

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)

        throws InterruptedException, ExecutionException, TimeoutException;

}

 

(3)FutureTask类

FutureTask类实现了RunnableFuture接口,RunnableFuture接口实现了Future类和Runnable类。所以FutureTask是Future的实现类,并且它是一个Runnable任务。

所以我们将FutureTask传入Thread的target参数,其实Thread线程执行的是FutureTask的任务,即执行的是FutureTask的run方法。

因为FutureTask对象封装了Callable的子类,所以我们在FutureTask的run方法中就可以同步的执行Callable子类的call方法。当调用FutureTask对象的get()方法时,如果Callable还没执行完,就将其阻塞在此即可,直到call()方法执行完,返回结果。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值