0
点赞
收藏
分享

微信扫一扫

javaEE基础 —— 线程的状态和安全

芝婵 2022-04-07 阅读 51
java-ee

 

目录

一、线程的状态

1.new

2.terminated

3.runnable 

 4.timed_waiting

5.blocked 

 6.waiting

 二、线程安全

1.线程安全的概念

2.线程不安全的原因


一、线程的状态

1.new

示例代码:

    public static void main(String[] args) {
        Thread t = new Thread(() -> {

        });
        System.out.println(t.getState());
    }

运行结果:

 

2.terminated

示例代码:

    public static void main(String[] args) {
        Thread t = new Thread(() -> {

        });
        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.getState());
    }

运行结果: 

 

 

 需要注意的是,上述两个状态是Java自身定义的,和操作系统中的PCB状态没有关系。

3.runnable 

示例代码:

    public static void main(String[] args) {
        Thread t = new Thread(() -> {

        });
        t.start();
        System.out.println(t.getState());
    }

 运行结果:

 4.timed_waiting

代码示例:

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t.start();
        Thread.sleep(500);
        System.out.println(t.getState());
    }

 运行结果:

5.blocked 

 6.waiting

小结 

我们可以将这几种状态转移的关系简单地画下面这个图:

 

 

 二、线程安全

1.线程安全的概念

2.线程不安全的原因

说到线程不安全的原因就不得不提原子性操作,我们先来了解一下什么是原子性操作:

 我们来看一段代码:

class count {
    public static int num;

    public void increase() {
        num++;
    }
}
class Main2 {
    public static void main(String[] args) throws InterruptedException {
        count c = new count();
        Thread t1 = new Thread(() -> {
            Object o = new Object();
            for(int i = 0; i < 50000; i++) {
                c.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            Object o = new Object();
            for(int i = 0; i < 50000; i++) {
                c.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count.num);
    }
}

我们定义了两个线程,每一个线程都会使num自增50000次,那么按照代码的逻辑最后num的值应该是100000,那么实际结果真的是这样子吗?

执行结果:

我们会发现 结果跟我们认为的是有很大差距的,那么这种结果的原因是什么呢?我们来分析一下。

除了  非原子性操作带来的线程不安全之外,线程的抢占式执行才是线程不安全的万恶之源。

而在上述代码之中,我们针对自增的是同一个对象,如果我们可以使两个线程去执行不同变量的自增最后将结果加起来,一样可以避免出现线程不安全的问题。

当然,我们也可以使用synchronized关键字,对代码的某些关键操作上锁,将非原子性操作打包成一个原子性的操作

优化后的代码:

class count {
    public static int num;

    synchronized public void increase() {//加锁
        num++;
    }
}
class Main2 {
    public static void main(String[] args) throws InterruptedException {
        count c = new count();
        Thread t1 = new Thread(() -> {
            Object o = new Object();
            for(int i = 0; i < 50000; i++) {
                c.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            Object o = new Object();
            for(int i = 0; i < 50000; i++) {
                c.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count.num);
    }
}

此外,内存可见性也会导致线程安全

代码示例:

    public static int a = 1;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(a == 1) {

            }
            System.out.println("循环结束");
        });
        t.start();
        Scanner scan = new Scanner(System.in);
        System.out.println("输入一个数字:");
        a = scan.nextInt();
        System.out.println("main结束!");
    }

执行结果: 

 

 我们发现,在我们修改了a的值之后,直到主线程结束t线程也没有结束,这就是内存可见性导致的线程不安全 

修改后: 

    volatile public static int a = 1;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(a == 1) {

            }
            System.out.println("循环结束");
        });
        t.start();
        Scanner scan = new Scanner(System.in);
        System.out.println("输入一个数字:");
        a = scan.nextInt();
        System.out.println("main结束!");
    }

 

 此外,volatile关键字还可以防止指令重排序。

以上就是今天的全部内容了,喜欢的话就点个赞吧!

举报

相关推荐

0 条评论