以下学习内容来自此处:https://www.bilibili.com/video/BV1V4411p7EF
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxCNWpXTxklaNVTVU1EMFpXTwJ0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0cjNxUDO1kDMyADNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
线程创建
Java中线程创建的方式有三种:
- 继承
类Thread
- 实现
接口Runnable
- 实现
接口Callable
继承Thread类
- 自定义类继承
类Thread
- 重写
方法run()
- 创建线程对象,调用
方法start()
public class ThreadByExtendsThreadClass extends Thread{
@Override
public void run() {
for(int i = 0; i <= 200; ++i){
System.out.println("thread...." + i);
}
}
public static void main(String[] args) {
ThreadByExtendsThreadClass threadByExtendsThreadClass = new ThreadByExtendsThreadClass();
threadByExtendsThreadClass.start();
for(int i = 0; i <= 200; ++i){
System.out.println("main....." + i);
}
}
}
调用
start()
启动线程不一定立即执行,需要根据CPU分配。
实现Runnable接口
- 实现
接口:Runnable
implements Runnable
- 实现
方法run()
- 创建
线程类对象,并调用Thread
方法来启动start()
public class ThreadByImplementRunnable implements Runnable{
@Override
public void run() {
for(int i = 0; i <= 200; ++i){
System.out.println("thread..." + i);
}
}
public static void main(String[] args) {
new Thread(new ThreadByImplementRunnable()).start();
for(int i = 0; i <= 233; ++i){
System.out.println("main..." + i);
}
}
}
继承Thread类 | 实现Runnable接口 |
---|---|
子类继承Thread类后具备多线程的能力 | 实现接口Runnable具有多线程能力 |
启动线程的方式: | 启动方式: |
不建议使用:避免OOP单继承局限性 | 推荐使用:避免单继承局限性,灵活方便, 且同一个对象可以别多个线程调用 |
使用
Runnable
接口实现的方式是通过 静态代理 。
实现Callable接口
- 实现
接口,指定call返回值类型Callable<>
- 重写
方法,需抛出异常call
- 创建目标对象
- 创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(线程池大小)
- 提交执行:
Feature<Boolean> res = ser.submit(目标对象)
- 获取结果:
boolean res = res.get()
- 关闭服务:
ser.shutdownNow()
好处:可以定义返回值以及抛出异常
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadsImplementCallable implements Callable<String> {
@Override
public String call() throws Exception {
for(int i = 0; i < 10; ++i){
System.out.println(Thread.currentThread().getName() + " " + i);
}
return Thread.currentThread().getName() + " done!";
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThreadsImplementCallable t[] = new ThreadsImplementCallable[3];
for(int i = 0; i < 3; ++i){
t[i] = new ThreadsImplementCallable();
}
ExecutorService executorService = Executors.newFixedThreadPool(3);
List<Future<String>> res = new ArrayList<>();
for(int i = 0; i < 3; ++i){
res.add(executorService.submit(t[i]));
}
for(int i = 0; i < 3; ++i){
System.out.println(res.get(i).get());
}
executorService.shutdownNow();
}
}
仅有一个方法的接口称为函数式接口
匿名内部类没有类的名称,但需要借助接口或者父类
线程的状态
stop
对于让线程停止的方式,建议使用 设置标志循环检测 来实现终止。
sleep
指定当前线程 阻塞 的毫秒数, 存在异常
IntertuptedException
, 时间达到后进入 就绪 状态。每个对象都有一个锁,sleep不会释放
yield
线程礼让,及将线程转为 就绪状态,此时交由CPU调度,可能会执行其他线程也可能不会。
join
合并线程,待该线程执行完毕后,在执行其他线程,其他线程阻塞。(插队)
线程的优先级
守护线程(daemon)
线程分为 用户线程 和 守护线程 ; 其中虚拟机必须确保用户线程执行完毕,而不用等待守护线程执行完毕,如后台的日志、监控内存、垃圾回收(gc)
thread.setDaemon(true)
即设置该线程为保护线程。(默认为false)
线程同步
并发:同一对象被多个线程同时操作。
锁机制:
synchronized
,排他锁,存在以下问题:
- 一个线程持有后其他线程挂起
- 多线程竞争中,频繁加锁和释放会导致较多的上下文切换和调度延时,性能问题
- 优先级高的等待优先级低的释放锁,会导致优先级倒置,性能下降。
synchronized
默认锁的是this对象。
死锁
死锁的必要条件:
- 互斥条件
- 请求与保持条件
- 不剥夺条件
- 循环等待条件
(避免synchronized块中请求多个资源)
Lock
不加锁时:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
public static void main(String[] args) {
Ticket ticket1 = new Ticket();
new Thread(ticket1, "1").start();
new Thread(ticket1, "2").start();
new Thread(ticket1, "3").start();
}
}
class Ticket implements Runnable {
int ticketnum = 10;
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if (ticketnum >= 0) {
System.out.println(Thread.currentThread().getName() + " get " + ticketnum-- + "...");
} else {
break;
}
}
}
}
使用ReentrantLock加锁后:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
public static void main(String[] args) {
Ticket ticket1 = new Ticket();
new Thread(ticket1, "1").start();
new Thread(ticket1, "2").start();
new Thread(ticket1, "3").start();
}
}
class Ticket implements Runnable {
int ticketnum = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
lock.lock();
if (ticketnum >= 0) {
System.out.println(Thread.currentThread().getName() + " get " + ticketnum-- + "...");
} else {
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
对比:
线程通信
线程池
实现方式即
Callable
那里的;前者使用的是 submit来执行,也可以使用
execute()
方法实现。
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadsPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
更加深入的多线程学习以及JUC可以查看这里https://www.bilibili.com/video/BV1B7411L7tE