java多线程编程核心技术7-拾遗增补

来源:互联网 发布:竞彩足球胜平负数据 编辑:程序博客网 时间:2024/05/16 00:54
一。线程的状态
1.线程在不同的运行时期有不同的状态,状态信息就存在于Thread.State枚举类中
  new:尚未启动的线程
  runnable:在java虚拟机执行的线程
  blocked:受阻塞并等待某个监视器锁的线程。
  waiting:无限期的等待另一个线程来执行某一特定操作的线程
  timed_waiting:等待另一个线程来执行取决于指定等待时间的操作的线程。
  terminated:已退出的线程。


2. 验证NEW、RUNNABLE 和 TERMINATED
    public class MyThread extends Thread { 
public MyThread() {
System.out.println("构造方法中的状态:" + Thread.currentThread().getState());

@Override
public void run() {
System.out.println("run方法中的状态:" + Thread.currentThread().getState());
}
}
public class Run { 
public static void main(String[] args) {
try {
MyThread t = new MyThread();
System.out.println("main方法中的状态1:" + t.getState());
Thread.sleep(1000);
t.start();
Thread.sleep(1000);
System.out.println("main方法中的状态2:" + t.getState());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
构造方法中的状态:RUNNABLE
main方法中的状态1:NEW
run方法中的状态:RUNNABLE
main方法中的状态2:TERMINATED

3.验证TIMED_WAITING
    线程状态TIMED_WAITING代表线程执行了Thread.sleep()方法,呈等待状态,等待时间到达,继续向下运行。
    public class MyThread extends Thread { 
@Override
public void run() {
try {
System.out.println("begin sleep");
Thread.sleep(10000);
System.out.println("  end sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run { 
public static void main(String[] args) {
try {
MyThread t = new MyThread();
t.start();
Thread.sleep(1000);
System.out.println("main方法中的状态:" + t.getState());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
begin sleep
    main方法中的状态:TIMED_WAITING
    end sleep

4.验证BLOCKED,此状态出现在某一个线程在等待锁的时候
    public class MyService { 
synchronized static public void serviceMethod() {
try {
System.out.println(Thread.currentThread().getName() + "进入了业务方法!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
@Override
public void run() {
MyService.serviceMethod();

}
public class ThreadB extends Thread { 
@Override
public void run() {
MyService.serviceMethod();
}
}
public class Run { 
public static void main(String[] args) throws InterruptedException {
ThreadA t1 = new ThreadA();
t1.setName("a");
t1.start(); 
ThreadB t2 = new ThreadB();
t2.setName("b");
t2.start();
//让主线程sleep下,否则线程b可能还没启动,呈现runnable状态
Thread.sleep(1000);
System.out.println("main方法中的t2状态:" + t2.getState());
}
}
运行结果:
a进入了业务方法!
main方法中的t2状态:BLOCKED
b进入了业务方法!


5.验证WAITING,线程执行了Object.wait()方法后所处的状态。
    public class Lock {
public static final Byte lock = new Byte("0"); 
}
public class MyThread extends Thread { 
@Override
public void run() {
try {
synchronized (Lock.lock) {
Lock.lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run { 
public static void main(String[] args) throws InterruptedException {
try {
MyThread t = new MyThread();
t.start();
Thread.sleep(1000);
System.out.println("main方法中的t状态:" + t.getState());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

二。线程组
1.可以把线程归属到某一个线程中,线程组中可以有线程对象也可以有线程组。
  作用:可以批量的管理线程或线程组对象,有效地对线程或线程组对象进行组织。
  
2.一级关联:父对象中有子对象,但并不创建子孙对象。
public class ThreadA extends Thread {
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out
.println("ThreadName=" + 
Thread.currentThread().
getName());
Thread.sleep(3000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class ThreadB extends Thread { 
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out
.println("ThreadName=" + 
Thread.currentThread().
getName());
Thread.sleep(3000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
public class Run { 
public static void main(String[] args) throws InterruptedException {
ThreadA aRunnable = new ThreadA();
ThreadB bRunnable = new ThreadB(); 
ThreadGroup group = new ThreadGroup("高洪岩的线程组"); 
//新建线程时指定组
Thread aThread = new Thread(group, aRunnable);
Thread bThread = new Thread(group, bRunnable);
aThread.start();
bThread.start();
System.out.println("活动的线程数为:" + group.activeCount());
System.out.println("线程组的名称为:" + group.getName()); 
}
}
运行结果:
活动的线程数为:2
线程组的名称为:高洪岩的线程组

3.多级关联:父对象中有子对象,子对象中再创建子对象。开发中不太常见,但是JDK是支持的,不要设计的太复杂反而不利于线程对象的管理。
public class Run { 
public static void main(String[] args) throws InterruptedException {
// 在main组中添加一个线程组A,然后在这个A组中添加线程对象Z
// 方法activeGroupCount()和activeCount()的值不是固定的
// 是系统中环境的一个快照
ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
ThreadGroup group = new ThreadGroup(mainGroup, "A");
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("runMethod!");
Thread.sleep(10000);// 线程必须在运行状态才可以受组管理
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}; 
Thread newThread = new Thread(group, runnable);
newThread.setName("Z");
newThread.start();// 线程必须启动然后才归到组A中
// ///
ThreadGroup[] listGroup = new ThreadGroup[Thread.currentThread()
.getThreadGroup().activeGroupCount()];
Thread.currentThread().getThreadGroup().enumerate(listGroup);
System.out.println("main线程中有多少个子线程组:" + listGroup.length + " 名字为:"
+ listGroup[0].getName());
Thread[] listThread = new Thread[listGroup[0].activeCount()];
listGroup[0].enumerate(listThread);
System.out.println(listThread[0].getName());



}
运行结果:
main线程中有多少个子线程组:1 名字为:A
    Z


4.线程组自动归属特性,自动归属就是自动归到当前线程组中。
  下列实例证明,在实例化一个ThreadGroup线程组x时如果不指定所属的线程组,则x线程组自动归到当前线程对象所属的线程组中,
  也就是隐式地在一个线程组中添加了一个子线程组。
    public class Run { 
public static void main(String[] args) throws InterruptedException {
System.out.println("A处线程:"+Thread.currentThread().getName()+" 中有线程组数量:"+Thread.currentThread().getThreadGroup().activeGroupCount());
ThreadGroup group=new ThreadGroup("新的组1");
ThreadGroup group2=new ThreadGroup("新的组2");
System.out.println("A处线程:"+Thread.currentThread().getName()+" 中有线程组数量:"+Thread.currentThread().getThreadGroup().activeGroupCount());
ThreadGroup[] threadGroup=new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
Thread.currentThread().getThreadGroup().enumerate(threadGroup);
for (int i = 0; i < threadGroup.length; i++) {
System.out.println("第一个线程组名称为:"+threadGroup[i].getName());


}
运行结果:
A处线程:main 中有线程组数量:0
A处线程:main 中有线程组数量:2
第一个线程组名称为:新的组1
第一个线程组名称为:新的组2


5.获取根线程组(system就是根线程组,再取就报错了)
    public class Run { 
public static void main(String[] args) throws InterruptedException {
System.out.println("线程:" + Thread.currentThread().getName()
+ " 所在的线程组名为:"
+ Thread.currentThread().getThreadGroup().getName());
System.out
.println("main线程所在的线程组的父线程组的名称是:"
+ Thread.currentThread().getThreadGroup().getParent()
.getName());
System.out.println("main线程所在的线程组的父线程组的父线程组的名称是:"
+ Thread.currentThread().getThreadGroup().getParent()
.getParent().getName());

}
运行结果:
    线程:main 所在的线程组名为:main
main线程所在的线程组的父线程组的名称是:system
Exception in thread "main" java.lang.NullPointerException at com.test.Run.main(Run.java:25)



6. 线程组里加线程组
    public class Run { 
public static void main(String[] args) throws InterruptedException {
System.out.println("线程组名称:"
+ Thread.currentThread().getThreadGroup().getName());
System.out.println("线程组中活动的线程数量:"
+ Thread.currentThread().getThreadGroup().activeCount());
System.out.println("线程组中线程组的数量-加之前:"
+ Thread.currentThread().getThreadGroup().activeGroupCount());
ThreadGroup newGroup = new ThreadGroup(Thread.currentThread()
.getThreadGroup(), "newGroup");
System.out.println("线程组中线程组的数量-加之之后:"
+ Thread.currentThread().getThreadGroup().activeGroupCount());
System.out
.println("父线程组名称:"
+ Thread.currentThread().getThreadGroup().getParent()
.getName());

}
运行结果:
线程组名称:main
线程组中活动的线程数量:1
线程组中线程组的数量-加之前:0
线程组中线程组的数量-加之之后:1
父线程组名称:system



7.组内的线程批量停止。
    public class MyThread extends Thread { 
public MyThread(ThreadGroup group, String name) {
super(group, name);

@Override
public void run() {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ "准备开始死循环了:)");
while (!this.isInterrupted()) {
}
System.out.println("ThreadName=" + Thread.currentThread().getName()
+ "结束了:)");
}
}
public class Run { 
public static void main(String[] args) throws InterruptedException {
try {
ThreadGroup group = new ThreadGroup("我的线程组"); 
for (int i = 0; i < 5; i++) {
MyThread thread = new MyThread(group, "线程" + (i + 1));
thread.start();
}
Thread.sleep(5000);
group.interrupt();
System.out.println("调用了interrupt()方法");
} catch (InterruptedException e) {
System.out.println("停了停了!");
e.printStackTrace();
}

}
运行结果:
ThreadName=线程1准备开始死循环了:)
ThreadName=线程4准备开始死循环了:)
ThreadName=线程3准备开始死循环了:)
ThreadName=线程2准备开始死循环了:)
ThreadName=线程5准备开始死循环了:)
调用了interrupt()方法
ThreadName=线程2结束了:)
ThreadName=线程4结束了:)
ThreadName=线程5结束了:)
ThreadName=线程1结束了:)
ThreadName=线程3结束了:)

8.递归与非递归取得组内对象
    public class Run { 
public static void main(String[] args) throws InterruptedException {
ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
ThreadGroup groupA = new ThreadGroup(mainGroup, "A");
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("runMethod!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
ThreadGroup groupB = new ThreadGroup(groupA, "B");
ThreadGroup groupC = new ThreadGroup(groupB, "C");


// 分配空间,但不一定全部用完
ThreadGroup[] listGroup1 = new ThreadGroup[Thread.currentThread()
.getThreadGroup().activeGroupCount()];
System.out.println(listGroup1.length);
// 递归取得子对象及子孙线程组
Thread.currentThread().getThreadGroup().enumerate(listGroup1, true);
for (int i = 0; i < listGroup1.length; i++) {
if (listGroup1[i] != null) {
System.out.println(listGroup1[i].getName());
}
}
// 非递归取得子对象,也就是只取得A
ThreadGroup[] listGroup2 = new ThreadGroup[Thread.currentThread()
.getThreadGroup().activeGroupCount()];
Thread.currentThread().getThreadGroup().enumerate(listGroup2, false);
for (int i = 0; i < listGroup2.length; i++) {
if (listGroup2[i] != null) {
System.out.println(listGroup2[i].getName());
}
}

}
运行结果:
3
A
B
C
A

9.使线程具有有序性
    public class MyThread extends Thread { 
private Object lock;
private String showChar;
private int showNumPosition; 
private int printCount = 0;// 统计打印了几个字母 
volatile private static int addNumber = 1; 
public MyThread(Object lock, String showChar, int showNumPosition) {
super();
this.lock = lock;
this.showChar = showChar;
this.showNumPosition = showNumPosition;

@Override
public void run() {
try {
synchronized (lock) {
while (true) {
if (addNumber % 3 == showNumPosition) {
System.out.println("ThreadName="
+ Thread.currentThread().getName()
+ " runCount=" + addNumber + " " + showChar);
lock.notifyAll();
addNumber++;
printCount++;
if (printCount == 3) {
break;
}
} else {
lock.wait();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();

}
}
public class Run { 
public static void main(String[] args) throws InterruptedException {
Object lock = new Object(); 
MyThread a = new MyThread(lock, "A", 1);
MyThread b = new MyThread(lock, "B", 2);
MyThread c = new MyThread(lock, "C", 0); 
a.start();
b.start();
c.start();

}
运行结果:
ThreadName=Thread-0 runCount=1 A
ThreadName=Thread-1 runCount=2 B
ThreadName=Thread-2 runCount=3 C
ThreadName=Thread-0 runCount=4 A
ThreadName=Thread-1 runCount=5 B
ThreadName=Thread-2 runCount=6 C
ThreadName=Thread-0 runCount=7 A
ThreadName=Thread-1 runCount=8 B
ThreadName=Thread-2 runCount=9 C



三。SimpleDateFormat非线程安全。


1.类SimpleDateFormat主要负责日期的转换与格式化,但在多线程环境中,使用此类容易造成数据转换及处理的不准确,因为它并不是线程安全的。
    public class MyThread extends Thread { 
private SimpleDateFormat sdf;
private String dateString; 
public MyThread(SimpleDateFormat sdf, String dateString) {
super();
this.sdf = sdf;
this.dateString = dateString;

@Override
public void run() {
try {
Date dateRef = sdf.parse(dateString);
String newDateString = sdf.format(dateRef).toString();
if (!newDateString.equals(dateString)) {
System.out.println("ThreadName=" + this.getName()
+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"
+ newDateString);
}
} catch (ParseException e) {
e.printStackTrace();

}
}
public class Run { 
public static void main(String[] args) throws InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");


String[] dateStringArray = new String[] { "2000-01-01", "2000-01-02",
"2000-01-03", "2000-01-04", "2000-01-05", "2000-01-06",
"2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10" };


MyThread[] threadArray = new MyThread[10];
for (int i = 0; i < 10; i++) {
threadArray[i] = new MyThread(sdf, dateStringArray[i]);
}
for (int i = 0; i < 10; i++) {
threadArray[i].start();
}

}
运行结果:
ThreadName=Thread-1报错了 日期字符串:2000-01-02 转换成的日期为:2000-01-05
ThreadName=Thread-3报错了 日期字符串:2000-01-04 转换成的日期为:0200-01-10
ThreadName=Thread-5报错了 日期字符串:2000-01-06 转换成的日期为:0200-01-10
   
2.解决异常方法1-每个线程创建了一个SimpleDateFormat类的实例
    public class MyThread extends Thread {


private SimpleDateFormat sdf;
private String dateString; 
public MyThread(SimpleDateFormat sdf, String dateString) {
super();
this.sdf = sdf;
this.dateString = dateString;

@Override
public void run() {
try {
Date dateRef = DateTools.parse(sdf.toPattern(), dateString);
String newDateString = DateTools.format("yyyy-MM-dd", dateRef)
.toString();
if (!newDateString.equals(dateString)) {
System.out.println("ThreadName=" + this.getName()
+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"
+ newDateString);
}
} catch (ParseException e) {
e.printStackTrace();

}
}
public class DateTools {
public static Date parse(String formatPattern, String dateString)
throws ParseException {
return new SimpleDateFormat(formatPattern).parse(dateString);

public static String format(String formatPattern, Date date) {
return new SimpleDateFormat(formatPattern).format(date).toString();
}
}
主要是使用DateTools为每个线程创建了一个SimpleDateFormat类的实例。run方法与上面相同。


3.解决异常方法2-使用ThreadLocal类使每个线程有独立的SimpleDateFormat副本
public class MyThread extends Thread { 
private SimpleDateFormat sdf;
private String dateString; 
public MyThread(SimpleDateFormat sdf, String dateString) {
super();
this.sdf = sdf;
this.dateString = dateString;

@Override
public void run() {
try {
Date dateRef = DateTools.getSimpleDateFormat("yyyy-MM-dd").parse(
dateString);
String newDateString = DateTools.getSimpleDateFormat("yyyy-MM-dd")
.format(dateRef).toString();
if (!newDateString.equals(dateString)) {
System.out.println("ThreadName=" + this.getName()
+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"
+ newDateString);
}
} catch (ParseException e) {
e.printStackTrace();

}
}
public class DateTools { 
private static ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>(); 
public static SimpleDateFormat getSimpleDateFormat(String datePattern) {
SimpleDateFormat sdf = null;
sdf = tl.get();
if (sdf == null) {
sdf = new SimpleDateFormat(datePattern);
tl.set(sdf);
}
return sdf;
}
}
每个线程通过DateTools获取的ThreadLocal类型的SimpleDateFormat是属于线程自己的副本,不会相互影响。run方法与上面相同。



四。线程中出现异常的处理


1.使用UncaughtExceptionHandler类捕捉线程对象异常并处理。
    public class Run { 
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
t1.setName("线程t1");
t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程:" + t.getName() + " 出现了异常:");
//e.printStackTrace();
}
});
t1.start();


MyThread t2 = new MyThread();
t2.setName("线程t2");
t2.start();

}
    运行结果:
线程:线程t1 出现了异常:
Exception in thread "线程t2" java.lang.NullPointerException
at com.test.MyThread.run(MyThread.java:12)

    还可通过setDefaultUncaughtExceptionHandler给所有此线程对象设置异常处理。
public class Run { 
public static void main(String[] args) throws InterruptedException {
MyThread
.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程:" + t.getName() + " 出现了异常:");
//e.printStackTrace(); 
}
}); 
MyThread t1 = new MyThread();
t1.setName("线程t1");
t1.start();

MyThread t2 = new MyThread();
t2.setName("线程t2");
t2.start();

}
运行结果:
    线程:线程t1 出现了异常:
线程:线程t2 出现了异常:




五。线程组内处理异常
1. 默认情况下,线程组中的一个线程出现异常并不会影响其他线程的运行。
    public class MyThread extends Thread { 
private String num; 
public MyThread(ThreadGroup group, String name, String num) {
super(group, name);
this.num = num;

@Override
public void run() {
int numInt = Integer.parseInt(num);
while (true) {
System.out.println("死循环中:" + Thread.currentThread().getName());


}   
public class Run { 
public static void main(String[] args) throws InterruptedException {
ThreadGroup group = new ThreadGroup("我的线程组");
MyThread[] myThread = new MyThread[10];
for (int i = 0; i < myThread.length; i++) {
myThread[i] = new MyThread(group, "线程" + (i + 1), "1");
myThread[i].start();
}
MyThread newT = new MyThread(group, "报错线程", "a");
newT.start();

}


2.实现线程组内一个线程出现异常后全部线程都停止运行。
    public class MyThread extends Thread { 
private String num; 
public MyThread(ThreadGroup group, String name, String num) {
super(group, name);
this.num = num;

@Override
public void run() {
int numInt = Integer.parseInt(num);
while (this.isInterrupted() == false) {
System.out.println("死循环中:" + Thread.currentThread().getName());
}

}
public class MyThreadGroup extends ThreadGroup { 
public MyThreadGroup(String name) {
super(name);

@Override
public void uncaughtException(Thread t, Throwable e) {
super.uncaughtException(t, e);
//设置组内的所有线程的中断标记为true
this.interrupt();

}
public class Run { 
public static void main(String[] args) throws InterruptedException {
MyThreadGroup group = new MyThreadGroup("我的线程组");
MyThread[] myThread = new MyThread[10];
for (int i = 0; i < myThread.length; i++) {
myThread[i] = new MyThread(group, "线程" + (i + 1), "1");
myThread[i].start();
}
MyThread newT = new MyThread(group, "报错线程", "a");
newT.start();

}
报错线程运行后其他线程陆续停止了,注意线程对象的run方法内部不要有异常catch语句,否则线程组的uncaughtException方法就捕获
不到线程报错,从而也就不发终止线程组的其他线程。






六。线程异常处理的传递
如果同时存在,优先级顺序为
1.线程对象的异常处理最高MyThread.setUncaughtExceptionHandler 
2.其次线程组的异常处理 (想同时打印线程类的异常信息则需在uncaughtException方法第一行加上super.uncaughtException(t, e);)
3.线程类的异常处理优先级最低 MyThread.setDefaultUncaughtExceptionHandler













0 0
原创粉丝点击