概念
了解多线程前先来解释一下有关线程的相关名词的概念
程序
为完成特定任务、用某种语言编写的一组指令的集合。即一段代码。
进程
是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。
线程
进程可进一步细化为线程,是一个程序内部的一条执行路径。
并行
多个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()释放。