一、守护线程与yield
1. 守护线程(Daemon):
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
public final boolean isDaemon() {
return daemon;
}
setDaemon()方法是将线程标记为daemon线程或用户线程。当运行的唯一线程都是守护线程时,JVM将退出。
isDaemon()方法是测试当前线程是否是守护线程。
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnableThread());
thread.setDaemon(true);// 把线程设置为守护线程。当进程中没有用户线程是,JVM将退出,设置需要在启动之前设置
thread.start();
for (int i = 0; i < 50 ; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程(用户线程)当前执行:"+i);
}
}
}
class MyRunnableThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50 ; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("MyRunnableThread当前执行:"+i);
}
}
}
案例通过守护线程和用户线程(mainThread)做比较,注意,使用守护线程时要在start()之前,等待CPU调度之前设置为守护线程。 否则处于Alive()状态,会抛出异常。异常解释看上篇文章:两篇文章了解进程与线程( 基础篇)。
可以看出,当主线程结束时,JVM退出,守护线程也就结束了。那么这个有什么用呢?举个例子,当我们的项目涉及用户下载时,当用户退出应用,主线程结束,如果非守护线程,下载继续执行,所以可以将下载功能依托于守护线程,当用户退出时退出,在Android开发中较常用。 但不要涉及到数据存储,否则用户线程结束时候,数据可能没来得及保存好。
2. yield:
public static native void yield();
调用本地方法(C/C++写的功能),让一次。
准确来说,我觉得这个方法没啥用。。。
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnableThread());
thread.start();
for (int i = 0; i < 50 ; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程(用户线程)当前执行:"+i);
}
}
}
class MyRunnableThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50 ; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i == 6) Thread.yield();
System.out.println("MyRunnableThread当前执行:"+i);
}
}
}
就让一次,抢CPU时间片时,当i == 6时候,让一次调度,并执行其他线程。
二、线程同步:
1. 多线程共享数据:
在多线程的操作中,多个线程可能处理同一个数据资源,这就是多线程中的共享数据。
以售票为模拟,假设有十张票,两个窗口售卖。常规开两个线程同时处理会出现数据为假的问题,比如线程1已经卖了就剩3张了,可能线程2还有4张。
错误示例:
public class ThreadDemo {