JAVA中的多线程

161 阅读4分钟

概念

了解多线程前先来解释一下有关线程的相关名词的概念

程序

为完成特定任务、用某种语言编写的一组指令的集合。即一段代码。

进程

是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。

线程

进程可进一步细化为线程,是一个程序内部的一条执行路径。

并行

多个CPU同时执行多个任务

并发

一个CPU同时执行多个任务

JAVA创建线程的方式

继承Thread

public class ThreadOne extends Thread {

    @Override
    public void run() {
        System.out.println("Thread run");
    }
}

通过实例化调用对象的start方法开启线程

	new ThreadOne().start();

实现Runnable接口

相比继承的方式更加灵活

public class ImplRunnable implements Runnable {
    public void run() {
        System.out.println("Thread Run");
    }
}

将对象作为构造参数实例化Thread,调用Thread的start方法。

        Thread thread = new Thread(new ImplRunnable());
        thread.start();

实现Callable接口

能够接收线程返回结果

public class ImplCallable implements Callable<String> {

    public String call() throws Exception {
        System.out.println("Thread run");
        return "Callable thread return";
    }
}

线程开启及接收返回值方式

        ImplCallable implCallable = new ImplCallable();
        FutureTask<String> futureTask = new FutureTask<String>(implCallable);
        Thread thread = new Thread(futureTask);
        thread.run();
        String s = futureTask.get(); //这里会阻塞到线程返回结果
        System.out.println(s);

线程池

为什么要有线程池?

经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
线程池就因此而诞生,提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

JAVA中创建线程池的方法

线程池工厂类 Executors

  • newCachedThreadPool 创建一个可根据需要创建新线程的线程池
  • newFixedThreadPool 创建一个可重用固定线程数的线程池
  • newSingleThreadExecutor 创建一个只有一个线程的线程池
  • newScheduledThreadPool 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 通过工厂会返回一个 ExecutorService 线程池执行服务 可通过 execute 执行实现Runnable接口的线程,也可通过submit方法执行实现Callable的线程。
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new ImplRunnable()); //执行Runnable
        Future<String> future = executorService.submit(new ImplCallable()); //执行Callable
        System.out.println(future.get());

Thread中的方法

start

启动当前线程;调用当前线程的run()

run

通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中

currentThread

静态方法,返回执行当前代码的线程

getName

获取当前线程的名字

setName

设置当前线程的名字

yield

释放当前CPU的执行权

join

在线程a中调用线程b的join(),此时a进入阻塞状态,直到线程b执行完以后,线程a才结束阻塞状态

stop

已过时。当执行此方法,强制结束当前线程

sleep

让当前线程阻塞指定的毫秒数

isAlive

判断当前线程是否存活

线程中的死锁

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了死锁。

线程中的通信

方法

wait

一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。

notify

一旦执行此方法,就好唤醒wait的一个线程。如果有多个线程被wait,就唤醒优先级高的。

notifyAll

一旦执行此方法,就会唤醒所有被wait的线程。

注意点

  • wait(),notify(),notifyAll()三个方法必须使用在同步代码块中
  • wait(),notify(),notifyAll()三个方法调用者必须是同步代码块或同步方法中的同步监视器。否则会出现异常。
  • wait(),notify(),notifyAll()三个方法定义在java.lang.Object中

sleep()和wait()的异同

相同点:
一旦执行方法,都可以使线程阻塞

不同点:

  • 两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
  • 调用的要求不同:sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码块中。
  • 关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不释放同步监视器,而wait()释放。