多线程基础知识你都忘了吧,进来看看呗!
好久没有写博客,今天捞一捞多线程
废话不多说,开始搞!
1.线程的三种方式
1.1.继承Thread
第一种方式是通过类继承Thread,实现run方法
然后创建对象调用start方法,会自动执行run方法里面的代码
代码例子如下:
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
static class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println("输出打印:"+i);
}
}
}
}
1.2.实现Runnable接口
第二种方式是通过实现Runnable接口
这种方式,需要我们创建两个对象, new Thread()
, 还有实现 Runnable的 类
调用start方法,同样会自动调用run方法里面的代码
代码例子如下:
public class RunnableTest {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("输出打印:"+i);
}
}
}
}
1.3.实现Callable接口
第三种方式是实现Callable接口
这种比较厉害,能够获取线程执行完毕返回的结果.
使用的时候,我们需要创建一个 FutureTask
对象来包装我们我们实现类,
然后把这个对象传到Thread
的构造函数中,同样是调用start()方法
内部这次调用的是call
方法
public class CallableTest {
public static void main(String[] args) {
//FutureTask包装我们的任务,FutureTask可以用于获取执行直接
FutureTask<Integer> task = new FutureTask<>(new MyCallable());
Thread thread = new Thread(task);
thread.start();
try {
Integer result = task.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int num = 0;
for (int i = 0; i <= 100; i++) {
num +=i;
}
return num;
}
}
}
2.用户线程和守护线程
❝用户线程执行完毕之后,守护线程就停止执行
❞
设置代码:setDaemon()
下面我创建了两个线程(注意:我使用的是Lambad表达式)
第一个线程设置为守护线程,遍历0到100之间的数,每遍历一次睡眠200毫秒
第二线程默认为用户线程,遍历0到100之间的数,每遍历一次睡眠50毫秒
显然是第二个线程执行完毕先
通过执行代码可以发现第二个线程用户线程
执行完毕之后,第一个线程守护线程
就停止执行了
public class DaemonTest {
public static void main(String[] args) {
//守护线程
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(200);
System.out.println("t1:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.setDaemon(true); //设置守护线程
t1.start();
//用户线程
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(50);
System.out.println("t2:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2.start();
}
}
3.线程的优先级
❝线程的切换是由线程调度控制的,我们无法通过代码来进行干涉
但是我们可以通过提高线程的
❞优先级
来最大程度的改善线程获取时间片的几率
❝一个CPU内核在同一个时间内,只能执行一个线程
所以各个线程都在抢夺CPU时间,谁抢到了时间,就执行谁
例如:
A线程抢到了时间2秒,就执行2秒,然后挂起再去抢夺
B线程抢到了时间3秒,就执行3秒,然后挂起再去抢夺
❞
线程的优先级被划分为10级,值分别为1-10
,10最高,线程中提供了3个常量表示最低,最高,以及默认的优先级
Thread.MIN_PRIORITY 1 // 最小等级
Thread.MAX-PRIORITY 10 // 最大等级
Thread.NORM_PRIOIRY 5 // 默认等级
void setPrioriy(int prioriy ) // 设置线程等级
创建两个线程
第一个线程等级设置为 1
第二个线程等级设置为 10
通过执行代码,我们发现第二个线程抢到时间的概率是比第一个线程的概率高的
public class DaemonTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
System.out.println("t1:"+i);
}
});
t1.setPriority(1);//设置优先级为 1,那么抢夺到资源的概率就小
t1.start();
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
System.out.println("t2:"+i);
}
});
t2.setPriority(10); //抢夺资源的概率高
t2.start();
}
}
注意:
如果没有设置等级的话,默认的线程等级为 5
4.线程中断
有两个重要的方法
isInterrupted
,判断当前的线程是否已经中断了
interrupt
中断线程
注意:
stop()
中断线程的方法已经被Java抛弃了,它是通过抛异常的方式中断的,有比较大的误操作,导致事务回滚
示例代码创建一个线程
线程里面写了一个无限循环,每次循环,调用isInterrupted
方法,判断线程是否已经中断
如果已经中断了,可以去执行其他的代码逻辑
执行线程,睡眠2秒之后,调用interrupt
方法,中断线程
public class DaemonTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true){
boolean flag = Thread.currentThread().isInterrupted();
if (flag){
//如果线程中断了,你可以执行某些代码
}else{
System.out.println("线程执行中.......");
}
}
});
t1.start();
try {
//2秒之后中断线程
Thread.sleep(2000);
t1.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5.线程的生命周期
线程的生命周期有
❝NEW 新建对象状态
RUNNABLE 就绪,运行时状态
BLOCKED 加锁等待状态,待其他线程释放锁了执行
WAITING 调用wait等待状态 ,通过notify唤醒
TIMED_WAITING 睡眠状态,睡眠时间到了唤醒
TERMIANTED 销毁状态
❞
多线程基础知识你都忘了吧,进来看看呗!
好久没有写博客,今天捞一捞多线程
废话不多说,开始搞!
1.线程的三种方式
1.1.继承Thread
第一种方式是通过类继承Thread,实现run方法
然后创建对象调用start方法,会自动执行run方法里面的代码
代码例子如下:
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
static class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println("输出打印:"+i);
}
}
}
}
1.2.实现Runnable接口
第二种方式是通过实现Runnable接口
这种方式,需要我们创建两个对象, new Thread()
, 还有实现 Runnable的 类
调用start方法,同样会自动调用run方法里面的代码
代码例子如下:
public class RunnableTest {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("输出打印:"+i);
}
}
}
}
1.3.实现Callable接口
第三种方式是实现Callable接口
这种比较厉害,能够获取线程执行完毕返回的结果.
使用的时候,我们需要创建一个 FutureTask
对象来包装我们我们实现类,
然后把这个对象传到Thread
的构造函数中,同样是调用start()方法
内部这次调用的是call
方法
public class CallableTest {
public static void main(String[] args) {
//FutureTask包装我们的任务,FutureTask可以用于获取执行直接
FutureTask<Integer> task = new FutureTask<>(new MyCallable());
Thread thread = new Thread(task);
thread.start();
try {
Integer result = task.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int num = 0;
for (int i = 0; i <= 100; i++) {
num +=i;
}
return num;
}
}
}
2.用户线程和守护线程
❝用户线程执行完毕之后,守护线程就停止执行
❞
设置代码:setDaemon()
下面我创建了两个线程(注意:我使用的是Lambad表达式)
第一个线程设置为守护线程,遍历0到100之间的数,每遍历一次睡眠200毫秒
第二线程默认为用户线程,遍历0到100之间的数,每遍历一次睡眠50毫秒
显然是第二个线程执行完毕先
通过执行代码可以发现第二个线程用户线程
执行完毕之后,第一个线程守护线程
就停止执行了
public class DaemonTest {
public static void main(String[] args) {
//守护线程
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(200);
System.out.println("t1:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.setDaemon(true); //设置守护线程
t1.start();
//用户线程
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(50);
System.out.println("t2:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2.start();
}
}
3.线程的优先级
❝线程的切换是由线程调度控制的,我们无法通过代码来进行干涉
但是我们可以通过提高线程的
❞优先级
来最大程度的改善线程获取时间片的几率
❝一个CPU内核在同一个时间内,只能执行一个线程
所以各个线程都在抢夺CPU时间,谁抢到了时间,就执行谁
例如:
A线程抢到了时间2秒,就执行2秒,然后挂起再去抢夺
B线程抢到了时间3秒,就执行3秒,然后挂起再去抢夺
❞
线程的优先级被划分为10级,值分别为1-10
,10最高,线程中提供了3个常量表示最低,最高,以及默认的优先级
Thread.MIN_PRIORITY 1 // 最小等级
Thread.MAX-PRIORITY 10 // 最大等级
Thread.NORM_PRIOIRY 5 // 默认等级
void setPrioriy(int prioriy ) // 设置线程等级
创建两个线程
第一个线程等级设置为 1
第二个线程等级设置为 10
通过执行代码,我们发现第二个线程抢到时间的概率是比第一个线程的概率高的
public class DaemonTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
System.out.println("t1:"+i);
}
});
t1.setPriority(1);//设置优先级为 1,那么抢夺到资源的概率就小
t1.start();
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
System.out.println("t2:"+i);
}
});
t2.setPriority(10); //抢夺资源的概率高
t2.start();
}
}
注意:
如果没有设置等级的话,默认的线程等级为 5
4.线程中断
有两个重要的方法
isInterrupted
,判断当前的线程是否已经中断了
interrupt
中断线程
注意:
stop()
中断线程的方法已经被Java抛弃了,它是通过抛异常的方式中断的,有比较大的误操作,导致事务回滚
示例代码创建一个线程
线程里面写了一个无限循环,每次循环,调用isInterrupted
方法,判断线程是否已经中断
如果已经中断了,可以去执行其他的代码逻辑
执行线程,睡眠2秒之后,调用interrupt
方法,中断线程
public class DaemonTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true){
boolean flag = Thread.currentThread().isInterrupted();
if (flag){
//如果线程中断了,你可以执行某些代码
}else{
System.out.println("线程执行中.......");
}
}
});
t1.start();
try {
//2秒之后中断线程
Thread.sleep(2000);
t1.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5.线程的生命周期
线程的生命周期有
❝NEW 新建对象状态
RUNNABLE 就绪,运行时状态
BLOCKED 加锁等待状态,待其他线程释放锁了执行
WAITING 调用wait等待状态 ,通过notify唤醒
TIMED_WAITING 睡眠状态,睡眠时间到了唤醒
TERMIANTED 销毁状态
❞

在运行过程中,各个线程都在抢夺CPU的时间,没有抢夺到CPU时间在就绪
里面,随时准备执行
如果某个线程设置了加锁
,或者设置wait
,或者设置了sleep
,
线程就会进入阻塞状态,等待被唤醒,被唤醒之后,重新进入就绪
里面
6.join方法
❝❞
join()
可以理解为插队执行,例如我在A线程里面调用B线程的join()
那么就会先执行B线程后再执行A线程
例如下面我创建了三个线程分别为t1,t2,t3
我的要求是他们的执行顺序必须要是先执行t1,再到t2,然后到t3
这时候就需要使用到了join
方法
当t2线程抢夺到了CPU时间,我调用t1线程的join方法插队执行
当t3线程抢夺到了CPU时间,我调用t2线程的join方法插队执行
public class DaemonTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("t1: "+ i);
}
});
Thread t2 = new Thread(() -> {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("t2: "+ i);
}
});
Thread t3 = new Thread(() -> {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("t3: "+ i);
}
});
t1.start();
t2.start();
t3.start();
}
}
7.线程常用API
start() // 启动线程
getID() // 获取当前线程ID Thread-编号 (编号从0开启)
getName() // 获取当前线程名称
stop() // 停止线程(不建议使用)
getPriority() // 返回线程的优先级
isAlive() // 测试线程是否处于活动状态
isDaemon() // 测试线程是否为守护线程
isInterrupted() // 测试线程是否已经中断
Thread.currentThread() // 获取当前线程对象
在运行过程中,各个线程都在抢夺CPU的时间,没有抢夺到CPU时间在就绪
里面,随时准备执行
如果某个线程设置了加锁
,或者设置wait
,或者设置了sleep
,
线程就会进入阻塞状态,等待被唤醒,被唤醒之后,重新进入就绪
里面
6.join方法
❝❞
join()
可以理解为插队执行,例如我在A线程里面调用B线程的join()
那么就会先执行B线程后再执行A线程
例如下面我创建了三个线程分别为t1,t2,t3
我的要求是他们的执行顺序必须要是先执行t1,再到t2,然后到t3
这时候就需要使用到了join
方法
当t2线程抢夺到了CPU时间,我调用t1线程的join方法插队执行
当t3线程抢夺到了CPU时间,我调用t2线程的join方法插队执行
public class DaemonTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("t1: "+ i);
}
});
Thread t2 = new Thread(() -> {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("t2: "+ i);
}
});
Thread t3 = new Thread(() -> {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("t3: "+ i);
}
});
t1.start();
t2.start();
t3.start();
}
}
7.线程常用API
start() // 启动线程
getID() // 获取当前线程ID Thread-编号 (编号从0开启)
getName() // 获取当前线程名称
stop() // 停止线程(不建议使用)
getPriority() // 返回线程的优先级
isAlive() // 测试线程是否处于活动状态
isDaemon() // 测试线程是否为守护线程
isInterrupted() // 测试线程是否已经中断
Thread.currentThread() // 获取当前线程对象