最近在读java多线程编程核心技术,记录学习心得
实现多线程有两种:1.继承Thread类 2.实现Runnable接口
1.1继承Thread类
class Thread implements Runnable (Thread 继承了Runnable接口)
使用多线程技术时,代码运行结果与代码执行顺序或调用顺序是无关的.
1.2实现Runnable接口(如果已经有父类了,不能在继承Thread类,则实现Runnable接口)
把Thread类对象传入构造函数,可以实现将一个Thread对象的run方法交给其他的线程使用
public class CountOperate extends Thread{ public CountOperate(){ System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName="+Thread.currentThread().getName()); System.out.println("this.getName="+this.getName()); System.out.println("CountOperate----end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName="+Thread.currentThread().getName()); System.out.println("this.getName="+this.getName()); System.out.println("run----end"); } }
第一种:
public static void main(String[] args) { CountOperate c=new CountOperate(); Thread t1=new Thread("A"); t1.start(); /*output: CountOperate---begin Thread.currentThread().getName=main this.getName=Thread-0 CountOperate----end */ }
是main线程执行构造函数(ps:这个main和main方法没关系,只不过名字相同)
第二种:
public static void main(String[] args) { CountOperate c=new CountOperate(); Thread t1=new Thread(c,"A"); t1.start(); /* CountOperate---begin Thread.currentThread().getName=main this.getName=Thread-0 CountOperate----end run---begin Thread.currentThread().getName=A this.getName=Thread-0 run----end */ }
main线程执行构造方法,start()会自动调用run方法
2 实例变量与线程安全
这里就涉及到一个线程安全的问题了.
public class MyThread extends Thread{ private int count=5; @Override public void run() { super.run(); count--; System.out.println("由"+Thread.currentThread().getName()+"计算 count="+count); } }
public static void main(String[] args) { MyThread myThread=new MyThread(); Thread a=new Thread(myThread,"a"); Thread b=new Thread(myThread,"b"); Thread c=new Thread(myThread,"c"); Thread d=new Thread(myThread,"d"); Thread e=new Thread(myThread,"e"); a.start(); b.start(); c.start(); d.start(); e.start(); /*output: 由a计算 count=2 由c计算 count=2 由d计算 count=1 由b计算 count=2 由e计算 count=0 */ }
原因:i--,分为3步4, 1)取得原有的i值 2)计算i-1 3)对i进行赋值 同时访问i值,就会产生非线程安全问题
解决方法:在run方法synchronized 关键字
3.Thread currentThread():获取当前线程对象
boolean siAlive():判断当前的进程是否处于活动状态
String getId() 获取线程的唯一标识
4.线程中断
interrupt():停止线程
interrupted():判断当前线程是否已经中断
isInterrupted():测试线程是否已经中断
ps:比较interrupted()方法和isInterrupted()方法?
从源码看:
public static boolean interrupted() { return currentThread().isInterrupted(true); }
举个例子我们能更好理解:
//线程类 public class MyThread extends Thread{ @Override public void run() { super.run(); for (int i = 0; i <50000; i++) { System.out.println("i="+(i+1)); } } }
//测试类 public class Run2 { public static void main(String[] args) { Thread.currentThread().interrupt(); System.out.println("是否停止1?="+Thread.interrupted()); System.out.println("是否停止2?="+Thread.interrupted()); } /*output: 是否停止1?=true 是否停止2?=false */ }
原因:interrupted()方法具有清除状态的功能
ps:native修饰方法
public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); }
private native void interrupt0();
native修饰方法,表示调用的是原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中.
public boolean isInterrupted() { return isInterrupted(false); }
例子:
public static void main(String[] args) { try { MyThread myThread=new MyThread(); myThread.start(); myThread.sleep(1000); myThread.interrupt(); System.out.println("是否停止1?="+myThread.isInterrupted()); System.out.println("是否停止2?="+myThread.isInterrupted()); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end!"); } /*取打印结果: 是否停止1?=false 是否停止2?=false end! */
isInterrupted()测试线程对象是否已经是中断状态,但不清除状态标志.
5.在沉睡中停止:
public class MyThread extends Thread{ @Override public void run() { super.run(); try { System.out.println("run begin"); System.out.println("线程1="+Thread.currentThread().getName()); Thread.sleep(200000); System.out.println("run end"); } catch (InterruptedException e) { System.out.println("在沉睡中停止!进入catch!"); e.printStackTrace(); } } }
public static void main(String[] args) { MyThread thread =new MyThread(); try { thread.start(); System.out.println("线程2="+Thread.currentThread().getName()); Thread.sleep(2000); thread.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end!"); } /* output: 线程2=main run begin 线程1=Thread-0 end! 在沉睡中停止!进入catch! java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at us.codecraft.tinyioc.MyThread.run(MyThread.java:59) */ }
stop() 包里停止线程,但是可能会造成数据不一致的结果,不建议使用
使用return 停止线程
while (true) { if (Thread.interrupted()) { System.out.println("停止了"); return; } System.out.println(System.currentTimeMillis()); }
6.暂停线程
suspend()(暂停线程)和resume()(重启线程)方法
缺点:
(1).独占,如果使用不当会,会造成公共的同步对象的独占,使其他线程无法访问公共同步对象
(2).不同步
7.yield():放弃当前的cpu资源,将他让给其他的任务去占用cpu执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获取cpu时间片
@Override public void run() { long beginTime=System.currentTimeMillis(); int count =0; for (int i = 0; i <5000000 ; i++) { //Thread.yield(); count=count+(i+1); } long endTime=System.currentTimeMillis(); System.out.println("用时:"+(endTime-beginTime)+"毫秒"); }
public static void main(String[] args) { MyThread thread=new MyThread(); thread.start(); /*output:用时:11毫秒*/ //去掉Thread.yield() /*用时:453毫秒*/ }
8.线程的优先级 setPriority()
public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); //线程优先级分为1-10,如果小于1大于10 则抛出异常 if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { //如果设置权限登记大于线程组的最大等级,就将设置权限设为线程租最大等级 if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } }
优先级可以被继承(此继承非彼继承),比如由A线程启动B线程,B线程的优先级与A是一样的
8 优先级具有规则性
public class MyThread1 extends Thread{ @Override public void run() { long beginTime=System.currentTimeMillis(); long addResult=0; for(int j = 0; j <10; j++){ //业务逻辑 for (int i = 0; i <50000; i++) { Random random=new Random(); random.nextInt(); addResult=addResult+i; } } long endTime=System.currentTimeMillis(); System.out.println("***** thread 1 use time ="+(endTime-beginTime)); } }
public class MyThread2 extends Thread{ @Override public void run() { long beginTime=System.currentTimeMillis(); long addResult=0; for (int i = 0; i <10; i++) { //业务逻辑 for (int j = 0; j <50000; j++) { Random random=new Random(); random.nextInt(); addResult=addResult+i; } } long endTime=System.currentTimeMillis(); System.out.println("***** thread 2 use time ="+(endTime-beginTime)); } }
public class Run { public static void main(String[] args) { for (int i = 0; i <5 ; i++) { MyThread1 thread1=new MyThread1(); thread1.setPriority(1); thread1.start(); MyThread2 thread2=new MyThread2(); thread2.setPriority(10); thread2.start(); } } }
结果:
//***** thread 2 use time =130 ***** thread 2 use time =263 ***** thread 2 use time =279 //***** thread 2 use time =156 ***** thread 2 use time =284 ***** thread 2 use time =328 //***** thread 1 use time =248 ***** thread 1 use time =292 ***** thread 2 use time =364 //***** thread 2 use time =355 ***** thread 1 use time =315 ***** thread 1 use time =393 //***** thread 1 use time =370 ***** thread 1 use time =334 ***** thread 1 use time =351 //***** thread 2 use time =387 ***** thread 1 use time =370 ***** thread 2 use time =433 //***** thread 2 use time =396 ***** thread 2 use time =428 ***** thread 1 use time =437 //***** thread 1 use time =397 ***** thread 2 use time =433 ***** thread 2 use time =440 //***** thread 1 use time =431 ***** thread 1 use time =402 ***** thread 1 use time =440 //***** thread 1 use time =455 ***** thread 2 use time =447 ***** thread 1 use time =443
总结:这是3次执行结果,我们发现,优先级较高则优先执行完run()方法中的任务,但这个结果不能说的太肯定,因为优先级还具有随机性(第二列),也就是优先级高的线程不一定每一次都先执行完.
也就是说优先级高的获取cpu资源的概率大,但是有时候概率低的也有可能先执行完.
9守护线程
线程有两种,第一种是用户线程,第二种是守护线程(例如垃圾回收器)
如果没有非守护线程,则守护线程自动销毁
例子:
public class MyThread extends Thread{ private int i=0; @Override public void run() { try { while (true) { i++; System.out.println("i=" + i); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Run { public static void main(String[] args) { try { MyThread thread=new MyThread(); //设置为守护线程 thread.setDaemon(true); thread.start(); Thread.sleep(5000); //主线程沉睡,守护线程离开thread对象不再打印 System.out.println("我离开thread对象就不再打印了"); } catch (InterruptedException e) { e.printStackTrace(); } } /*output: i=1 i=2 i=3 i=4 i=5 我离开thread对象就不再打印了 i=6 */ }