Java Thread

来源:互联网 发布:逆袭网络剧第一集 编辑:程序博客网 时间:2024/06/06 07:15

本文章转自博主https://originals-tz.github.io/

0x00 基本概念

使用多线程的目的在于更好地利用CPU资源

介绍一些概念:
- 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程

  • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

  • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

  • 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果,反过来,线程不安全就意味着线程的调度顺序会影响最终结果

  • 同步:同步就是协同步调,按预定的先后次序进行运行。

0x01 Java的进程状态

新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

0x02 Java的多进程创建

1.继承Thread类,重写run()

第一个Test,两个线程输出数字,每输出一个暂停3ms

code

class Test1 extends Thread{    private String name;    public Test1(String id) {        name = id;    }    public void run(){        for(int i = 0; i < 5; i++) {            System.out.println(name + ":" + i);            try {                sleep(2);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    public static void main(String[] args) {        Test1 a = new Test1("thread 1");        Test1 b = new Test1("thread 2");        a.start();        b.start();    }}

output

thread 2:0thread 1:0thread 1:1thread 2:1thread 2:2thread 1:2thread 1:3thread 2:3thread 1:4thread 2:4

可以发现输出的结果是乱序的

2.实现Runnable接口,重写该接口的run()方法

code

class Test2 implements Runnable{    private int number = 10;    private String name;    public Test2(String n) {        name = n;    }    @Override    public void run() {       while (number > 0) {           number--;           System.out.println(name+":number:"+number);           try {               Thread.sleep(2);           } catch (InterruptedException e) {               e.printStackTrace();           }       }       System.out.println(name + " : zero!");    }    public static void main(String[] args) {        Test2 b = new Test2("thread");        new Thread(b).start();        new Thread(b).start();    }}

output

thread:number:8thread:number:8thread:number:7thread:number:6thread:number:5thread:number:4thread:number:3thread:number:2thread:number:0thread:number:0thread : zero!thread : zero!

2.9.Thread 和 Runnable

使用Runnable可以:
- 避免点继承的局限,一个类可以继承多个接口。
- 适合于资源的共享:用Thread实现Test2,会发现不能对同一个资源进行操作
- Thread也是Runnable接口的子类

在使用Thread的时候只需要new一个实例出来,调用start()方法即可以启动一个线程。

Thread Test = new Thread();Test.start();

在使用Runnable的时候需要先new一个继承Runnable的实例,之后用子类Thread调用。

Test impelements RunnableTest t = new Test();Thread test = new Thread(t);

在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。

3.互斥访问

Test2中,我们对同一个资源进行操作
考虑这种情况

Thread1 Thread2 Number– Number– Print Number Print Number
Thread1.Number--Thread2.Number--Thread1.Print NumberThread2.Print Number

原本Thread1.Print Number输出的是Thread1.Number--之后的数值

但是按照上述顺序,Thread1.Print Number输出的是Thread1.Number--Thread2.Number--之后的数值,导致输出结果错误

因此,对于这种情况(多个线程对一个资源进行操作),必须要上锁

mylock.lock();number--;System.out.println(name+":number:"+number);mylock.unlock();
原创粉丝点击