java多线程深入话题
- 多线程的深入话题
- 优雅的停止线程
- 后台守护线程
- volatile关键字
多线程的深入话题
优雅的停止线程
在多线程启动使用的是Thread类里面的start()方法,而多线程的停止处理,Thread的stop()方法已经废除,不建议使用
还要几个方法也被禁用
- 停止多线程:public void stop();
- 销毁多线程:public void destroy();
- 线程挂起:public final void syspend()、暂停执行;
- 恢复挂起的线程执行:public final void resume();
之所以废除掉这些方法,主要的原因是因为这些方法有的可能导致线程死锁,这个时候我们需要线程停止就需要线程柔和的方式
范例:柔和的停止线程
public class Threade {
public static boolean flag=true;
public static void main(String[] args) throws Exception {
new Thread(()->{
long num=0;
while(flag) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行、num="+num++);
}
},"执行线程").start();
Thread.sleep(200);
flag=false;
}
}
因为现在程序执行,没有其他线程去控制flag的内容,所以现在这个时候,我们就可以通过改变flag来控制程序执行,但是万一有其他线程去控制flag的话,一旦其他线程阻塞导致flag的值无法改变,这个时候就会发现线程无法停止了
所以这个停止线程的方式是现阶段最有效的线程停止方法
后台守护线程
现在假设有一个人并且这个人有一个保镖,那么这个保镖一定是在这个人活着的时候才进行守护。所以在的线程里面可以进行守护线程的定义,也就是说如果现在主线程或者其他线程还在执行的时候,那么守护线程将一直存在,并且运行在后台状态
在Thread类里面提供有如下的守护线程操作方法:
- 设置为守护线程:public final void setDaemon(boolean on);
- 判断是否为守护线程:public fianl boolean isDaemon();
范例:使用守护线程
先看看没有使用守护线程的效果
public class Threade {
public static void main(String[] args) throws Exception {
Thread userthread=new Thread(()->{
for(int x=0;x<10;x++) {//用户线程只执行10次
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行、x="+x++);
}
},"用户线程");
Thread daemonthread=new Thread(()->{
for(int x=0;x<Integer.MAX_VALUE;x++)//守护线程执行次数大于用户线程
{
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行、x="+x++);
}
},"守护线程");
Thread.sleep(200);
userthread.start();
daemonthread.start();
}
}
在这里,用户线程执行完成后守护线程依然在一直执行
接下来看看使用守护线程的效果
public class Threade {
public static void main(String[] args) throws Exception {
Thread userthread=new Thread(()->{
for(int x=0;x<10;x++) {//用户线程只执行10次
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行、x="+x++);
}
},"用户线程");
Thread daemonthread=new Thread(()->{
for(int x=0;x<Integer.MAX_VALUE;x++)//守护线程执行次数大于用户线程
{
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行、x="+x++);
}
},"守护线程");
daemonthread.setDaemon(true);
userthread.start();
daemonthread.start();
}
}
当用户线程执行完成后守护线程同样停止执行
在程序执行过程中最大的守护线程就是GC线程
程序执行中GC线程会一直存在,如果程序执行完毕GC线程也将消失
volatile关键字
volatile关键字主要在属性定义上使用的,表示此属性为直接数据操作,而不进行副本的拷贝处理,所以在一些书上就将其错误的理解为同步属性
在正常进行变量处理的时候往往会经历如下几个步骤:
- 获取变量原有的数据内容副本;
- 利用副本为变量进行数据计算;
- 将计算后的变量,保存到原神空间之中;
而如果一个属性追加了volatile关键字,表示的就是不使用副本,而是直接操作原始变量,相当与节约了拷贝、重新保存步骤
范例:volatile的正确使用
package java线程;
public class Mythread implements Runnable {
private volatile int title=5;
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (this) {
while(this.title>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票处理="+this.title--);
}
}
}
public static void main(String[] args) throws Exception {
Mythread mt=new Mythread();
new Thread(mt,"票贩子A").start();
new Thread(mt,"票贩子B").start();
new Thread(mt,"票贩子C").start();
}
}
面试题:请解释volatile与synchronized的区别
- volatile主要在属性上使用,而synchronized是在代码1块方法上使用的
- volatile无法描述同步处理它只是一种直接的内存处理,避免了副本的操作
- synchronized可以处理同步问题
本质上两者没有联系