说明:参考《Java多线程核心技术》
7、线程的优先级
源码:
public final void setPriority(int newPriority) {
// this thread's thread group.
ThreadGroup g;
// if the current thread is allowed to access this thread
checkAccess();
if (newPriority > 10 || newPriority < 1) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
如果线程A启动线程B,那么线程A和线程B的优先级相同。
默认的线程优先级为5,最大为10,最小为1.
高优先级的线程总是大部分先执行完,但是不代表高优先级的的线程总是先全部执行完。当线程优先级等级差别较大的时候,谁先执行和代码的调用顺序是无关的。优先级高的线程不一定每一次都先执行完,优先级的执行具有随机性。可以通过设置线程的优先级,来尽量让想要先执行的线程先执行。
8、守护线程(Daemon)
Java中的线程有两种,一种为用户线程,另一种为守护线程,守护线程是一种特殊的线程,当进程中不存在非守护线程时,则守护线程自行销毁。最典型的守护线程就是垃圾回收线程。
2、对象及变量的访问
1、实例变量的非线程安全
如果多个线程共同访问一个对象的实例变量,那么可能出现线程不安全的问题。
public class UnSafeThreadA extends Thread{
private PrivateNum num;
public UnSafeThreadA(PrivateNum num) {
super();
this.num = num;
}
@Override
public void run(){
super.run();
num.addI("a");
}
}
public class UnSafeThreadB extends Thread {
private PrivateNum num;
public UnSafeThreadB(PrivateNum num) {
super();
this.num = num;
}
@Override
public void run(){
super.run();
num.addI("b");
}
}
public class PrivateNum {
private int num = 0;
public void addI(String userName) {
try {
if (userName.equals("a")){
num = 100;
System.out.println("a is right");
Thread.sleep(1000);
} else {
num = 200;
System.out.println(userName + "---" + "num = " + num);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class UnSafeThreadTest {
public static void main(String[] args) {
// 共同访问实体变量
PrivateNum privateNum = new PrivateNum();
UnSafeThreadA threadA = new UnSafeThreadA(privateNum);
threadA.start();
UnSafeThreadB threadB = new UnSafeThreadB(privateNum);
threadB.start();
}
}
/**
a is right
b is right
b---num = 200
a---num = 200
*/
原因在于:ThreadA 在执行期间,由于sleep()方法使得ThreadB有机会去占用CPU的time slice。在sleep()方法结束之前,ThreadB已经执行结束了。通过缩小sleep方法的参数,让程序可能出现想要的结果。
正确的解决办法是在addI()方法前加synchronized关键字。
2、多个对象多个锁
多个对象多个锁的话,很可能产生线程安全地问题
public class PrivateNum {
private int num = 0;
// add synchronized
synchronized public void addI(String userName) {
try {
if (userName.equals("a")){
num = 100;
System.out.println("a is right");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("b is right");
}
System.out.println(userName + "---" + "num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MultiObjectTest {
public static void main(String[] args) {
// two different Object
PrivateNum numA = new PrivateNum();
PrivateNum numB = new PrivateNum();
UnSafeThreadA threadA = new UnSafeThreadA(numA);
threadA.start();
UnSafeThreadB threadB = new UnSafeThreadB(numB);
threadB.start();
}
}
/**
* a is right
b is right
b---num = 200
a---num = 100
*/
关键字synchronized取得的锁都是对象锁,而不是把一段代码或者方法当做锁,所以哪个线程先执行带有synchronized关键字的方法,哪个线程就持有方法所属对象的锁,其他的线程就处于堵塞状态。
3、synchronized和asynchronized
线程A先获得object对象的锁,但是线程B可以通过异步的方式调用object对象的非synchronized修饰的方法。
线程A先获得object对象的锁,如果线程B此时向调用object对象中的synchronized方法,则必须等待。