Java多线程理解

来源:互联网 发布:经典算法面试题及答案 编辑:程序博客网 时间:2024/06/14 02:15

两个概念

并行

并行指的是一台机器多个cpu处理,是真正意义上的同时处理。

并发

并发指的是单cpu环境下,为了提高cpu使用效率,将cpu资源按时间片划分,交由不同的线程任务轮流执行,微观上讲,多个线程任务间,并不是同时处理的。

对于线程,举Java中两种实现方式

1.继承Thread类

public class MyThread extends Thread {    public static void main(String [] args){        MyThread thread = new MyThread();        thread.start();    }    @Override    public void run() {        System.out.println("thread running");    }}

调用方法如main()方法中一样,通过线程实例的start方法调用

2.实现Runnable接口

public class MyThread implements Runnable{    public static void main(String [] args){        Thread thread = new Thread(new MyThread());        thread.start();    }    @Override    public void run() {        System.out.println("thread running");    }}

调用方法如main()方法中,新建一个Thread实例,并在构造时传入实现Runnable接口的类实例,然后通过Thread实例调用其start()方法

两种方法都需要重写run()方法,不难推出,Thread类实际上也是一个Runnable接口实现类

线程状态转换

不多说,只谈一点
在调用线程start()方法后,线程并不是立即执行,而是进入可运行状态,与其他线程竞争cpu资源,到cpu执行时才是真正运行
这里写图片描述

线程同步问题

这里简单地谈下我对线程同步的理解,之后有时间再详细深入地总结。

在我看来,多线程同步的目的,归根到底是对线程间共享资源或共享数据的保护。
一个简单的例子,在秒杀活动中,多用户在极短时间内同时发起抢购。
在后台是多个线程并发的,多个不同线程间会涉及一个重要的公共数据库存量n
假设同时有A,B两条线程发起抢购
理想情况:A和B会使库存量-2,得到结果 n=98
然而由于Java线程特性,结果并非一定如此,由于线程调度的随机性,可能会出现下述的这种情况。
数据异常情况:A先到达,读取n的值为100,然后cpu轮到b,b也读取n的值为100,再轮到a,a行n > = n-1,此时n为99,此时cpu运行b,b执行n = n -1=99,此时a和b都被执行,结果应该是98,但是计> > 算结果确是99

或许还会有其他数据异常状况,这会使我们得程序不安全,无法投入使用。

其实到这里的故事大家都耳朵出老茧,但是却没有将它和之后的故事建立联系,这也是导致了我之前一直对线程同步的问题懵的原因


按照我们的理想情况,不同线程对共享数据操作时,必须是由某一线程独占的,其余线程需要等之前的线程执行完成之后才能正确
没错,说到底为了结果的准确,在对线程间的公共数据和资源操作时应该是由某一个线程独占的

这样,线程同步的问题,就转化为如何让某线程独占数据和资源,其实微观上看,在线程独占数据和资源时也恰恰是他独占CPU操作数据和资源的时候。


或者可以说,线程同步的问题就是如何让多条线程在操作共享资源的时候独占 cpu ,从而达到一种暂时有序的线程调度,来保证数据的安全

还是用一段简单的代码案例来做个说明

Java中对象锁可以用于实现线程同步。通过synchronized块持有某个对象的锁。
如之前的秒杀活动,在这里提供超超超超简化用以理解

public class Buyer implements Runnable{    public static void main(String [] args){        Object lock = new Object();        new Thread(new Buyer("buyer-1",lock)).start();        new Thread(new Buyer("buyer-2",lock)).start();        new Thread(new Buyer("buyer-3",lock)).start();        new Thread(new Buyer("buyer-4",lock)).start();        new Thread(new Buyer("buyer-5",lock)).start();        new Thread(new Buyer("buyer-6",lock)).start();        new Thread(new Buyer("buyer-7",lock)).start();        new Thread(new Buyer("buyer-8",lock)).start();    }    private String name;    private static Integer number = 100;    private Object lock;    public Buyer(String name,Object lock) {        this.name = name;        this.lock = lock;    }    @Override    public void run() {        synchronized (lock) {            number--;            System.out.println(name + ":[ now number become " + number + "]");        }    }}

所有抢购线程在执行number–操作时都将持有lock对象的锁,直到运行完synchronized块中的代码,然后,其他的线程才会继续竞争cpu(lock的对象锁),如此往复。

从结果看,无论线程的调度顺序如何,都无法影响结果的准确。

buyer-1:[ now number become 99]buyer-4:[ now number become 98]buyer-5:[ now number become 97]buyer-3:[ now number become 96]buyer-2:[ now number become 95]buyer-7:[ now number become 94]buyer-6:[ now number become 93]buyer-8:[ now number become 92]
buyer-1:[ now number become 99]buyer-4:[ now number become 98]buyer-5:[ now number become 97]buyer-3:[ now number become 96]buyer-2:[ now number become 95]buyer-8:[ now number become 94]buyer-7:[ now number become 93]buyer-6:[ now number become 92]

就先写到这里,再总结一段时间,然后会将线程的同步问题掰开揉碎好好总结一下。

原创粉丝点击