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;
	}
}

java 多线程部分数据丢失 java多线程销毁_java


因为现在程序执行,没有其他线程去控制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();
	}
}

java 多线程部分数据丢失 java多线程销毁_jvm_02


在这里,用户线程执行完成后守护线程依然在一直执行

接下来看看使用守护线程的效果

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();
	}
}

当用户线程执行完成后守护线程同样停止执行

java 多线程部分数据丢失 java多线程销毁_java_03


在程序执行过程中最大的守护线程就是GC线程

程序执行中GC线程会一直存在,如果程序执行完毕GC线程也将消失

volatile关键字

volatile关键字主要在属性定义上使用的,表示此属性为直接数据操作,而不进行副本的拷贝处理,所以在一些书上就将其错误的理解为同步属性

在正常进行变量处理的时候往往会经历如下几个步骤:

  • 获取变量原有的数据内容副本;
  • 利用副本为变量进行数据计算;
  • 将计算后的变量,保存到原神空间之中;

而如果一个属性追加了volatile关键字,表示的就是不使用副本,而是直接操作原始变量,相当与节约了拷贝、重新保存步骤

java 多线程部分数据丢失 java多线程销毁_java_04


范例: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();
		
		
	}

}

java 多线程部分数据丢失 java多线程销毁_守护线程_05


面试题:请解释volatile与synchronized的区别

  • volatile主要在属性上使用,而synchronized是在代码1块方法上使用的
  • volatile无法描述同步处理它只是一种直接的内存处理,避免了副本的操作
  • synchronized可以处理同步问题
    本质上两者没有联系