一。Java多线程技能:
1. 一个进程正在运行时至少会有一个线程正在运行,main方法的线程是由JVM创建的,通过jps能检测到。
2. 使用多线程技术时,代码的运行结果与代码的执行顺序或调用顺序是无关的。
3. new Thread(Runnable target)不只可以传入Runnable接口的对象,还可以传入另一个Thread类的对象,完全可以将一个Thread对象中的run()
方法交由其他的线程进行调用。
4. 在main方法中创建线程并调用其start(),线程的构造方法是在main线程里执行的,run方法在对应的线程里执行,但是如果直接调用线程的run方法,则其也是在main线程里执行的。
5. isAlive()方法:判断当前的线程是否处于活动状态,注意:将线程对象以构造参数的方式传递给Thread对象进行start()时,this指的时线程对象, Thread.currentThread()或this.Thread.currentThread()指的时Thread对象,Thread对象时存活的(isAlive())
6. Thread.sleep()方法:让当前线程休眠,如果在main方法里通过调用线程类的run方法执行线程,则虽然Thread.sleep()在run方法里,休眠的仍然是main线程(可通过打印this.currentThread().getName()印证)
7. getId()方法的唯一作用是取得线程的唯一标识。
二。线程的中断:
1.通过interrupt()中断:
1. thread.interrupt(): 中断thread对象所代表的线程。
2. Thread.currentThread().interrupt(): 中断当前线程。
3. this.interrupted()/Thread.interrupted(): 测试当前线程是否已经中断,如果已被设置中断标记,调用过一次后会清除此标记,导致再次调用没有。
4. this.isInterrupted()/Thread.isInterrupted():测试线程是否已经中断,调用后不清除中断标记。
2.通过异常法中断:
【java多线程编程核心技术1-Thread基础知识】1.通过在线程的run方法中的for循环里判断线程是否停止状态(this.interrupted())。
public class MyThread1 extends Thread {
@Override
public void run() {
super.run();
for (int i = 0;
i < 500000;
i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("如果我会for循环又会继续运行......");
}
}
2.为了避免中断后for下面的语句仍然运行,需要中断时抛出中断异常并处理中断。
public class MyThread1 extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0;
i < 500000;
i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("如果我会for循环又会继续运行......");
} catch (InterruptedException e) {
System.out.println("进MyThread.java类run方法中的catch了!");
e.printStackTrace();
}
}
}
3.在睡眠中停止线程:
1.先sleep再interrupt:如果在sleep状态下停止某一线程,会进入catch语句并且清除停止状态值,使之变成false,sleep之后的程序不会执行。
2.先interrupt再sleep:如果在sleep之前还有程序如for循环,调用中断后并不会影响for循环的执行,等执行到sleep时才进入catch语句并且清除停止状态值,使之变成false。
public class MyThread1 extends Thread {
@Override
public void run() {
super.run();
try {
// 调用此线程的interrupt方法是,for循环会正常执行完毕
for (int i = 0;
i < 500000;
i++) {
System.out.println("i=" + (i + 1));
}
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到了sleep!进入catch!");
e.printStackTrace();
}
}
}
4.暴力停止线程:
1.thread.stop():是一个depreated方法,线程被暴力停止(stop)运行后图标呈灰色,调用stop方法时会抛出ThreadDeath异常,通常不需要显示的捕捉
2.缺点:1.强制停止线程可能使一些请理性的工作得不到完成。
2.对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。
5.使用return停止线程:
1.将方法interrupt()与return结合使用也能实现停止线程的效果。
public class MyThread1 extends Thread {
@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("timer=" + System.currentTimeMillis());
}
}
}
三。暂停线程:
1. 使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。
缺点1-独占:
1.极易造成公共对象的独占,使得其他线程无法访问公共同步对象。
如一个线程进入了一个公共对象的同步方法内suspend()了后,由于始终占据锁不放,所以其他线程无法进入这个公共对象的此同步方法内。
public class SynchronizedObject {
synchronized public void printString() {
System.out.println("begin");
if (Thread.currentThread().getName().equals("a")) {
System.out.println("a线程永远 suspend了!");
Thread.currentThread().suspend();
}
System.out.println("end");
}
}
2.另一种独占情况,当把run方法中的打印语句放开后,main方法里的打印语句不会执行,因为打印方法prinln内部实现是同步的代码块
分为打印和换行两步,当程序运行到代码块内部suspend时,打印语句的锁不会释放,导致main方法里的打印语句不能执行。
public class MyThread extends Thread {
private long i = 0;
@Override
public void run() {
while (true) {
i++;
// System.out.println(i);
}
}
} public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.suspend();
System.out.println("main end!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
缺点2-不同步:
1.容易出现因为线程的暂停而导致数据不同步的情况。
即如果设置共享对象多个值中间suspend了线程,导致此对象的值只被设置了一部分,从而其他线程使用该对象时出现了值不一致的情况。
四.yield方法:
作用:放弃当前的CPU资源,将它让给其他的任务,但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU的时间片。
如下示例:for循环中每次都yield一次,等待再次获取,总的执行时间将呈几何倍增加。
public class MyThread1 extends Thread {
@Override
public void run() {
long beginTime = System.currentTimeMillis();
int count = 0;
for (int i = 0;
i < 50000000;
i++) {
// Thread.yield();
count = count + (i + 1);
}
long endTime = System.currentTimeMillis();
System.out.println("用时:" + (endTime - beginTime) + "毫秒!");
} }
五。线程的优先级:
1. setPriority():用于设置线程的优先级,优先级较高的线程获得的CPU资源较多,设置优先级有助于帮“线程规划器”确定下一次选择哪一个线程来优先执行。
线程优先级分为1~10这10个等级,不在此范围内的优先级会抛出异常,常用1,5,10标识低、中、高优先级。
2.线程优先级的继承特性:A线程启动B线程,则B线程的优先级与A是一样的。
3.线程的优先级具有规则性:
1.优先级高的大部分情况下是先执行完的,尽管它可能启动时间可能晚于其他低优先级的线程。
2.“随机性”,优先级较高的线程不一定每一次都先执行完。
4.结论:不要把线程的优先级与运行结果的顺序作为衡量的标准,优先级较高的线程并不一定每一次都先执行完run()方法中的任务。
六。守护线程:
1. 当进程中没有非守护线程时,则守护线程自动销毁。 Daemon为其他线程的运行提供便利服务,守护线程最经典的应用就是GC垃圾回收器。
2. 通过thread.setDaemon(true)来设置一个线程为守护线程。
public class MyThread1 extends Thread {
private int i = 0;
@Override
public void run() {
try {
while (true) {
i++;
System.out.println("i=" + (i));
Thread.sleep(100);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public class MyTest2 {
public static void main(String[] args) throws InterruptedException {
try {
MyThread1 thread = new MyThread1();
thread.setDaemon(true);
thread.start();
Thread.sleep(5000);
System.out.println("我离开thread对象也不再打印了,也就是停止了!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}