进程与线程、上下文切换、线程创建、守护线程和用户线程、死锁的四个必要条件

在这里插入图片描述
上述的图有些简略,下面详细说明下,线程共有6种状态

new,runnable,blocked,waiting,timed waiting,terminated

1,当进入synchronized同步代码块或同步方法时,且没有获取到锁,线程就进入了blocked状态,直到锁被释放,重新进入runnable状态

2,当线程调用wait()或者join时,线程都会进入到waiting状态,当调用notify或notifyAll时,或者join的线程执行结束后,会进入runnable状态

3,当线程调用sleep(time),或者wait(time)时,进入timed waiting状态

当休眠时间结束后,或者调用notify或notifyAll时会重新runnable状态

4,程序执行结束,线程进入terminated状态

注意:
blocked,waiting,timed waiting 我们都称为阻塞状态
上述的就绪状态和运行状态,都表现为runnable状态

1 进程与线程的概念

进程是受操作系统管理的基本运行单元,线程可以理解成是在进程中独立运行的子任务。注意:多线程是异步的,所以千万不要把代码的书写顺序当成线程执行的顺序,线程的调用的时机是随机的。根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

多线程的好处:可以提高 CPU 的利用率。在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待,这样就大大提高了程序的效率。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

多线程的劣势:线程也是程序,所以线程需要占用内存,线程越多占用内存也越多多线程需要协调和管理,所以需要 CPU 时间跟踪线程线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。

2什么是上下文切换?

当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换

3 线程创建的四种方式

创建线程有四种方式:
1 继承 Thread 类;
2 实现 Runnable 接口;
3 实现 Callable 接口;
4 使用 Executors 工具类创建线程池**【ThreadPool来创建】**

3.1 继承Thread

public class Run {
    
    
    public static void main(String[] args) {
    
    
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("运行结束!");
    }
}
class MyThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        System.out.println("MyThread");
    }
}

注意:代码的运行结果与代码的执行顺序或调用顺序是无关的,需要注意的是执行start()方法的顺序不代表线程启动的顺序。

3.2 实现 Runnable

public class Run {
    
    
    public static void main(String[] args) {
    
    
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        System.out.println("运行结束!");
    }
}
class MyRunnable implements Runnable {
    
    
    @Override
    public void run() {
    
    
        System.out.println("running...");
    }
}

Thread继承了Runnable接口:

public class Thread extends Object implements Runnable

可以看出Thread(Runnable target)不仅可以传入 Runnable接口的对象,还可以传入Thread类的对象。

4守护线程和用户线程有什么区别呢?

用户 (User) 线程:运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
守护 (Daemon) 线程:运行在后台,为其他前台线程服务。也可以说守护线程是 JVM 中非守护线程的 “佣人”。一旦所有用户线程都结束运行,守护线程会随 JVM 一起结束工作

5形成死锁的四个必要条件是什么

1 互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
2 请求与保持条件一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
3 不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
4 循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞

6什么是死锁?

死锁是指两个或两个以上的线程在执行过程中由于竞争资源或者由于彼此通信而造成的一种阻塞的现象若无外力作用,它们都将无法推进下去

7如何避免线程死锁

破坏互斥条件:这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。

破坏请求与保持条件:一次性申请所有的资源

破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

1 非共享数据

public class Run {
    
    
    public static void main(String[] args) {
    
    
        MyThread a = new MyThread("A");
        MyThread b = new MyThread("B");
        MyThread c = new MyThread("C");
        a.start();
        b.start();
        c.start();
    }
}
class MyThread extends Thread {
    
    
    private int count = 5;
    public MyThread(String name) {
    
    
        super(name);
        this.setName(name);
    }
    @Override
    public void run() {
    
    
        super.run();
        while (count > 0) {
    
    
            count--;
            System.out.println("由" + this.currentThread().getName() + "计算,count=" + count);
        }
    }
}

2 共享数据

