Thread

来源:互联网 发布:搜索关键词淘宝 编辑:程序博客网 时间:2024/05/15 23:46

在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。 

所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。只要俩种情况下程序会终止:

  • The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
  • 当 Runtime的exit方法呗调用并且安全管理同意执行这个方法。
  • All threads that are not daemon threads have died, either by returning from the call to therun method or by throwing an exception that propagates beyond therun method. 
  • 当所有的非守护线程都停止执行之后或者在run方法之前抛出异常。
俩种创建线程的方法:

1.就是定义一个继承Thread的类,并且要实现覆盖Thread的run方法,然后初始化这个子类就可以得到一条新的线程。

public class Main extends Thread {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.print("run");}}
2.另外一种就是直接实现Runable接口  并实现run方法。   thread是实现了Runable接口的类。

不过一般采用上一个方法来创建。

--------------------------------------------------------------------------------     class PrimeRun implements Runnable {         public void run() {         }     }


那么什么时候使用这俩种方式来创建线程,这俩种方式应该怎么取舍呢。

我们知道java是单继承的,当类继承java之后就不能继承其他父类了,当实现Runable的时候还可以继承一个父类,

Thread因为继承Runable同时其中也实现了很多其他的方法,比如线程优先级等,相当于是线程延伸了其他很多功能。


synchronized 关键字     

当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

因为vector所有方法都实现了这个关键字   所以vector是线程安全的。


ThreadLocal  出自 http://blog.csdn.net/lufeng20/article/details/24314381

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

  • void set(Object value)设置当前线程的线程局部变量的值。
  • public Object get()该方法返回当前线程所对应的线程局部变量。
  • public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
  • protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
 值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

  ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:

package com.test;public class TestNum {// ①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {public Integer initialValue() {return 0;}};// ②获取下一个序列值public int getNextNum() {seqNum.set(seqNum.get() + 1);return seqNum.get();}public static void main(String[] args) {TestNum sn = new TestNum();// ③ 3个线程共享sn,各自产生序列号TestClient t1 = new TestClient(sn);TestClient t2 = new TestClient(sn);TestClient t3 = new TestClient(sn);t1.start();t2.start();t3.start();}private static class TestClient extends Thread {private TestNum sn;public TestClient(TestNum sn) {this.sn = sn;}public void run() {for (int i = 0; i < 3; i++) {// ④每个线程打出3个序列值System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn[" + sn.getNextNum() + "]");}}}}

 通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个TestNum实例。运行以上代码,在控制台上输出以下的结果:

thread[Thread-0] --> sn[1]
thread[Thread-1] --> sn[1]
thread[Thread-2] --> sn[1]
thread[Thread-1] --> sn[2]
thread[Thread-0] --> sn[2]
thread[Thread-1] --> sn[3]
thread[Thread-2] --> sn[2]
thread[Thread-0] --> sn[3]
thread[Thread-2] --> sn[3]

考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个TestNum实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

线程同步是通过对象的锁机制来实现的,这个时候我们需要知道什么时候来对变量进行操作,需要事先知道这个变量什么时候被某个对象所操作,相对于ThreadLocal而言,难度较大。




线程常用的方法:

1.  Java中sleep和wait的区别

① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。

sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。

② 锁: 最主要sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。

Thread.sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。

③ 使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。

 注意这里notify和notifyAll俩者的区别:

notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。

   synchronized(x){ 
      x.notify() 
     //或者wait() 
}

yield()函数

调用一次  使得线程池中相同优先级别的线程之间切换,而不是这个线程一直执行下去。



0 0
原创粉丝点击