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();

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

 

posted @ 2017-06-29 15:01  正好  阅读(161)  评论(0编辑  收藏  举报