首页 技术 正文
技术 2022年11月8日
0 收藏 630 点赞 1,734 浏览 9610 个字

线程创建与终止

线程创建

Thread类与Runnable接口的关系

public interface Runnable {
  public abstract void run();
}public class Thread implements Runnable {
/* What will be run. */
  private Runnable target;
  ......
  /**
   * Causes this thread to begin execution; the Java Virtual Machine
  * calls the <code>run</code> method of this thread.
  */
  public synchronized void start() {......}  ......
@Override
public void run() {
if (target != null) {
target.run();
}
  }
  ......
}

Thread类与Runnable接口都位于java.lang包中。从上面我们可以看出,Runnable接口中只定义了run()方法,Thread类实现了Runnable 接口并重写了run()方法。当调用Thread 类的start()方法时,实际上Java虚拟机就去调用Thread 类的run()方法,而Thread 类的run()方法中最终调用的是Runnable类型对象的run()方法。

继承Thread并重写run方法

public class ThreadTest1 extends Thread {
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 1:" + Thread.currentThread().getName());
}
} public static void main(String[] args) {
ThreadTest1 thread = new ThreadTest1 ();
thread.start();
}//main end
}

可以写成内部类的形式,new Thread(){@Override run(…)}.start();

实现Runnable接口并重写run方法

public class ThreadTest2  implements Runnable {
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 3:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ThreadTest2 thread3 = new ThreadTest2();
Thread thread = new Thread(thread3);
thread.start();
}//main end
}

可以写成内部类的形式,new Thread(new Runnable(){@Override run(…)}).start();

线程终止

当调用Thread类的start()方法时,将会创建一个线程,这时刚创建的线程处于就绪状态(可运行状态),并没有运行,处于就绪状态的线程就可以等JVM调度。当JVM调度该线程时,该线程进入运行状态,即执行Thread类的run()方法中的内容。run()方法执行完,线程结束,线程进入死亡状态。这是线程自然终止的过程,我们也可以通过Thread类提供的一些方法来终止线程。

interrupt()\isInterrupted()\interrupted()方法介绍

stop()方法没有做任何的清除操作就粗暴终止线程,释放该线程所持有的对象锁(下文将介绍),受该对象锁保护的其它对象对其他线程可见,因此具有不安全性。

suspend()方法会使目标线程会停下来,但仍然持有在这之前获得的对象锁,对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。

终上所述,不建议使用stop()方法和suspend()方法来终止线程,通常我们通过interrupt()方法来终止处于阻塞状态和运行状态的线程。

需要注意的是,interrupt()方法不会中断一个正在运行的线程,仅仅是将线程的中断标记设为true,当调用了阻塞方法之后,线程会不断监听中断标志,如果为true,则产生一个InterruptedException异常,将InterruptedException放在catch中就能终止线程。

isInterrupted()方法可以返回中断标记,常用循环判断条件。

interrupted()方法测试当前线程是否已经中断,线程的中断标志由该方法清除。interrupted()除了返回中断标记之外,它还会清除中断标记。

interrupt()用法

看下面例子

public class ThreadInterruptedTest extends Thread {
@Override
public void run() {
try {
int i = 0;
while(!isInterrupted()) {
i ++ ;
Thread.sleep(1000);
System.out.println(this.getName() + " is looping,i=" + i);
}
} catch (InterruptedException e) {
System.out.println(this.getName() +
" catch InterruptedException,state:" + this.getState());
e.printStackTrace();
}
} public static void main(String[] args) throws Exception { ThreadInterruptedTest thread = new ThreadInterruptedTest();
System.out.println(thread.getName()
+ " state:" + thread.getState()); thread.start();
System.out.println(thread.getName()
+ " state:" + thread.getState()); Thread.sleep(5000); System.out.println("flag: " + thread.isInterrupted()); //发出中断指令
thread.interrupt(); System.out.println("flag: " + thread.isInterrupted()); System.out.println(thread.getName()
+ " state:" + thread.getState()); System.out.println(thread.interrupted());
}
}

运行结果

Thread-0 state:NEW
Thread-0 state:RUNNABLE
Thread-0 is looping,i=1
Thread-0 is looping,i=2
Thread-0 is looping,i=3
Thread-0 is looping,i=4
flag: false
flag: true
Thread-0 state:TIMED_WAITING
Thread-0 catch InterruptedException,state:RUNNABLE
false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.itpsc.thread.ThreadInterruptedTest.run(ThreadInterruptedTest.java:11)

从运行结果可以看出,调用interrupt() 发出中断指令前,中断标志位false,发出中断指令后中断标志位为true,而调用interrupted()方法后则中断标志被清除。从发出的异常来看,是在一个sleep interrupted,且发出异常后线程被唤醒,以便线程能从异常中正常退出。

线程运行状态图

线程从创建到终止可能会经历各种状态。在Java基础加强之集合篇(模块记忆、精要分析))。而ThreadLocalMap中的Entry 继承了WeakReference,弱引用是不能保证不被垃圾回收器回收的,这就是前文提到的在线程消失之后,线程局部变量的所有副本都会被垃圾回收。此外,Entry 中使用ThreadLocal作为key,线程局部变量作为value。如果threadLocals不为空,则设值否者调用createMap方法创建threadLocals。注意设值的时候传的是this而不是当前线程t。

