1.1 非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。
1.2 Thread的常用方法
(1)currentThread()方法可返回代码段正在被哪个线程调用的信息 继承Thread类的构造方法是由外部线程调用
(2)isAlive()方法是判断当前的线程是否处于活动状态
区分this和Thread.currentThread()的区别,this指的是CountOperate对象实例,而Thread.currentThread指的是当前线程实例。public class CountOperate extends Thread{
public CountOperate(){
System.out.println("CountOperate---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());//获取线程名
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); //查看线程是否存活
System.out.println("this.getName=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("CountOperate---end ");
System.out.println("Thread.currentThread()==this :"+ (Thread.currentThread() == this));
}
@Override
public void run() {
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()" + Thread.currentThread().isAlive());
System.out.println("Thread.currentThread()==this :"+ (Thread.currentThread() == this));
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("run --- end");
}
}
public class Run {
public static void main(String[] args){
CountOperate c = new CountOperate();
c.start();
Thread t1 = new Thread(c);
System.out.println("main begin t1 isAlive=" + t1.isAlive());
t1.setName("A");
t1.start();
System.out.println("main end t1 isAlive=" + t1.isAlive());
}
}
c.start的运行结果如下:
可以看到this指向的就是new CountOperate创建的这个线程实例,Thread.currentThread()==this :true
t1.start的运行结果如下:
this指向的还是new CountOperator创建的这个线程实例,而不是 new Thread(thread)创建的实例t1。
因此this和Thread.currentThread指向的不是同一个线程实例,通过源码可以看到new Thread(thread)会将thread应用的对象绑定到一个private的target对象上,在t1被执行的时候,会调用target.run()方法,也就是直接调用thread对象的run方法。
this.getName()实际上返回的是target.getName(),而Thread.currentThread().getName()实际上是t1.getName()。
(3)sleep()方法 在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行),这个正在执行的线程指的是this.currentThread()返回的线程。
(4)getId()方法 取得线程的唯一标识
1.7停止线程
大多数停止一个线程的操作使用Thread.interrupt()方法,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止操作。
Thread.stop()方法可以停止一个线程,但是是不安全的,现已被弃用作废。
1.7.1判断线程是否是停止状态
(1)this.interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志置清楚为false的功能。
(2)this.isInterrupted(): 测试线程Thread对象是否已经是中断状态,但不清楚状态标志。
1.7.2能停止的线程--异常法
主线程调用thread.interrupt()给线程打终止标记,线程中通过this.interrupted()方法判断线程是否标记中止,如果标记中止,则通过抛出异常截获的方式来停止线程。
package p1;
public class MyThread1 extends Thread {
public void run() {
try {
for(int i=0;i<100000;i++) {
if(this.interrupted()) {
throw new InterruptedException();
}
System.out.println("i="+i);
}
}catch(InterruptedException e) {
System.out.println("进入catch方法中");
}
}
}
package p1;
public class Run1 {
public static void main(String[] args) {
MyThread1 myThread1=new MyThread1();
myThread1.start();
for(int i=0;i<80000;i++) {}
myThread1.interrupt();
}
}
1.7.4 在沉睡中停止
如果在sleep方法状态下停止某一线程,会进入cacth语句,并且清楚停止状态值,使之变为false。
如果反过来,先停止进程,再进入到sleep方法,sleep方法前的代码都会被执行,然后在sleep的时候抛出异常。
1.7.4 暴力停止
使用stop()方法来停止线程,但是stop()方法已经被作废了,因为如果让强制让线程停止则有可能使一些清理性工作得不到完成,另外一个情况是对锁定的对象进行了解锁,导致数据得不到同步的处理,出现数据不一致的问题。
1.8 暂停线程
1.8.1 suspend与resume方法的使用
暂停线程意味着线程还可以恢复运行,可以使用suspend方法()暂停线程,使用resume()方法恢复线程的执行。在使用suspend与resume方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步方法。
如下代码
package p1;
public class Run2 {
public static void main(String[] args) {
try {
MyThread2 myThread2=new MyThread2();
myThread2.start();
Thread.sleep(1000);
myThread2.suspend();
System.out.println("main end");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class MyThread2 extends Thread{
private long i=0;
public void run() {
while(true) {
i++;
System.out.println(i);
}
}
}
运行程序,控制台将不打印main end,出现这样的情况原因是,当程序运行到println()方法内部时,同步锁未被释放,导致当前PrintStream对象中的println()方法一直呈“暂停”状态,并且“锁未释放”,而main()方法中的输出代码不能打印。
1.9 yield方法
yield()方法的作用是放弃当前资源,将它让给其他的任务去占用cpu执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获得cpu时间片。
public class Run3 {
public static void main(String[] args) {
MyThread3 myThread3=new MyThread3();
myThread3.start();
}
}
public class MyThread3 extends Thread{
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));
}
}
当不使用Thread.yield()方法,程序运行时间为20,当使用Thread.yield()方法时,程序运行时间为11075,程序放弃当前cpu资源,让给其他任务执行,导致时间增长。
1.10 线程的优先级
在操作系统中,线程可以划分优先级,优先级高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。在Java中,线程的优先级分为1~10这10个等级,如果小于1或者大于10,则JDK抛出异常throw new IllegalArgumentException()。
线程的优先级具有继承性,比如A线程启动了B线程,则B线程的优先级与A是一样的。
线程的优先级具有规则性,高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完才能执行其他线程,仍然是交替着执行,线程的优先级与代码执行顺序无关,CPU尽量将执行资源让给优先级比较高的线程。
优先级具有随机性,优先级较高的线程不一定每一次都先执行完,有可能优先级较低的线程先执行完。
优先级的两个方法: setPriority 与 getPriority
1.11 守护线程
Java线程中有两种线程,一种是用户线程,另外一种就是守护(Daemon)线程。
当进程中不存在非守护线程了,则守护线程自动销毁,典型的守护线程是垃圾回收线程,用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆,只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
package p1;
public class Run4 {
public static void main(String[] args) {
MyThread4 myThread4=new MyThread4();
MyDaemon myDaemon=new MyDaemon();
myDaemon.setDaemon(true);//设置为守护线程
myThread4.start();
myDaemon.start();
}
}
package p1;
public class MyThread4 extends Thread {
public void run() {
for(int i=0;i<5;i++) {
System.out.println("线程第"+i+"次执行");
try {
Thread.sleep(8);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
如上代码所示,非守护线程运行了4次就结束了,守护线程也跟随着一起结束,同生共死。