public class Run {
    
    
    public static void main(String[] args) {
    
    
        MyThread myThread = new MyThread();
        Thread a = new Thread(myThread, "A");
        Thread b = new Thread(myThread, "B");
        Thread c = new Thread(myThread, "C");
        Thread d = new Thread(myThread, "D");
        Thread e = new Thread(myThread, "E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}
class MyThread extends Thread {
    
    
    private int count = 5;

    @Override
    public void run() {
    
    
        super.run();
        while (count > 0) {
    
    
            count--;
            System.out.println("由" + this.currentThread().getName() + "计算,count=" + count);
        }

    }
}

3 System.out.println()的线性安全问题

System.out.println源码:

    public void println(String x) {
    
    
        synchronized (this) {
    
     //print内部是是加锁的
            print(x);
            newLine();
        }
    }
    public static void main(String[] args) {
    
    
        MyThread run = new MyThread();
        Thread a = new Thread(run);
        Thread b = new Thread(run);
        Thread c = new Thread(run);
        Thread d = new Thread(run);
        Thread e = new Thread(run);
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}
class MyThread extends Thread {
    
    
    private int i = 5;
    @Override
    public void run() {
    
    
        System.out.println("i=" + (i--) + "threadName=" + Thread.currentThread().getName());
    }
}
i=4threadName=Thread-3
i=3threadName=Thread-2
i=1threadName=Thread-5
i=5threadName=Thread-1
i=2threadName=Thread-4

注意:print()方法内部是加锁、同步的,但是 i = i - 1的操作是在print()之外。

4 currentThread()

注意:构造方法是在对象的创建过程中调用的,即在MyThread myThread = new MyThread()阶段调用,是由主线程main调用的。

5 isAlive()

public final boolean isAlive() 测试这个线程是否活着。 结果 :true如果这个线程还活着; 否则false。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6 sleep()

当前正在执行的线程指的是this.currentThread()返回的线程

7 getId()

public long getId()返回此线程的标识符

8 停止线程

public void interrupt()中断这个线程。

2.8.1 异常法

在这里插入图片描述

注意:thread.interrupt(),但是没有中断thread线程的run(),还是运行:System.out.println(“If this code is for and continues to run, the thread does not stop.”)

在这里插入图片描述
注意:通过throw new InterruptedException() 的方法就实现停止线程。

2.8.2 stop停止线程

已经弃用的方法,stop释放锁会造成数据不一致。

2.8.3 判断线程是否停止状态

1 interrupted
源码

  public static boolean interrupted() {
    
    
        return currentThread().isInterrupted(true);
    }
interrupted()是静态方法,内部实现是调用的当前线程的isInterrupted(),
并且会重置当前线程的中断状态,**与t.interrupted()的t是无关的。**

在这里插入图片描述

是否中断1= false
是否中断2= false
end!

2 isInterrupted
源码

    public boolean isInterrupted() {
    
    
        return isInterrupted(false);
    }
isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的,不会重置当前线程的中断状态。**与t.isInterrupted()的t是有关的。**

public class Run {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(600);
            thread.interrupt();
            System.out.println("是否中断1? = " + thread.isInterrupted());
            System.out.println("是否中断2? = " + thread.isInterrupted());

        }catch (InterruptedException e) {
    
    
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

class MyThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        for (int i= 0 ; i < 50000 ; i++) {
    
    
            System.out.println("i=" + i);
        }
    }
}
i=45576
i=45577
是否中断1= true
是否中断2= true
end!
i=45578
i=45579
i=45580

特别注意:中断与停止的问题

9 暂停线程

suspend():暂停线程
resume():恢复线程
在这里插入图片描述
suspend与resume的缺点——独占

在这里插入图片描述

10 yield()

放弃当前的cpu资源,将它让给其他任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获得cpu时间片。

11 线程的优先级

public final void setPriority(int newPriority)更改此线程的优先级。 
参数 
newPriority - 设置此线程的优先级
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
异常 
IllegalArgumentException - 如果优先级不在 MIN_PRIORITY到 MAX_PRIORITY 。 
SecurityException - 如果当前线程不能修改此线程。 

注意:CPU尽量是把执行资源让给优先级较高的线程,优先级具有随机性。

12 守护线程

守护线程是一个特殊的线程,他的特性有“陪伴”的含义,当进程中不存在非守护线程,则守护线程自动销毁。

猜你喜欢

转载自blog.csdn.net/zs18753479279/article/details/115139987