Java线程知识考点

2022-07-28,,,

Java线程知识考点

1. 进程和线程的区别

进程和线程的由来

进程是资源分配的最小单位,线程是CPU调度的最小单位

  • 所有与进程相关的资源,都被记录在PCB中
  • 进程是抢占处理机的调度单位;线程属于某个进程,共享器资源
  • 线程只由堆栈寄存器、程序计数器和TCB组成

总结:

  1. 线程不能看做独立应用,而进程可看做独立应用
  2. 进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径
  3. 线程没有独立的地址空间,多进程的程序比多线程程序健壮
  4. 进程的切换比线程的切换开销大

2. Java进程和线程的关系

  • Java对操作系统提供的功能进行封装,包括线程和进程
  • 运行一个程序会产生一个进程,进程包含至少一个线程
  • 每个进程对应一个JVM实例多个线程共享JVM里的堆
  • Java采用单线程变成模型,程序会自动创建主线程
  • 主线程可以创建子线程,原则上要后于子线程完成执行
public class CurrentThreadDemo {
    public static void main(String[] args) {
        System.out.println("Current Thread: "+Thread.currentThread().getName());
    }
}

Java程序会自动创建主线程

3. Thread中的start和run方法的区别?

package cn.itcast.java.basic;

public class ThreadTest {
    private static void attack(){
        System.out.println("Fight");
        System.out.println("Current Thread: "+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        Thread t=new Thread(){
            public void run(){
                attack();
            }
        };
        System.out.println("current main thread is :"+Thread.currentThread().getName());
        t.run();
    }
}

运行结果:run方法调用同个线程

修改方法

 public static void main(String[] args) {
        Thread t=new Thread(){
            public void run(){
                attack();
            }
        };
        System.out.println("current main thread is :"+Thread.currentThread().getName());
        //t.run();
        t.start();
    }

运行结果:

  1. 调用start()方法会创建一个新的子线程并启动start方法可启动多线程
  2. run()方法只是Thread的一个普通方法的调用还是在主线程里执行,是不会开启多线程的

4. Thread和Runnable的区别?

  1. Thread是实现了Runnable接口的类,使得run支持多线程
  2. 因类的单一继承原则,推荐多使用Runnable接口
  • 继承Thread类实现多线程

MyThread

package cn.itcast.java.basic;

public class MyThread extends Thread{
    private String name;
    public MyThread(String name){
        this.name=name;
    }
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println("Thread start: "+this.name+",i="+i);
        }
    }
}

ThreadDemo

package cn.itcast.java.basic;

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread mt1=new MyThread("thread1");
        MyThread mt2=new MyThread("thread2");
        MyThread mt3=new MyThread("thread3");
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

运行结果:线程交互执行,线程1还没执行完,线程2也在执行了

  • 实现Runnable接口 (多线程实现方式2)

可通过Thread类启动Runable实现多线程

MyRunnable

package cn.itcast.java.basic;

public class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name){
        this.name=name;
    }
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Thread start: "+this.name+",i="+i);
        }
    }
}

package cn.itcast.java.basic;

public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable mr1=new MyRunnable("runnable1");
        MyRunnable mr2=new MyRunnable("runnable2");
        MyRunnable mr3=new MyRunnable("runnable3");
        Thread t1=new Thread(mr1);
        Thread t2=new Thread(mr2);
        Thread t3=new Thread(mr3);
        t1.start();
        t2.start();
        t3.start();
    }

}

运行结果:

5. 如何给run()方法传参?

实现的方式主要有三种

6. 如何实现处理线程的返回值?

实现的方式主要有三种

1. 主线程等待法

package cn.itcast.java.basic.thread;

public class CycleWait implements Runnable{
    private String value;
    public void run() {
       try{
           Thread.currentThread().sleep(5000);//主线程睡眠5秒
       }catch (InterruptedException e){
           e.printStackTrace();
       }
       value="we have data now";
    }

    public static void main(String[] args) throws InterruptedException {
        CycleWait cw=new CycleWait();
        Thread t=new Thread(cw);
        t.start();
        //主线程等待法
        //begin
        while (cw.value==null){
            Thread.currentThread().sleep(100);//主线程等待100毫秒
        }
        //end
        System.out.println("value :"+cw.value);
    }
}

运行结果:

2. 使用Thread类的join()阻塞当前线程以等待子线程处理完毕

修改main方法

package cn.itcast.java.basic.thread;

public class CycleWait implements Runnable{

    //....
    public static void main(String[] args) throws InterruptedException {
        CycleWait cw=new CycleWait();
        Thread t=new Thread(cw);
        t.start();
        t.join();
        System.out.println("value :"+cw.value);
    }
}

运行结果:结果一致

3. 通过Callable接口实现: 通过FutureTask Or 线程池获取

  • 通过FutureTask

MyCallable

package cn.itcast.java.basic.thread;
import java.util.concurrent.Callable;

public class MyCallable implements Callable<String>{
    public String call() throws Exception {
        String value="test";
        System.out.println("Ready to work");
        Thread.currentThread().sleep(5000);
        System.out.println("task done");
        return value;
    }
}

FutureTaskDemo

package cn.itcast.java.basic.thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task=new FutureTask<String>(new MyCallable());
        new Thread(task).start();
        if(!task.isDone()){
            System.out.println("tast has not finished,please wait");
        }
        System.out.println("task return:"+task.get());
    }
}

