欢迎光临
我们一直在努力

java中的线程

1. 什么是线程

        一个进程有一个或多个线程(至少包含主线程),线程是不需要单独分配进程资源,多个线程共享着进程内部的资源,它依靠进程存在。线程是进程中真正执行任务的一方,它有自己的程序计算器、方法栈等。线程在执行任务时相互之间的切换相对灵活,一个任务可能会由多个线程进行执行(多线程)。进程是系统资源分配的最小单位,线程是系统调度的最小单位

2. 线程创建

        java中线程的入口为run()方法,当线程创建并执行strat()方法后会自动执行run()方法里的代码

2.1 继承Thread重写run方法

public class MyThread extends Thread {
@Override
public void run() {
while(true){
System.out.println("MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

2.2 实现Runnable接口并重写run方法

public class MyThread implements Runnable {
@Override
public void run() {
while(true){
System.out.println("MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}

2.3 通过内部类重写Thread中的run方法

Thread thread = new Thread() {
@Override
public void run() {
while (true) {
System.out.println("MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};

2.4 通过内部类实现Runnable接口中的run方法

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("MyThread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});

2.5 用lambda表达式创建

Thread thread = new Thread(()->{
while (true){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("MyThread");
}
});

3. 线程状态

1. NEW: 仅创建线程对象(如 new Thread(…)),未调用 start() 方法,线程还未被操作系统调度

2. RUNNABLE: 调用 start() 后,线程进入 “可运行” 状态(等待 CPU 调度)或 “运行中” 状态(CPU 正在执行线程代码);注意:RUNNABLE包含 “就绪” 和 “运行” 两种子状态,Java 层面统一归为 RUNNABLE

3. BLOCKED: 程竞争 synchronized 锁失败(锁被其他线程持有),会进入该状态;直到持有锁的线程释放锁,当前线程竞争到锁后回到 RUNNABLE

4. WAITING: 线程进行没有超时时间的等待

5. TIMED_WAITING: 线程进行有超时时间的等待

6. TERMINATED: 线程执行完毕

4. Thread

        Thread表示java中的一个类,一个Thread对象对应操作系统中的一个线程

4.1 构造方法

        执行构造方法时我们还可以传参数给对象创建名字

4.2 属性

1. getId获取线程的ID,ID线程的唯一标识符,每个线程对应一个ID不会重复

2. getName可以获取线程的名字,线程的名字可以在调试工具中用到

3. getState用于获取当前线程状态

4. getPriority用于获取线程的优先级,优先级高的线程会先执行

5. isDaemon用于判断当前线程是否为后台线程。线程可以分为后台线程和前台线程,区别在于进程是否结束需要看前台线程是否结束,而后台线程不影响进程的结束

6. isAlive用户判断线程是否存活,可能在操作系统中线程已经结束但在java进程中线程还未结束

7. 用户判断线程是否被中断

4.3 开启线程

        一个线程对象创建后,如果没有执行start()开启方法,线程中定义的run()方法也不会被执行,即线程处于NEW转态而不是RUNNABLE

Thread thread = new Thread(()->{
while (true){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("MyThread");
}
});
thread.start();

4.4 线程等待

        如果要满足一个线程执行结束后再执行另一个线程的代码逻辑,就可以用到join()方法,哪个线程对象调用这个方法这个线程就为被等待线程,在哪个线程中执行的对应的线程就是等待线程

public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("T1");
}
},"T1");
System.out.println("T1线程开启");
thread.start();
thread.join();
System.out.println("T1线程结束");
System.out.println("main线程开启");
}
}

 结果:

     当前中被等待线程为T1,等待线程为main,只要在T1线程结束后再执行main线程   

T1线程开启
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1线程结束
main线程开启

4.5 获取当前线程

        currentThread()为一个静态方法,用于获取当前线程的对象的引用

public class ThreadTest {
public static void main(String[] args) {
Thread thread = new Thread(()->{
System.out.println("T1中: "+Thread.currentThread().getName());
},"T1");
thread.start();
System.out.println("main中: "+Thread.currentThread().getName());
}
}

  结果:

4.6 休眠线程

        我们可以通过sleep()方法对线程进行休眠,这里的休眠就是把cpu资源先让出来等到当前线程可以执行时获得cpu,也就是实际休眠的时间会大于等于设置的时间,时间的单位为毫秒

Thread thread = new Thread(()->{
while (true){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("MyThread");
}
});

4.7 中断线程

        当想让一个线程中途结束,可以通过中断线程的形式,但具体是直接结束还是继续执行要看代码怎么写(是否捕获InterruptedException)

1. interrupt方法用于中断当前线程,如果当前线程处于运行状态则仅设置中断标记为 true,线程会继续执行,需要代码主动检查标记并处理中断。如果当前线程为阻塞状态时设置中断标记为 true 后,会立即抛出 InterruptedException 异常,同时自动清除中断标记(标记变回 false);线程是否终止,取决于是否捕获该异常并编写终止逻辑

2. interrupted方法是一个静态的方法,用于判断当前线程中断标识位是否为true,如果是就改为false

3. isInterrupted方法是一个实例方法,用于判断目前线程是否为true,但不改变标识位

5.线程安全问题

        如果代码存在多线程的情况下,可能会存在结果不符合预期的情况

实例:

public class Test {
public static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int i = 0; i < 5000; i++) {
count++;
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 5000; i++) {
count++;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count: "+ count);
}
}

//输出大多数情况下不为10000

1. 线程不安全的原因

1. 线程的调度是随机的

        多个线程在执行的过程中是随机交互执行的,这也是线程不安全的主要原因

2. 线程共享同一个变量

        上⾯的线程不安全的代码中, 涉及到多个线程针对 count 变量进⾏修改. 此时这个 count 是⼀个多个线程都能访问到的 "共享数据"

3.  操作不是原⼦的

        上面的代码count++看似就一条命令,实则是三条指令包括:1. 从内存中取出变量的值 2. 修改这个变量 3. 把变量放回到内存中。这三个指令在多线程的执行状态下可能会发生交错执行

4. 内存可见性

        可⻅性指, ⼀个线程对共享变量值的修改,能够及时地被其他线程看到

5. 指令重排序

        编译器在编译代码的时候会在不修改代码逻辑的情况下,对指令的先后顺序进行修改,从而达到优化代码的效果,修改指令先后顺序的操作就是指令重排序

2. 解决线程问题的方案

        线程调度是随机的这个问题是操作系统约定的所以不好操作,而修改同一变量可能受到业务的限制不能修改,原子性问题我们可以通过加锁的操作达到(常见的所包括synchronized和lock(),通常用前者因为后者需要手动解锁),指令重排序和内存可见性可以通过volatile关键字进行保证

赞(0)
未经允许不得转载:171主机测评 » java中的线程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址