java多线程编程核心技术1-Thread基础知识

一。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(); } } }



















    推荐阅读