- 线程的状态
- 线程组
- SimpleDateFormat非线程安全问题
- 线程异常处理和传递
1.线程的状态
线程的状态有:创建、运行、结束、等待、等待锁、等待时间。
创建是创建线程的实例对象,但是还没有调用start方法
运行是线程启动,正常进入运行
结束是线程任务结束
等待是进入等待状态,等待其他线程唤醒
等待锁是线程竞争同步区域的锁,目前还没有竞争到
等待时间是线程等待一定时间后,自动回到运行状态
这些状态有助于对线程的理解,对多个线程之间的交互也有帮助。
2.线程组
一个线程组是一个线程的集合。除此之外,一个线程组可以包含其他线程组。线程组是树形结构,其中每一个线程组都有一个父线程除了初始线程组。一个线程可以获取其本身线程组的信息,但是不可以获取其父线程组的信息和其他线程组的信息。这个线程组的具体使用信息可以通过查询API来使用,算是多线程部分一个非常基础的类。
线程组的示例:
public class MyThread implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
public class ThreadSample {
public static void main(String[] args) {
ThreadGroup tg=new ThreadGroup("tg");
Thread a=new Thread(tg,new MyThread());
Thread b=new Thread(tg,new MyThread());
a.start();
b.start();
System.out.println("thread group info : "+ tg.activeCount() +" , name :"+tg.getParent().getName());
}
}
运行结果:
Thread-0
Thread-1
thread group info : 2 , name :main
3.SimpleDateFormat非线程安全问题
下面是存在线程安全问题的代码:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatRunnable implements Runnable{
private SimpleDateFormat sdf;
private String dateString;
public SimpleDateFormatRunnable(SimpleDateFormat sdf,String dateString) {
this.sdf=sdf;
this.dateString=dateString;
}
@Override
public void run() {
Date dateRef;
try {
dateRef = sdf.parse(dateString);
String newDateString=sdf.format(dateRef).toString();
if(!dateString.equals(newDateString)) {
System.out.println("not equeals, dateString : "+dateString +" , newDateString :"+newDateString);
}
} catch (ParseException e) {
System.out.println(e.getMessage());;
}
}
}
import java.text.SimpleDateFormat;
public class ThreadSample {
public static void main(String[] args) {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String[] dates=new String[] {"2018-01-01","2018-02-02","2018-02-02","2018-03-03","2018-04-04","2018-05-05","2018-06-06","2018-07-07","2018-08-08","2018-09-09","2018-10-10","2018-11-11","2018-12-12"};
for(int i=0;i<dates.length;i++) {
Thread t=new Thread(new SimpleDateFormatRunnable(sdf,dates[i]));
t.start();
}
}
}
测试结果如下所示,选取多次运行之后的某一次:
not equeals, dateString : 2018-02-02 , newDateString :2018-01-01
not equeals, dateString : 2018-06-06 , newDateString :2018-05-05
not equeals, dateString : 2018-03-03 , newDateString :0010-10-10
not equeals, dateString : 2018-07-07 , newDateString :2018-07-09
问题原因分析:mian线程与示例中其他的线程运行次序是混乱的,非main线程之间的运行也是次序混乱的,运行程序之中并没有添加什么同步机制以及保证线程数据在运算过程中的一致性。
修改如下所示:
import java.text.SimpleDateFormat;
public class DateTool {
private static ThreadLocal<SimpleDateFormat> tl=new ThreadLocal<SimpleDateFormat>();
private static final String DATEPATTERN="yyyy-MM-dd";
public static SimpleDateFormat getSimpleDateFormat() {
SimpleDateFormat sdf=null;
sdf=tl.get();
if(null==sdf) {
sdf=new SimpleDateFormat(DATEPATTERN);
tl.set(sdf);
}
return sdf;
}
}
import java.text.ParseException;
import java.util.Date;
public class SimpleDateFormatRunnable implements Runnable{
private String dateString;
public SimpleDateFormatRunnable(String dateString) {
this.dateString=dateString;
}
@Override
public void run() {
Date dateRef;
try {
dateRef = DateTool.getSimpleDateFormat().parse(dateString);
String newDateString=DateTool.getSimpleDateFormat().format(dateRef).toString();
if(!dateString.equals(newDateString)) {
System.out.println("not equeals, dateString : "+dateString +" , newDateString :"+newDateString);
}
} catch (ParseException e) {
System.out.println(e.getMessage());;
}
}
}
public class ThreadSample {
public static void main(String[] args) {
String[] dates=new String[] {"2018-01-01","2018-02-02","2018-02-02","2018-03-03","2018-04-04","2018-05-05","2018-06-06","2018-07-07","2018-08-08","2018-09-09","2018-10-10","2018-11-11","2018-12-12"};
for(int i=0;i<dates.length;i++) {
Thread t=new Thread(new SimpleDateFormatRunnable(dates[i]));
t.start();
}
}
}
解决方案是利用单例模式,使用ThreadLocal变量进行解决线程安全问题
4.线程异常处理和传递
对制定的线程设置默认的异常处理器,方法是setUncaughtExceptionHandler(),具体示例如下所示:
import java.lang.Thread.UncaughtExceptionHandler;
public class ThreadSample {
public static void main(String[] args) {
String[] dates=new String[] {"2018-01-01","2018-02-02","2018-02-02","2018-03-03","2018-04-04","2018-05-05","2018-06-06","2018-07-07","2018-08-08","2018-09-09","2018-10-10","2018-11-11","2018-12-12"};
for(int i=0;i<dates.length;i++) {
Thread t=new Thread(new SimpleDateFormatRunnable(dates[i]));
t.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){
@Override
public void uncaughtException(Thread thread, Throwable e) {
System.out.println("thread name is "+thread.getName());
System.out.println("exception info is "+ e.getMessage());
}
});
t.start();
}
}
}
线程组的一个线程出现异常并不影响组内其他线程的运行,但是考虑到组内抛出异常的线程可能会占有部分资源或者是对环境的数据有部分的不完全修改,所以当组内一个线程抛出异常的时候,要对组内其他线程做中断处理。线程组内的异常处理如下:
public class IThreadGroup extends ThreadGroup{
public IThreadGroup(String name) {
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
super.uncaughtException(t, e);
this.interrupt();
}
}
不再直接使用默认的ThreadGroup,使用自定义的子类实例,补充额外异常处理信息。同时线程组内线程对象的run方法内部不可以保留catch语句,不过可以向上抛出异常。示例代码如下:
public class IThreadGroup extends ThreadGroup{
public IThreadGroup(String name) {
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
super.uncaughtException(t, e);
System.out.println("thread group exception happend at thread : "+t.getName());
this.interrupt();
}
}
import java.text.ParseException;
import java.util.Date;
public class SimpleDateFormatRunnable implements Runnable{
private String dateString;
public SimpleDateFormatRunnable(String dateString) {
this.dateString=dateString;
}
@Override
public void run() {
Date dateRef=null;
System.out.println(dateRef.getTime());
}
}
import java.lang.Thread.UncaughtExceptionHandler;
public class ThreadSample {
public static void main(String[] args) {
IThreadGroup itg=new IThreadGroup("itg");
String[] dates=new String[] {"2018-01-01","2018-02-02","2018-02-02","2018-03-03","2018-04-04","2018-05-05","2018-06-06","2018-07-07","2018-08-08","2018-09-09","2018-10-10","2018-11-11","2018-12-12"};
for(int i=0;i<dates.length;i++) {
Thread t=new Thread(itg,new SimpleDateFormatRunnable(dates[i]));
t.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){
@Override
public void uncaughtException(Thread thread, Throwable e) {
System.out.println("thread name is "+thread.getName());
System.out.println("exception info is "+ e.getMessage());
}
});
t.start();
}
}
}
运行结果揭示重要的信息:
1.当线程设置了异常处理的时候,当线程对象遇到了异常,优先抛出线程对象的异常处理结果
2.当线程没有设置异常处理,线程对象所属的线程组设置了异常处理处理,线程组内的线程遇到异常发生时调用的是线程组的异常处理
3.线程处理的机制是从小到大的传播机制