JavaSE——多线程:常用操作方法

1.多线程的常用操作方法

1.1.线程命名与取得

多线程的运行状态是不确定的,所以对于多线程操作必须有一个明确标识出线程对象的信息,这个信息往往通过名称来描述。在Thread类中提供有如下的线程名称获得相关方法:

No. 方法名称 类型 描述
1 public Thread(Runnable target,String name) 构造 创建线程的时候设置名称
2 public final synchronized void setName(String name) 普通 设置线程名称
3 public final String getName() 普通 取得线程名字
4 public static native Thread currentThread() 本地 取得当前线程对象

线程名称取得示例:

class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName() + ",i=" + i);
        }
    }
}
public class Test{
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        new Thread(mt).start();//没设置名字
        new Thread(mt).start();//没设置名字
        new Thread(mt,"小靓仔").start();//设置名字
    }
}
//当前线程:Thread-0,i=0
//当前线程:Thread-0,i=1
//当前线程:Thread-0,i=2
//当前线程:小靓仔,i=0
//当前线程:小靓仔,i=1
//当前线程:Thread-1,i=0
//当前线程:Thread-1,i=1
//当前线程:Thread-1,i=2
//当前线程:Thread-1,i=3
//当前线程:Thread-1,i=4
//当前线程:小靓仔,i=2
//当前线程:Thread-0,i=3
//当前线程:Thread-0,i=4
//当前线程:Thread-0,i=5
//当前线程:Thread-0,i=6
//当前线程:Thread-0,i=7
//当前线程:Thread-0,i=8
//当前线程:Thread-0,i=9
//当前线程:小靓仔,i=3
//当前线程:Thread-1,i=5
//当前线程:Thread-1,i=6
//当前线程:Thread-1,i=7
//当前线程:Thread-1,i=8
//当前线程:Thread-1,i=9
//当前线程:小靓仔,i=4
//当前线程:小靓仔,i=5
//当前线程:小靓仔,i=6
//当前线程:小靓仔,i=7
//当前线程:小靓仔,i=8
//当前线程:小靓仔,i=9

通过上述代码发现,如果没有设置线程名字,则会自动分配一个线程名字,需要注意的是,线程名字如果要设置应该避免重复,同时中间不要修改

1.2.主方法(主线程)

观察如下代码:

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println("当前线程:" + Thread.currentThread().getName());
    }
}

public class Test {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.run(); // 直接通过对象调用run()方法
        new Thread(mt).start(); // 通过线程调用
    }
}
//当前线程:main
//当前线程:Thread-0

通过以上程序我们发现,主方法本身就是一个线程(主线程),名字叫main,所有的线程都是通过主线程创建并启动的

1.3.线程方法概览图

在这里插入图片描述

1.4.线程休眠(sleep方法)

方法:

public static native void sleep(long millis) throws InterruptedException

线程休眠:指的是让线程暂缓执行,等到预计时间再恢复。线程休眠会立即交出CPU,让CPU去执行其他任务,但是线程休眠不会释放对象锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象,线程休眠时间使用毫秒作为单位运行——阻塞——就绪),示例如下:

class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000 ; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程:" + Thread.currentThread().getName()+" ,i = "
                    +i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

1.5.线程让步(yield()方法)

线程让步:暂停当前正在执行的线程对象并执行其他程序,yield()会让当前线程交出CPU,但是不是立即交出,yield()交出CPU后只能让拥有相同优先级的线程有获取CPU的机会,并且不会释放对象锁(运行——就绪),示例如下:

 class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 3 ; i++) {
            Thread.yield();
            System.out.println("当前线程:" + Thread.currentThread().getName()+" ,i = "
                    +i);
        }
    }
}
public class Test {
    public static void main(String[] args){
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

1.6.join()方法

等待线程终止,如果在主方法调用会使主线程休眠让调用该方法的线程执行完毕后(调用该方法的线程run方法先执行完毕)再恢复主线程,join()方法会释放对象锁运行——阻塞——就绪),示例如下:

class MyThread implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("主线程睡眠前的时间");
            Test.printTime();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName());
            System.out.println("睡眠结束的时间");
            Test.printTime();
        } catch (InterruptedException e) {
// TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException{
        MyThread mt = new MyThread();
        Thread thread = new Thread(mt,"子线程A");
        thread.start();
        System.out.println(Thread.currentThread().getName());
        thread.join();
        System.out.println("代码结束");
    }
    public static void printTime() {
        Date date=new Date();
        DateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time=format.format(date);
        System.out.println(time);
    }
}

1.7.线程停止

多线程中有三种方式可以停止线程:

  • 手工设置标记位,让线程在满足条件后退出(推荐)
  • 使用stop方法,强制让线程(立马)退出,但是该方法不安全,已经被@Deprecated(过期)
  • 使用Thread类提供的interrupt()中断线程

1.7.1.设置标记为使线程退出

示例:

class MyThread implements Runnable {
    private boolean flag = true;
    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
                Thread.sleep(1000);
                System.out.println("第"+i+"次执行,线程名称
                        为:"+Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
// TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread, "子线程A");
        thread1.start();
        Thread.sleep(2000);
        myThread.setFlag(false);
        System.out.println("代码结束");
    }
}

1.7.2.使用stop方法使线程退出

MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread,"子线程A");
thread1.start();
Thread.sleep(3000);
thread1.stop();
System.out.println("代码结束");

使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了,为什么不安全呢?因为stop会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也会马上stop了,这样就产生了不完整的残废数据

1.7.3.使用Thread.interrupt()方法

示例:

class MyThread implements Runnable {
    private boolean flag = true;
    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
                /**
                 * 这里阻塞之后,线程被调用了interrupte()方法,
                 * 清除中断标志,就会抛出一个异常
                 * java.lang.InterruptedException
                 */
                Thread.sleep(1000);
                boolean bool = Thread.currentThread().isInterrupted();
                if (bool) {
                    System.out.println("非阻塞情况下执行该操作。。。线程状态" + bool);
                    break;
                }
                System.out.println("第"+i+"次执行,线程名称
                        为:"+Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                System.out.println("退出了");
                /**
                 * 这里退出阻塞状态,且中断标志被系统会自动清除,
                 * 并且重新设置为false,所以此处bool为false
                 */
                boolean bool = Thread.currentThread().isInterrupted();
                System.out.println(bool);
                //退出run方法,中断进程
                return;
            }
        }
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread, "子线程A");
        thread1.start();
        Thread.sleep(3000);
        thread1.interrupt();
        System.out.println("代码结束");
    }
}

interrupt()只是将线程状态改为中断状态而已,并不会中断一个正在运行的线程,这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。interrupte()方法并不会立即执行中断操作,这个方法只会给线程设置一个为true的中断标志(中断标志只是一个布尔类型的变量),而设置之后,则根据线程当前的状态进行不同的后续操作:

  • 线程的当前状态处于非阻塞状态,那么仅仅是线程的中断标志被修改为true而已
  • 线程的当前状态处于阻塞状态,那么在中断标志设置为true后,还会有如下情况之一的操作:
  • 1.如果是wait()sleep()以及jion()三个方法引起的阻塞,那么会将线程的中断标志重新设置为false,并抛出一个InterruptedException(受查异常)
  • 2.如果在中断时,线程正处于非阻塞状态,则将中断标志修改为true,而在此基础上,一旦进入阻塞状态,则按照阻塞状态的情况来进行处理

例如,一个线程在运行状态中,其中断标志被设置为true之后,一旦线程调用了wait()、jion()、sleep()方法中的一种,立马抛出一个 InterruptedException,且中断标志被程序自动清除,重新设置为false

通过上面的分析,我们可以总结:调用线程类的interrupted方法,其本质只是设置该线程的中断标志,将中断标志设置为true,并根据线程状态决定是否抛出异常。因此,通过interrupted方法真正实现线程的中断原理是:开发人员根据中断标志的具体值,来决定如何退出线程

2.线程优先级

2.1.设置和取得优先级

线程的优先级是指优先级越高线程可能先执行而已,仅仅是可能,在Thread类中提供有如下优先级方法:

  • 设置优先级(左1右10闭区间)
public final void setPriority(int newPriority)
  • 取得优先级
public final int getPriority()

对于优先级设置的内容可以通过Thread类的几个常量来决定:

  • 最高优先级:public final static int MAX_PRIORITY = 10;
  • 中等优先级:public final static int NORM_PRIORITY = 5;
  • 最低优先级:public final static int MIN_PRIORITY = 1;
class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5 ; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName()+" ,i = "
                    +i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt,"1") ;
        Thread t2 = new Thread(mt,"2") ;
        Thread t3 = new Thread(mt,"3") ;
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);
        t3.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
        t3.start();
    }
}
//当前线程:1 ,i = 0
//当前线程:3 ,i = 0
//当前线程:3 ,i = 1
//当前线程:3 ,i = 2
//当前线程:3 ,i = 3
//当前线程:2 ,i = 0
//当前线程:3 ,i = 4
//当前线程:1 ,i = 1
//当前线程:1 ,i = 2
//当前线程:1 ,i = 3
//当前线程:2 ,i = 1
//当前线程:2 ,i = 2
//当前线程:1 ,i = 4
//当前线程:2 ,i = 3
//当前线程:2 ,i = 4

2.2.主线程的优先级

主方法是一个线程,那么主线程的优先级是什么呢?

public class Test {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getPriority());
    }
}
//5

主方法只是一个中等优先级

2.3.线程的继承性

线程是有继承关系的,比如当A线程中启动B线程,那么B和A的优先级将是一样的,线程继承性如下:

class A implements Runnable {
    @Override
    public void run() {
        System.out.println("A的优先级为:" + Thread.currentThread().getPriority());
        Thread thread = new Thread(new B());
        thread.start();
    }
}
class B implements Runnable {
    @Override
    public void run() {
        System.out.println("B的优先级为:" + Thread.currentThread().getPriority());
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new A());
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
    }
}
//A的优先级为:10
//B的优先级为:10

3.守护线程

  • 守护线程:守护线程是一种特殊的线程,属于陪伴线程,只要当前JVM进程中存在任何一个用户线程没有结束,守护线程就一直在工作;只有当最后一个用户线程停止后,守护线程会随JVM进程一起停止
  • java 中有两种线程:用户线程守护线程,可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是用户线程否则就是守护线程。典型的守护线程就是垃圾回收线程,主线程main是用户线程
class A implements Runnable {
    private int i;
    @Override
    public void run() {
        try {
            while (true) {
                i++;
                System.out.println("线程名称:" + Thread.currentThread().getName() +
                        ",i=" + i + ",是否为守护线程:"
                        + Thread.currentThread().isDaemon());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + "中断线程了");
        }
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new A(),"子线程A");
        // 设置线程A为守护线程,此语句必须在start方法之前执行
        thread1.setDaemon(true);
        thread1.start();
        Thread thread2 = new Thread(new A(),"子线程B");
        thread2.start();
        Thread.sleep(3000);
        // 中断非守护线程
        thread2.interrupt();
        Thread.sleep(10000);
        System.out.println("代码结束");
    }
}

从上面的代码可以看出来,B是用户线程,当它中断了之后守护线程还没有结束,是因为主线程(用户线程)还没有结束,所以说明是所有的用户线程结束之后守护线程才会结束

猜你喜欢

转载自blog.csdn.net/LiLiLiLaLa/article/details/94168924