运行结果:

  • 通过线程池获取
package cn.itcast.java.basic.thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        //创建可缓存线程池
        ExecutorService newCachedThreadPool= Executors.newCachedThreadPool();
        Future<String> future = newCachedThreadPool.submit(new MyCallable());
        if(!future.isDone()){//没有完成
            System.out.println("tast has not finished,please wait");
        }
        try {
            //等待
            System.out.println("task return:"+future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
            newCachedThreadPool.shutdown();//关闭线程池
        }
    }
}

运行结果:

7.线程的几种状态

六个状态:

  1. 新建(New):创建后尚未启动的线程的状态
  2. 运行(Runnable):包含Running和Ready
  3. 无限期等待(Waiting):不会被分配CPU执行时间,需要显示被唤醒
    • 没有设置Timeout参数的Object.wait()方法
    • 没有设置Timeout参数的Thread.join()方法
    • LockSupport.park()方法
  4. 限期等待(Timed Waiting):在一定时间后会由系统自动唤醒
    • Thread.sleep()方法
    • 设置了Timeout参数的Object.wait()方法
    • 设置了Timeout参数的Thread.join()方法
    • LockSupport.parkNanos()方法
    • LockSupport.parkUntil()方法
  5. 阻塞(Blocked):等待获取排它锁
  6. 结束状态(Terminated):已终止线程的状态,线程已经结束执行

8. sleep和wait的区别

基本的差别:

  1. sleep是Thread的方法,wait是Object类中定义的方法
  2. sleep()方法可以在任何地方使用
  3. wait()方法只能在synchronized方法或synchronized块中使用

最主要的本质区别:

  1. Thread.sleep只会让出CPU,不会锁行为的改变
  2. Object.wait不仅让出CPU,还会释放已经占有的同步资源锁
package cn.itcast.java.basic.thread.seven;

public class WaitSleepDemo {
    public static void main(String[] args) {
        final Object lock=new Object();
        //线程1执行wait方法
        new Thread(new Runnable() {
            public void run() {
                System.out.println("Thread A is waiting to get lock");
                synchronized (lock){//获取同步锁
                    try{
                        System.out.println("Thread A get lock");
                        Thread.sleep(20);
                        System.out.println("Thread A do wait method");
                        lock.wait(1000);//限期等待1秒
                        System.out.println("Thread A is done");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        //主线程休眠10毫秒
        try{
            Thread.sleep(10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        //线程2执行sleep方法
        new Thread(new Runnable() {
            public void run() {
                System.out.println("Thread B is waiting to get lock");
                synchronized (lock){//获取同步锁
                    try{
                        System.out.println("Thread B get lock");
                        System.out.println("Thread B is sleeping 10 ms");
                        Thread.sleep(20);
                        System.out.println("Thread B do wait method");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

运行结果如下:Object.wait不仅让出CPU,还会释放已经占有的同步资源锁

Thread.sleep只会让出CPU,不会锁行为的改变(即不会释放锁)

package cn.itcast.java.basic.thread.seven;

public class WaitSleepDemo2 {
    public static void main(String[] args) {
        final Object lock=new Object();
        //线程1执行wait方法
        new Thread(new Runnable() {
            public void run() {
                System.out.println("Thread A is waiting to get lock");
                synchronized (lock){//获取同步锁
                    try{
                        System.out.println("Thread A get lock");
                        Thread.sleep(20);
                        System.out.println("Thread A do wait method");
                        Thread.sleep(1000);//限期等待1秒
                        System.out.println("Thread A is done");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        //主线程休眠10毫秒
        try{
            Thread.sleep(10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        //线程2执行sleep方法
        new Thread(new Runnable() {
            public void run() {
                System.out.println("Thread B is waiting to get lock");
                synchronized (lock){//获取同步锁
                    try{
                        System.out.println("Thread B get lock");
                        System.out.println("Thread B is sleeping 10 ms");
                        lock.wait(20);
                        System.out.println("Thread B do wait method");
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

运行结果:

9. notify和notifyAll的区别

两个概念:

  • 锁池EntryList
  • 等待池WaitSet

锁池:

  • 假设线程A已经拥有了某个对象(不是类)的锁,而其他线程B,C想调用这个对象的某个synchronized方法(或者块),由于B,C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正在被线程A所占用,此时B,C线程就会被阻塞,进入一个地方去等待锁的释放,这个地方便是该对象的锁池

等待池:

  • 假设线程A调用了某个对象的wait()方法,线程A1就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入等待池中的线程不会去竞争该对象的锁。
  1. notifyAll 会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
  2. notify 只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会

10. yield

概念:

  • 当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能忽略这个暗示。

11. 如何中断线程?

已经被抛弃的方法

  • 通过调用stop()方法停止线程
  • 通过调用suspend()和resume()方法

目前使用的方法

  • 调用interrupt(), 通知线程应该中断了

  • 如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常

  • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置中断标志的线程将继续正常执行,不受影响。

  • 需要被调用的线程配合中断

  • 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。

  • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置中断标志的线程将继续正常运行,不受影响。

12. 线程状态以及状态之间的转换

本文地址:https://blog.csdn.net/qq_22075913/article/details/109480971

《Java线程知识考点.doc》

下载本文的Word格式文档,以方便收藏与打印。