zoukankan      html  css  js  c++  java
  • java线程

    一、

    1.说起线程,首先,java的线程有三个特性:原子性,可见性和有序性。

     原子性:即,一个操作或多个操作,要么全部执行并且执行不会被打断,要么都不执行。例如,从账户A转账到账户B,需要两个操作,从账户A扣钱,账户B加钱,这两个操作都必须具备原子性才能不出现问题。例如i+1=i,需要读取i的值,+1,再写入i,其不具备原子性,一出现问题,所以就需要synchronzed和lock来解决这些问题。

     可见性:当多个线程访问同一个变量时,线程A对这个变量进行修改,其他线程可以立即看到修改的值。

     有序性:程序执行的顺序按照代码的先后顺序执行。例如账号A取钱和账户B存钱,必须是先取再存或者先存再取。

    2.synchronized

     使用synchronzed修饰的方法或者代码块可以看成是一个原子操作。  使用synchronized在某些情况下会造成死锁,例如一个共享变量被两个方法使用,一个有sync修饰,一个没有用于异步回调,就会造成死锁,在sync修饰的方法没有执行完毕释放共享变量时,用于异步回调的方法就会等待。

     每个锁对象(JLS中叫monitor)都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个线程被唤醒(notify)后,才会进入到就绪队列,等待CPU的调度,反之,当一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒,这个涉及到线程间的通信,下一篇博文会说明。看我们的例子,当第一个线程执行输出方法时,获得同步锁,执行输出方法,恰好此时第二个线程也要执行输出方法,但发现同步锁没有被释放,第二个线程就会进入就绪队列,等待锁被释放。

     一个线程执行互斥(同步)代码过程如下:

            1. 获得同步锁;

            2. 清空工作内存;

            3. 从主内存拷贝对象副本到工作内存;

            4. 执行代码(计算或者输出等);

            5. 刷新主内存数据;

            6. 释放同步锁。

       所以,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。

    2.Thread和Runnable的区别

      java有两种方式来实现多线程,一个是继承Thread类,重写Thread的run()方法,将线程运行的逻辑放入其中;一个是实现Runnable接口,实例化Thread类。

      以铁路售票为例,如图:

      

      第一种就是继承Thread类,第二张是实现Runnable接口。其区别显而易见,一个是多个线程分别完成自己的任务,一个是多个线程完成共同的任务。

      我们一般在应用中多使用Runnable接口,其好处如下:

        1.适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。

        2.可以避免由于Java的单继承特性带来的局限

        3.有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。

    3.Callable和Runnable的区别

      Runnable和Callable的区别是,

      (1)Callable规定的方法是call(),Runnable规定的方法是run()。

      (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得

      (3)call方法可以抛出异常,run方法不可以

      (4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

    1、通过实现Runnable接口来创建Thread线程:

      步骤1:创建实现Runnable接口的类:

      class SomeRunnable implements Runnable

      {

      public void run()

      {

      //do something here

      }

      }

      步骤2:创建一个类对象:

      Runnable oneRunnable = new SomeRunnable();

      步骤3:由Runnable创建一个Thread对象:

      Thread oneThread = new Thread(oneRunnable);

      步骤4:启动线程:

      oneThread.start();

      至此,一个线程就创建完成了。

      注释:线程的执行流程很简单,当执行代码oneThread.start();时,就会执行oneRunnable对象中的void run();方法,

      该方法执行完成后,线程就消亡了。

      2、与方法1类似,通过实现Callable接口来创建Thread线程:其中,Callable接口(也只有一个方法)定义如下:

      public interface Callable<V>

      {

      V call() throws Exception;

      }

      步骤1:创建实现Callable接口的类SomeCallable<Integer>(略);

      步骤2:创建一个类对象:

      Callable<Integer> oneCallable = new SomeCallable<Integer>();

      步骤3:由Callable<Integer>创建一个FutureTask<Integer>对象:

      FutureTask<Integer> oneTask = new FutureTask<Integer>(oneCallable);

      注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。

      步骤4:由FutureTask<Integer>创建一个Thread对象:

      Thread oneThread = new Thread(oneTask);

      步骤5:启动线程:

      oneThread.start();

      至此,一个线程就创建完成了。

      3、通过继承Thread类来创建一个线程:

      步骤1:定义一个继承Thread类的子类:

      class SomeThead extends Thraad

      {

      public void run()

      {

      //do something here

      }

      }

      步骤2:构造子类的一个对象:

      SomeThread oneThread = new SomeThread();

      步骤3:启动线程:

      oneThread.start();

      至此,一个线程就创建完成了。

  • 相关阅读:
    Spark入门实战系列--1.Spark及其生态圈简介
    理解JavaScript的原型链
    为什么overflow:hidden能达到清除浮动的目的?
    JavaScript中为什么需要!!?
    CSS品控与流程
    CSS高级特效(下)
    CSS高级特效(上)
    CSS变化、过渡与动画
    CSS表单与数据表(下)
    CSS表单与数据表(上)
  • 原文地址:https://www.cnblogs.com/ORC-NoEnd/p/7094486.html
Copyright © 2011-2022 走看看