- 接着上一篇文章讲,在这一篇的篇幅中我将继续带着大家学习java多线程中的一些常用方法。接下来会讲到如何让线程暂停和重启、放弃资源以及线程优先级的相关问题。
suspend()方法和resume()
- 在java多线程中,也会遇到线程的暂停这种情况,实现这一需求就是使用suspend()方法让线程暂停,resume()方法则是让线程重新启动。我们来看下面的例子,suspendAndResumeThread线程暂停后两秒再启动。
package day04;
/**
*
* @ClassName: SuspendAndResumeTest
* @Description: 线程暂停与重启测试学习
* @author Jian.Wang
* @date 2018年12月11日
*
*
*/
public class SuspendAndResumeTest {
private static final String MY_THREAD_NAME = "王守田";
@SuppressWarnings("deprecation")
public static void main(String[] args) {
try {
SuspendAndResumeThread suspendAndResumeThread = new SuspendAndResumeThread(MY_THREAD_NAME);
System.out.println("suspendAndResumeThread线程名称为: " + suspendAndResumeThread.getName() + "--" + suspendAndResumeThread.getId() );
suspendAndResumeThread.start();
System.out.println("线程暂停了.........." + System.currentTimeMillis());
suspendAndResumeThread.suspend();
Thread.sleep(2000);
suspendAndResumeThread.resume();
System.out.println("线程启动了.........." + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*
* @ClassName: SuspendAndResumeThread
* @Description: 线程暂停与重启
* @author Jian.Wang
* @date 2018年12月11日
*
*/
class SuspendAndResumeThread extends Thread{
public SuspendAndResumeThread() {
}
public SuspendAndResumeThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程中输出的数据: " + i);
}
}
}
- 在代码中细心的同学可能已经注意到了,
suspend() 方法和resume() 方法是这样的,过时了!!是的,因为这两个方法在使用的时候会出现一些问题,第一个问题就是:独占,也就是说如果使用不当的话可能造成公共的同步对象的独占,使得其他线程无法访问公共的同步对象。看下面的例子:
package day04;
import java.util.Objects;
/**
*
* @ClassName: MonopolyTest
* @Description: suspend()方法和resume()方法存在缺点一 -- 独占。
* @author Jian.Wang
* @date 2018年12月11日
*
*/
public class MonopolyTest {
private static final String THREAD_FIRST_NAME = "王守田";
private static final String THREAD_SECCEND_NAME = "王守地";
public static void main(String[] args) {
try {
MonopolyThreadBasic monopolyThreadBasic = new MonopolyThreadBasic();
Thread threadFirst = new Thread(monopolyThreadBasic,THREAD_FIRST_NAME);
Thread threadSeccend = new Thread(monopolyThreadBasic,THREAD_SECCEND_NAME);
System.out.println("当前时间戳---" + System.currentTimeMillis());
threadFirst.start();
Thread.sleep(2000);
threadSeccend.start();
System.out.println("当前时间戳---" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
*
* @ClassName: MonopolyThreadBasic
* @Description: 基础线程类
* @author Jian.Wang
* @date 2018年12月11日
*
*/
class MonopolyThreadBasic extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " -- " + Thread.currentThread().getId() + "访问了线程.......");
SynchronizedObject.printString();
}
}
/**
*
* @ClassName: SynchronizedObject
* @Description: 同步类,包含同步方法,模拟线程堵塞
* @author Jian.Wang
* @date 2018年12月11日
*
*/
class SynchronizedObject{
private static final String MY_THREAD_NAME = "王守田";
@SuppressWarnings("deprecation")
synchronized public static void printString() {
System.out.println("进入到了printString方法中.......");
if (Objects.equals(Thread.currentThread().getName(), MY_THREAD_NAME)) {
System.out.println(Thread.currentThread().getName() + "线程怕是要永远停止了哟........");
Thread.currentThread().suspend();
}
System.out.println("马上要出printString方法了.......");
}
}
- 根据输出我们可以看到,当名为王守田的线程执行同步方法中的代码时候进行了暂停,此时王守田一直霸占着公共资源不释放,此时名为王守地的线程进入,但是只能进入到run方法中执行第一行代码,无法进入到同步方法printString()方法中,此时程序一直一直在等待(右上角的红色方块就代表程序并未终止…),此时王守田出不去,王守地进不来,很尴尬。这就说明在使用暂停方法的时候很容易出现线程独占的巨大问题。
- 不同步,也是使用suspend()方法和resume()方法时很容易就会遇到的问题。当某一个线程在修改某一数据之后并没有完全修改完成就将线程暂停了,此时另外一个线程刚好获取到了这个修改了一半的值,此时数据就不同步了,我们来看下面的例子,输出结果:王守田 : wangshoudi就对不上。
package day04;
import java.util.Objects;
/**
*
* @ClassName: NoSynchronizationCharacter
* @Description: suspend()方法和resume()方法存在缺点二 -- 不同步。
* @author Jian.Wang
* @date 2018年12月11日
*
*/
public class NoSynchronizationCharacter {
private static final String MY_THREAD_NAME = "王守田";
private static final String MY_THREAD_PASSWORD = "wangshoutian";
private static final String HE_THREAD_NAME = "王守林";
public static void main(String[] args) {
try {
BasicObject basicObject = new BasicObject();
new Thread(MY_THREAD_NAME) {
@Override
public void run() {
basicObject.setValue(MY_THREAD_NAME, MY_THREAD_PASSWORD);
}
}.start();
Thread.sleep(1000);
new Thread(HE_THREAD_NAME) {
@Override
public void run() {
basicObject.printUserNameAndPassword();
}
}.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
*
* @ClassName: BasicObject
* @Description: 基础方法类
* @author Jian.Wang
* @date 2018年12月11日
*
*/
class BasicObject {
private final static String MY_THREAD_NAME = "王守田";
private String userName = "王守地";
private String password = "wangshoudi";
@SuppressWarnings("deprecation")
public void setValue(String u, String p) {
this.userName = u;
if (Objects.equals(Thread.currentThread().getName(), MY_THREAD_NAME)) {
System.out.println("线程" + Thread.currentThread().getName() + "将要停止......");
Thread.currentThread().suspend();
}
this.password = p;
}
public void printUserNameAndPassword() {
System.out.println(userName + " : " + password);
}
}
yieId()方法 放弃运行资格
- yieId()方法的作用是放弃当前的CPU资源,将它给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,又马上获得CPU时间片。CPU时间片可能有些同学不是很理解,时间片的概念在计算机操作系统中是一个比较重要的概念,它是分时操作系统分配给每个正在运行的进程微观上的一段CPU时间,在计算机中,并不是所有的程序都是一个并行的执行过程,而是某一个时间片内执行某一个进程或者进程中的某一个线程,因为CPU的执行速度十分快,快到我们完全无法感觉到,以至于认为所有的东西都是同时发生的。当某一个线程获取到了CPU的执行权的时候,使用yieId()方法放弃执行资格,也就是我们平时说的:现在我们把时间交给谁谁谁。现在我们来看例子:
package day04;
import java.util.Objects;
/**
*
* @ClassName: YieIdTest
* @Description: 放弃运行资格校验
* @author Jian.Wang
* @date 2018年12月11日
*
*/
public class YieIdTest {
private static final String MY_FIRST_THREAD_NAME = "王守田";
private static final String MY_SECCEND_THREAD_NAME = "王守地";
public static void main(String[] args) {
try {
YieTestThread firstThread = new YieTestThread(MY_FIRST_THREAD_NAME);
YieTestThread seccendThread = new YieTestThread(MY_SECCEND_THREAD_NAME);
firstThread.start();
seccendThread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*
* @ClassName: YieTestThread
* @Description: 线程类
* @author Jian.Wang
* @date 2018年12月11日
*
*/
class YieTestThread extends Thread{
private static final String MY_THREAD_NAME = "王守田";
public YieTestThread() {
}
public YieTestThread(String name) {
super(name);
}
@SuppressWarnings("static-access")
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
if (Objects.equals(Thread.currentThread().getName(), MY_THREAD_NAME) && Objects.equals(50, i)) {
System.out.println("我要放弃CPU资源了...................");
Thread.currentThread().yield();
}
}
}
}
- 上面结果可以看到,王守田先拿到了线程执行资格,但是在代码中设置了输出到50就让出执行资格给王守地,上面的例子就很好地说明了yieId()方法的使用和作用了。
线程优先级
- **在Thread中,定义了三个值用来设置线程执行的优先级MIN_PRIORITY = 1、NORM_PRIORITY = 5以及MAX_PRIORITY = 10。默认的值为NORM_PRIORITY 。**在多数的多线程程序中一般都是使用默认的线程优先级,手动设置的话可能会赵成不必要的问题。
- 继承性:比如A线程类的优先级为10,那么他的被继承类的优先级同样为10.
- 规则性:高优先级的线程会先执行,但是不代表高优先级的线程会一直一直执行下去,直到其执行完成,在有些时候高优先级在执行的时候也会让出时间片给优先级低的线程执行。
- 随机性:线程执行随机,不看代码先后。