线程基础知识和常用方法
JUC开发基础知识
先有进程,然后进程可以创建线程,线程是依附在进程里面的,线程里面包含多个协程
进程之间不共享全局变量,线程之间共享全局变量(线程通信就是用的这个,线程之间可以共享全局资源)
进程、线程、协程
进程就是一个应用单元、一个进程由多个线程组成。线程是CPU调度的基本单位。协程由java19新出也就是虚拟线程。
- 并行、并发、串行
并发:CPU上下文切换交替执行 并行:多核CPU同时执行任务
CPU核心数和线程数的关系
上下文切换
start和run
start方法会启动一个线程,程序会异步执行。而执行run方法不会启动一个线程,代码还是同步执行。
sleeep
线程睡眠方法。在以后的代码中推荐使用:TimeUnit.SECONDS.sleep(1);
在where(true)循环访问锁的过程中,可以加入sleep让线程阻塞时间,防止大量占用CPU资源。
让步和优先级
Thread.yield(),方法的作用:暂停当前正在执行的线程对象,并执行其他线程。
yield()做的是让当前运行的线程回到可运行状态,以允许具相同优先级的其他线程获得运行机会。因此使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield达到让步目的。
yield()方法并不能保证线程一定会让出CPU资源,它只是一个提示,告诉线程调度器当前线程愿意让出CPU资源。具体是否让出CPU资源,还是由调度器决定。(如果CPU资源不紧张,一般也不会让出,如果CPU资源紧张,调度器收到让步通知后,会将占有资源优先分给其他同优先级线程)
public class Test {
public static void main(String[] args) {
Runnable task1=new Task1();
Runnable task2 =new Task2();
Thread t1=new Thread(task1);
Thread t2=new Thread(task2);
t1.start();
t2.start();
}
static class Task1 implements Runnable{
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("A:"+i);
}
}
}
static class Task2 implements Runnable{
public void run() {
for (int i = 0; i < 6; i++) {
Thread.yield();
System.out.println("B:"+i);
}
}
}
}
优先级
t1.setPriority(Thread.MAX_PRIORITY);,设置线程执行优先级,数字越大优先级越高和yield一样,也是通知线程调度器,如果CPU资源空闲一般会优先执行,如果CPU资源紧张,如果没有其他线程让出资源的情况下也只能干等着。
public class Test {
public static void main(String[] args) {
Runnable task1=new Task1();
Runnable task2 =new Task2();
Thread t1=new Thread(task1);
t1.setPriority(Thread.MAX_PRIORITY);
Thread t2=new Thread(task2);
t1.start();
t2.start();
}
static class Task1 implements Runnable{
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("A:"+i);
}
}
}
static class Task2 implements Runnable{
public void run() {
for (int i = 0; i < 6; i++) {
Thread.yield();
System.out.println("B:"+i);
}
}
}
}
打断线程
正在睡眠的线程被打断,会报java.lang.InterruptedException异常
public class Sleep {
public static void main(String[] args) {
Thread t1=new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡眠结束");
});
t1.start();
t1.interrupt();
}
}
- 打断线程
静态方式会清除打断标记,此时如果中断正在执行的线程,则撤销打断代码不会报异常。对象方法不会清除打断标记,撤销打断,还是会报异常
撤销打断
public class Sleep {
public static void main(String[] args) {
Thread t1=new Thread(()->{
try {
Thread.interrupted();
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡眠结束");
});
t1.start();
t1.interrupt();
}
}
- 对象方法不会撤销打断,清除打断标志
@Slf4j
public class Sleep {
public static void main(String[] args) {
Thread t1=new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡眠结束");
});
t1.start();
log.info(t1.isInterrupted()+"");
t1.interrupt();
log.info(t1.isInterrupted()+"");
}
}
线程中断
interrupt()方式会中断线程。仅仅是设置线程的中断状态为ture,不会停止线程。
- 代码证明
public class Sleep {
public static void main(String[] args) {
Thread t=new Thread(()->{
while (true){
System.out.println("定时监控");
}
});
t.start();
t.interrupt(); //线程中断
System.out.println(t.isInterrupted());
}
}
线程手动中断
如果当前线程中断标记是true,则手动中断当前线程
public class Sleep {
public static void main(String[] args) {
Thread t=new Thread(()->{
while (true){
System.out.println("定时监控");
//获取线程中断标记
boolean interrupted = Thread.interrupted();
//如何线程中断标记是true,则中断该线程
if (interrupted){
break;
}
}
});
t.start();
t.interrupt();
System.out.println(t.isInterrupted());
}
}
- 需要特别注意和sleep配合使用的时候容易出现的问题
当出现InterruptedException 会清除中断标记 设置成false,此时再手动中断线程是无法中断的
public class Sleep {
public static void main(String[] args) {
Thread t=new Thread(()->{
while (true){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
//当出现InterruptedException 会清除中断标记 设置成false,此时再手动中断线程是无法中断的
e.printStackTrace();
}
System.out.println("定时监控");
if (Thread.interrupted()) { //false
break;
}
}
});
t.start();
t.interrupt();
System.out.println(t.isInterrupted());
}
}
解决方法:再次加上中断
public class Sleep {
public static void main(String[] args) {
Thread t=new Thread(()->{
while (true){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
//当出现InterruptedException 会清除中断标记 设置成false,此时再使用静态方法手动中断线程是无法中断的
e.printStackTrace();
//再次加上中断标记
Thread.currentThread().interrupt();
}
System.out.println("定时监控");
if (Thread.interrupted()) {
break;
}
}
});
t.start();
t.interrupt();
System.out.println(t.isInterrupted());
}
}
加入线程join()
作用:子线程执行完毕后,主线程才执行。比如t.join()后,t线程执行完毕,主线程才执行
public class Sleep {
static int value=1;
public static void main(String[] args) {
Thread t=new Thread(()->{
value=10;
System.out.println("线程runable");
});
t.start();
try {
//加入线程,t线程执行完毕才执行主线程,也就是t线程先执行并执行完毕后才执行主线程
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("主线程:"+value);
}
}
isAlive
线程是否存活
public class Sleep {
static int value=1;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
value=10;
System.out.println("线程runable");
});
t.start();
System.out.println(t.isAlive()); //true
t.join();
System.out.println(t.isAlive()); //false,由于加入线程了,子线程已经执行完毕,此时线程不是存活状态,false
System.out.println("主线程:"+value);
}
}
守护线程
守护线程会伴随所有普通线程,所有普通线程结束后,守护线程立马终结
public class Sleep {
static int value=1;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
for (int i = 0; i < 100; i++) {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("子线程:"+i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.setDaemon(true); //设置t为守护线程
t.start();
System.out.println("主线程:"+value);
}
}
实现Callable实现线程
声明一个任务
public class ExcuteSumTask implements Callable<Integer> {
int t=100;
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 1; i <= t; i++) {
sum=sum+i;
}
return sum;
}
}
- 测试
声明一个未来任务对象,将定义的任务放到任务对象中
public class Test {
public static void main(String[] args) {
Callable<Integer> excuteTask=new ExcuteSumTask();
//定义一个未来任务对象
FutureTask<Integer> task = new FutureTask<>(excuteTask);
Thread t=new Thread(task);
//启动线程
t.start();
try {
//获得线程执行结果
System.out.println(task.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}