/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap { /**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}

接下来我们看看createMap方法

/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
* @param map the map to store.
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);

createMap方法其实就是为当前线程的threadLocals变量分配空间并存储线程的第一个变量。现在我们已经知道线程是如何初始化并设值自己的局部变量了,下面我们看看取值。

Get

/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}  

先获取当前的线程,然后通过getMap(t)方法获取当前线程存变量的对象threadLocals,如果threadLocals不为空则取值并返回(注意传入的key是this对象而不是当前线程t),否则调用setInitialValue方法初始化。setInitialValue和set方法唯一不同的是调用了initialValue进行初始化,也就是在获取变量之前要初始化。

/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}  

总的来讲,每创建一个线程(Thread对象),该线程即拥有存储线程本地变量的threadLocals对象,threadLocals对象初始为null,当通过ThreadLocal对象调用set/get方法时,就会对线程的threadLocals对象进行初始化,并且以当前ThreadLocal对象为键值,以ThreadLocal要保存的变量为value,存到threadLocals。看下面的例子。

ThreadLocal应用

public class ThreadLocalShareVariable {    public static void main(String[] args) {
//创建3个线程
for(int i=0; i<3;i++) {
//创建线程
new Thread(new Runnable(){
@Override
public void run() {
//线程设置自己的变量
int age = new Random().nextInt(100);
String name = getRandomString(5);
System.out.println("Thread " + Thread.currentThread().getName()
+ " has put data:" + name + " " + age); //存储与当前线程有关的变量
Passenger.getInstance().setName(name);
Passenger.getInstance().setAge(age); //线程访问共享变量
new ModuleA().getData();
new ModuleB().getData();
}
}).start();
}
} static class ModuleA {
public void getData(){
//获取与当前线程有关的变量
String name = Passenger.getInstance().getName();
int data = Passenger.getInstance().getAge();
System.out.println("moduleA get data from "
+ Thread.currentThread().getName() + ":" + name + " "+ data);
}
} static class ModuleB {
public void getData(){
//获取与当前线程有关的变量
String name = Passenger.getInstance().getName();
int data = Passenger.getInstance().getAge();
System.out.println("moduleB get data from "
+ Thread.currentThread().getName() + ":" + name + " "+ data);
}
} /**
* 随机生成字符串
* @param length
* @return
*/
public static String getRandomString(int length){
final String str = "abcdefghijklmnopqrstuvwxyz";
StringBuffer sb = new StringBuffer();
int len = str.length();
for (int i = 0; i < length; i++) {
sb.append(str.charAt(
(int) Math.round(Math.random() * (len-1))));
}
return sb.toString();
}}class Passenger {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Passenger(){} //ThreadLocal存储线程变量
public static ThreadLocal<Passenger> thsd = new ThreadLocal<Passenger>(); public static Passenger getInstance() {
//获取当前线程范围内的共享变量实例
Passenger passenger = thsd.get();
//懒汉模式创建实例
if(passenger == null) {
passenger = new Passenger();
thsd.set(passenger);
}
return passenger;
}}

运行结果

Thread Thread-1 has put data:vwozg 33
Thread Thread-2 has put data:hubdn 30
Thread Thread-0 has put data:mkwrt 35
moduleA get data from Thread-2:hubdn 30
moduleA get data from Thread-0:mkwrt 35
moduleA get data from Thread-1:vwozg 33
moduleB get data from Thread-1:vwozg 33
moduleB get data from Thread-0:mkwrt 35
moduleB get data from Thread-2:hubdn 30

创建3个线程,每个线程要保存一个Passenger 对象,并且通过ModuleA 、ModuleB来访问每个线程对应保存的Passenger 对象。

多线程之间共享变量

上面我们讨论的是多线程之间如何访问自己的变量。那么多线程之间共享变量时如何的呢,看下的例子,线程1对共享变量进行减一操作,线程2对共享变量进行加2操作。

public class MutilThreadShareVariable {
static volatile int count = 100;
public static void main(String[] args) throws Exception{
final ShareDataDec sdDec = new ShareDataDec();
final ShareDataInc sdInc = new ShareDataInc();
//线程1
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++) {
sdDec.dec();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//线程2
new Thread(new Runnable(){
@Override
public void run() {
for(int i=0;i<5;i++) {
sdInc.inc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();;
} static class ShareDataDec {
public synchronized void dec() {
count --;
System.out.println("Thread " + Thread.currentThread().getName()
+ " dec 1 from count,count remain " + count);
}
} static class ShareDataInc {
public synchronized void inc() {
count = count + 2;
System.out.println("Thread " + Thread.currentThread().getName()
+ " inc 2 from count,count remain " + count);
}
}
}

运行结果

Thread Thread-0 dec 1 from count,count remain 99
Thread Thread-1 inc 2 from count,count remain 101
Thread Thread-0 dec 1 from count,count remain 100
Thread Thread-1 inc 2 from count,count remain 102
Thread Thread-0 dec 1 from count,count remain 101
Thread Thread-1 inc 2 from count,count remain 103
Thread Thread-0 dec 1 from count,count remain 102
Thread Thread-1 inc 2 from count,count remain 104
Thread Thread-0 dec 1 from count,count remain 103
Thread Thread-1 inc 2 from count,count remain 105

线程共享变量,只要对要对共享变量进行修改的代码进行同步即可。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,085
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,560
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,409
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,182
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,819
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,902