初始线程:
1.新建线程
1)继承Thread类 2)实现Runnable接口
2.线程终止
除非你很清楚你在做什么,否则不要随便使用stop()方法来停止一个线程。因为stop()方法太过于暴力,强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。 通过定义标记变量stopme,当stopme=true时,在run内部返回结果或跳出循环。
3.线程中断
线程中断是一种重要的线程协作机制。严格地讲,线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出啦!至于目标线程接到通知后如何处理,则完全由目标线程自行决定。
Thread类中与线程中断有关的,有三个方法:
注意:Thread.sleep()方法由于中断而抛出异常,此时,它会清除中断标记,如果不加处理,那么在下一次循环开始时,就无法捕获这个中断,故在异常处理中,再次设置中断标记位。
01 public static void main(String[] args) throws InterruptedException {
02 Thread t1=new Thread(){
03 @Override
04 public void run(){
05 while(true){
06 if(Thread.currentThread().isInterrupted()){
07 System.out.println("Interruted!”);
08 break;
09 }
10 try {
11 Thread.sleep(2000);
12 } catch (InterruptedException e) {
13 System.out.println("Interruted When Sleep”);
14 //设置中断状态
15 Thread.currentThread().interrupt();
16 }
17 Thread.yield();
18 }
19 }
20 };
21 t1.start();
22 Thread.sleep(2000);
23 t1.interrupt();
24 }
4.等待(wait)和通知(notify)
Object类提供两个重要的接口,线程等待wait()方法和通知notify()方法,并不是Thread类中的方法。
当线程A中,调用了obj.wait()方法,那么线程A就会停止继续执行,而转为等待状态。等待到何时结束呢?线程A会一直等到其他线程调用了obj.notify()方法为止。这时,obj对象就俨然成为多个线程之间的有效通信手段。
注意:Object.wait()和Thread.sleep()方法都可以让线程等待若干时间。除了wait()可以被唤醒外,另外一个主要区别就是wait()方法会释放目标对象的锁,而Thread.sleep()方法不会释放任何资源。
wait和notify()调用前,也必须获得object的监视器。
5.挂起(suspend)和继续执行(resume)线程
属于Thread的api,不推荐使用,因为suspend()在导致线程暂停的同时,并不会去释放任何锁资源。此时,其他任何线程想要访问被它暂用的锁时,都会被牵连,导致无法正常继续运行。直到对应的线程上进行了resume()操作,被挂起的线程才能继续,从而其他所有阻塞在相关锁上的线程也可以继续执行。但是,如果resume()操作意外地在suspend()前就执行了,那么被挂起的线程可能很难有机会被继续执行。
6.等待线程结束(join)和谦让(yield)
此时,这个线程就需要等待依赖线程执行完毕,才能继续执行。JDK Thread提供了join()操作来实现这个功能,
第一个join()方法表示无限等待,它会一直阻塞当前线程,直到目标线程执行完毕。第二个方法给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会因为“等不及了”,而继续往下执行。
join()的本质是让调用线程 wait()在当前线程对象实例上,,值得注意的一点是:不要在应用程序中,在Thread对象实例上使用类似wait()或者notify()等方法,因为这很有可能会影响系统API的工作,或者被系统API所影响。
另外一个比较有趣的方法,是Thread.yield(),它的定义如下:public static native void yield();
这是一个静态方法,一旦执行,它会使当前线程让出CPU。但要注意,让出CPU并不表示当前线程不执行了。当前线程在让出CPU后,还会进行CPU资源的争夺,但是是否能够再次被分配到,就不一定了。因此,对Thread.yield()的调用就好像是在说:我已经完成一些最重要的工作了,我应该是可以休息一下了,可以给其他线程一些工作机会啦!如果你觉得一个线程不那么重要,或者优先级非常低,而且又害怕它会占用太多的CPU资源,那么可以在适当的时候调用Thread.yield(),给予其它线程机会。
分门别类的管理:线程组
ThreadGroup tg = new ThreadGroup(“tg”);
Thread t1 = new Thread(tg, new ThreadGroupName(), "T1”);
tg.activeCount();
tg.list();
烈建议大家在创建线程和线程组的时候,给它们取一个好听的名字,方便调试
驻守后台:守护线程(Daemon)
守护线程是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。与之相对应的是用户线程,用户线程可以认为是系统的工作线程,它会完成这个程序应该要完成的业务操作。如果用户线程全部结束,这也意味着这个程序实际上无事可做了。守护线程要守护的对象已经不存在了,那么整个应用程序就自然应该结束。因此,当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出。
先干重要的事:线程优先级
在Java中,使用1到10表示线程优先级。一般可以使用Thread内置的三个静态标量表示:
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
数字越大则优先级越高,但有效范围在1到10之间。下面的代码展示了优先级的作用。高优先级的线程倾向于更快地完成。
线程安全的概念与synchronized
线程安全是并行程序的根本和根基。
大家还记得那个多线程读写long型数据的案例吧!这就是一个典型的反例。但在使用volatile关键字后,这种错误的情况有所改善。但是,volatile并不能真正的保证线程安全。它只能确保一个线程修改了数据后,其他线程能够看到这个改动。但当两个线程同时修改某一个数据时,却依然会产生冲突。
synchronized的用法
?指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
?直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
?直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。
除了用于线程同步、确保线程安全外,syn-chronized还可以保证线程间的可见性和有序性。从可见性的角度上讲,synchronized可以完全替代volatile的功能,只是使用上没有那么方便。就有序性而言,由于synchronized限制每次只有一个线程可以访问同步块,因此,无论同步块内的代码如何被乱序执行,只要保证串行语义一致,那么执行结果总是一样的。而其他访问线程,又必须在获得锁后方能进入代码块读取数据,因此,它们看到的最终结果并不取决于代码的执行过程,从而有序性问题自然得到了解决(换言之,被synchronized限制的多个线程是串行执行的)。
在Java中,Integer属于不变对象。也就是对象一旦被创建,就不可能被修改。也就是说,如果你有一个Integer代表1,那么它就永远表示1,你不可能修改Integer的值,使它为2。那如果你需要2怎么办呢?也很简单,新建一个Integer,并让它表示2即可。
使用javap反编译i++ 实际上使用了Integer.valueOf()方法新建了一个新的Integer对象,并将它赋值给变量i。也就是说,i++在真实执行时变成了:i=Integer.valueOf(i.intValue()+1);
进一步查看Integer.valueOf(),我们可以看到:public static Integer valueOf(int i) { assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);}
使用jstack工具显示程序的线程信息,如下所示。其中jps可以显示当前系统中所有的Java进程。而jstack可以打印给定Java进程的内部线程及其堆栈。
C:\Users\geym >jps
14240 HashMapMultiThread
1192 Jps
C:\Users\geym >jstack 14240