线程基础和使用

线程的生命周期(来源《JAVA并发编程的艺术》)

创建线程的方式

1、继承Thread类

public class ThreadTest extends Thread{
  public void run(){}
}

public class Main {
  public static void main(String[] args){
    new ThreadTest().start();//一个线程调用两次start()方法将会抛出线程状态异常,也就是的start()只可以被调用一次
  }
}

2、实现Runnable接口

覆写Runnable接口实现多线程可以避免单继承局限

public class ThreadTest implements Runnable {
  public void run(){}
}
public class Main {
  public static void main(String[] args){
    ThreadTest t = new ThreadTest();
    Thread thread = new Thread(t);
    thread().start();
    new Thread(new ThreadTest()).start();//或者这样写
  }
}

3、Callable/Future(可以带返回值):

public class ThreadTest implements Callable<String> {

  @Override
  public String call() throws Exception {
    Thread.sleep(1000);
    return ""SUCCESS"";
  }

  public static void main(String[] args) throws ExecutionException, InterruptedException {
    Callable<String> callable  =new ThreadTest();
    FutureTask <String>futureTask=new FutureTask<>(callable);
    Thread thread=new Thread(futureTask);
    thread().start();
    System.out.println(future.get()); //get()方法会阻塞,直到子线程执行结束才返回
  }
}

4、使用线程池创建

Java通过Executors提供四种线程池:

(1)newCachedThreadPool:

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。不考虑硬件条件,可以有无限大的线程数进来。

public class ThreadTest implements Runnable {
  @Override
  public void run() {
    try {
      System.out.println(""Start"");
      Thread.sleep(1000);
      System.out.println(""线程标识:""+this.toString());
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static  void main(String[]args) {
    ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
    for(int i=0;i<4;i++) {
      newCachedThreadPool.execute(new ThreadTest());
    }
  }
}

结果:不考虑机器性能的情况下,可以有无限大的线程数进来。

(2) newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

public static  void main(String[]args) {
  ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);//保持2个线程可以同时执行
  for(int i=0;i<4;i++) {
    newFixedThreadPool.execute(new ThreadTest());
  }
}

结果:

只能同时保持存在两个线程在执行。

(3) newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

public static  void main(String[]args) {
  //这里是用 ScheduledExecutorService 创建
  ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
  for(int i=0;i<4;i++) {
    //这里是schedule()方法
    newScheduledThreadPool.schedule(new ThreadTest(),3, TimeUnit.SECONDS);
  }
}

结果:线程延迟3秒后才执行

  • schedule(commod,delay,unit) :系统启动后,需要等待delay的时间,然后开始执行。只执行一次,没有周期性。

  • scheduleAtFixedRate(commod,initialDelay,period,unit):以period为固定周期时间,按照一定频率来重复执行任务,initialDelay是说系统启动后,需要等待多久才开始执行。例如:如果设置了period为5秒,线程启动之后执行了大于5秒,线程结束之后,立即启动线程的下一次,如果线程启动之后只执行了3秒就结束了那执行下一次,需要等待2秒再执行。这个是优先保证任务执行的频率,

  • scheduleWithFixedDelay(commod,initialDelay,delay,unit):以delay为固定延迟时间,按照一定的等待时间来执行任务,initialDelay意义与上面的相同。例如:设置了delay为5秒,线程启动之后不管执行了多久,结束之后都需要先生5秒,才能执行下一次。这个是优先保证任务执行的间隔

(4) newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

public static  void main(String[]args) {
    ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
    for(int i=0;i<4;i++) {
        newSingleThreadExecutor.execute(new ThreadTest());
    }
}

结果:


线程的中断(interrupt)

当一个线程while()、sleep()、join()、wait()时,如果想要中断它,不建议使用stop()方法来强制中断,因为程序无法知道这个线程执行到哪一步了,强行中断会产生问题。

正确的应该是用Interrupt来中断:

public class InterruptDemo02 implements Runnable{
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){ //false
            try {
                TimeUnit.SECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //继续中断才能中断这个线程,否则在抛出异常后,线程会有一个复位,导致isInterrupted()重新回到false
                Thread.currentThread().interrupt();
            }
        }
        System.out.println(""End"");
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new InterruptDemo02());
        t1.start();
        Thread.sleep(1000);
        t1.interrupt(); //使得isInterrupted()结果为true
    }
}

join

如果一个线程实例A执行了threadB.join(),其含义是:当前线程A会等待threadB线程终止后threadA才会继续执行。

sleep

让当前线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。需要注意的是如果当前线程获得了锁,sleep方法并不会失去锁。

sleep() VS wait()

  • sleep()方法是Thread的静态方法,而wait是Object实例方法
  • wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()方法没有这个限制可以在任何地方种使用。另外,wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源。而sleep()方法只是会让出CPU并不会释放掉对象锁;
  • sleep()方法在休眠时间达到后如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notift/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行。

yield

当前线程让出CPU,但是,需要注意的是,让出的CPU并不是代表当前线程不再运行了,如果在下一次竞争中,又获得了CPU时间片当前线程依然会继续运行。另外,让出的时间片只会分配给当前线程相同优先级的线程。

通过整型成员变量Priority来控制优先级,优先级的范围从1~10.在构建线程的时候可以通过setPriority(int)方法进行设置,默认优先级为5,优先级高的线程相较于优先级低的线程优先获得处理器时间片。

sleep()和yield()方法,同样都是当前线程会交出处理器资源,而它们不同的是,sleep()交出来的时间片其他线程都可以去竞争,也就是说都有机会获得当前线程让出的时间片。而yield()方法只允许与当前线程具有相同优先级的线程能够获得释放出来的CPU时间片。

参考:并发编程
Executors创建的4种线程池的使用
《java并发编程的艺术》

文章已创建 17

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部