1. 实例变量与线程安全
自定义线程类中的实例变量针对其他线程有共享与不共享之分,这在多个线程之间交互时是一个很重要的技术点。
不共享数据的情况:每个线程都有各自的实例变量,多个线程交互时不影响各自的实例变量值,不存在线程安全问题。
共享数据的情况:多个线程访问同一个实例变量,多个线程交互时存在“非线程安全问题”。
“非线程安全”是指多个线程对同一个对象中的实例变量进行操作时值被更改、值不同步的情况,进而影响程序的执行流程。
解决“非线程安全”可以使用synchronized关键字。
2. i–或i++与System.out.println()的异常
看下面例子中,i++与println()联合使用时有可能出现的异常情况:
public class MyThread implements Runnable{
private Integer count = 0;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " count = " + (count++));
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
Thread thread4 = new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
打印结果:
Thread-0 count = 0
Thread-2 count = 3
Thread-3 count = 2
Thread-1 count = 1
而如果改变run()方法成这样:
@Override
public void run() {
count++;
System.out.println(Thread.currentThread().getName()
+ " count = " + count);
}
则打印结果:
Thread-0 count = 1
Thread-3 count = 3
Thread-2 count = 2
Thread-1 count = 4
由上面实验可知:虽然println方法内部是同步的,但是i++操作是在进入println方法之前发生的,所以有发生非线程安全的概率。
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
而i++操作是非原子的,在某些JVM中,i++要分为三步:
a. 取得原有i的值;
b. 计算i + 1;
c. 对i进行赋值;
这三步中,如果有多个线程访问,那么一定会出现非线程安全问题。
为了线程安全,还是需要使用同步方法。
3. currentThread()方法
currentThread()方法可以返回代码段正在被哪个线程调用的信息。
public class MyThread5 extends Thread{
public MyThread5() {
System.out.println("construction begin");
System.out.println("Thread.currentThread().getName():"
+ Thread.currentThread().getName());
System.out.println("this.getName():" + this.getName());
System.out.println("contruction 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) {
MyThread5 myThread5 = new MyThread5();
Thread thread = new Thread(myThread5);
thread.setName("A");
thread.start();
}
}
输出结果:
construction begin
Thread.currentThread().getName():main
this.getName():Thread-0
contruction end
run begin
Thread.currentThread().getName():A
this.getName():Thread-0
run end
这里的this指该线程类的实例,改线程类继承Thread类,没有对Name重命名,所以在run()方法中通过this.getName()输出的是默认名称:Thread-0。
注意:如果将main方法中创建线程并启动的过程改成下面这种,那么run()方法中的this就代表当前线程:
public static void main(String[] args) {
MyThread5 myThread5 = new MyThread5();
/*Thread thread = new Thread(myThread5);
thread.setName("A");
thread.start();*/
myThread5.setName("A");
myThread5.start();
}
输出结果:
construction begin
Thread.currentThread().getName():main
this.getName():Thread-0
contruction end
run begin
Thread.currentThread().getName():A
this.getName():A // 区别
run end
4. isAlive()
isAlive()用于判断当前线程是否处于活动状态。活动状态是指线程已经启动且尚未终止。线程处于运行或者就绪状态,就认为线程是“存活”的。
看下面例子通过isAlive()方法区分this和Thread.currentThread()。
public class MyThread extends Thread{
public MyThread() {
System.out.println("构造方法 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("构造方法 end");
}
@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("this.getName():" + this.getName());
System.out.println("this.isAlive():" + this.isAlive());
System.out.println("run end");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.setName("A");
thread.start();
}
}
输出结果:
构造方法 begin
Thread.currentThread().getName():main
Thread.currentThread().isAlive():true
this.getName():Thread-0
this.isAlive():false
构造方法 end
run begin
Thread.currentThread().getName():A
Thread.currentThread().isAlive():true
this.getName():Thread-0
this.isAlive():false
run end
如果main方法改成如下:
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.setName("A");
myThread.start();
}
输出结果:
构造方法 begin
Thread.currentThread().getName():main
Thread.currentThread().isAlive():true
this.getName():Thread-0
this.isAlive():false
构造方法 end
run begin
Thread.currentThread().getName():A
Thread.currentThread().isAlive():true
this.getName():A // 区别
this.isAlive():true // 区别
run end
将线程对象以构造参数的方式传递给Thread对象进行start()启动线程,我们直接启动的线程是thread,而作为构造参数的myThread,赋值给Thread对象的target,之后通过thread的run方法调用target.run()。此时Thread.currentThread是Thread的引用thread,而this则表示myThread。
5. sleep()
sleep()的作用是在指定毫秒时间内,让当前正在执行的线程休眠(暂停执行)。这个正在执行的线程是指this.currentThread()返回的线程(Thread.currentThread())。
6. getId()
getId()方法的作用的取的线程的唯一标识。比如thread-0的0。
7. yield()
yield()方法的作用是放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
上述内容总结自《Java多线程编程核心技术》一书。