Java知识总结-多线程的实现

一、多线程的实现方式

编写多线程程序一般有三种方法,Thread,Runnable,Callable.

1).继承Thread类

class MyThread extends Thread{  
  public MyThread(String name) {  
  }  
  public void run(){    
  }  
}  

2).实现Runnable接口

public interface Runnable {
    public abstract void run();
}

3).实现Callable接口

public interface Callable {
    V call() throws Exception;
}

区别

  • Thread不适合于资源的共享
  • Thread类也是Runnable接口的子类。
  • Callable能返回执行结果,Runnable不能返回结果;
  • Callable的call()方法允许抛出异常,Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorsTester {

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
//        executorService.execute(new ThreadForpools(1));

        int a = 1, b = 100;
        Future future = executorService.submit(new Callable() {
            @Override
            public Integer call() throws Exception {
                System.out.println( " 开始处理线程!!!");
                Thread.sleep(3000);

                return a+b;
            }
        });
        System.out.println(future.isDone());
        System.out.println(future.isCancelled());

        while (! future.isDone()) {
            System.out.println("still waiting....");
            Thread.sleep(1000 * 1);
//            System.out.println("OK");
        }
        Integer obj = future.get();
        System.out.println(obj);

        executorService.shutdown();
    }

}

二、线程的状态

  • 创建(new)状态: 准备好了一个多线程的对象
  • 就绪(runnable)状态: 调用了start()方法, 等待CPU进行调度
  • 运行(running)状态: 执行run()方法
  • 阻塞(blocked)状态: 暂时停止执行, 可能将资源交给其它线程使用
  • 终止(dead)状态: 线程销毁

当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源),只有线程运行需要的所有条件满足了,才进入就绪状态。

当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。

当由于突然中断或者子任务执行完毕,线程就会被消亡。


Java知识总结-多线程的实现_第1张图片
1.jpg

blocked、waiting、time waiting又统称为阻塞状态

Java知识总结-多线程的实现_第2张图片
2.jpg

注:sleep和wait的区别:

  • sleep是Thread类的方法,wait是Object类中定义的方法.
  • Thread.sleep不会导致锁行为的改变, 如果当前线程是拥有锁的, 那么Thread.sleep不会让线程释放锁.
    Thread.sleep和Object.wait都会暂停当前的线程. OS会将执行时间分配给其它线程. 区别是, 调用wait后, 需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间.

线程的sleep()方法和yield()方法有什么区别?

  • ① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
  • ② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
  • ③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
  • ④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

请说出与线程同步以及线程调度相关的方法。

wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

守护线程(Daemon Thread)和用户线程(User Thread)的区别

Daemon的作用是为其他线程的运行提供服务,比如说GC线程。其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了。

守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

三、线程的优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。
设置线程优先级有助于帮“线程规划器”确定在下一次选择哪一个线程来优先执行。
设置线程的优先级使用setPriority()方法,此方法在JDK的源码如下:

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);
        }
    }

在Java中,线程的优先级分为1~10这10个等级,如果小于1或大于10,则JDK抛出异常throw new IllegalArgumentException()。

线程优先级特性:

  • 继承性:
    比如A线程启动B线程,则B线程的优先级与A是一样的。
  • 规则性:
    高优先级的线程总是大部分先执行完,但不代表高优先级线程全部先执行完。
  • 随机性:
    优先级较高的线程不一定每一次都先执行完。

你可能感兴趣的:(Java知识总结-多线程的实现)