java并发(2)——线程的基本操作

线程的基本操作

新建线程

java中建立线程可以有两种方式,分别是继承Thread类和实现Runnable接口。

public static class CreateThread extends Thread{
    @Override
    public void run() {
        System.out.println("Oh, I am CreateThread");
    }
}

public class CreateThread implements Runnable {
    @Override
    public void run() {
        System.out.println("Oh, I am Runnable");
    }
}

开启线程

开启线程的方式分为两种start(),run()

Thread t1 = new Thread();
t1.start()

Thread t2 = new Thread();
t2.run()

两者的区别为:

start()是开启一个新的线程并执行run()函数,run()只在当前线程之中串行执行run()的代码。

终止线程

通常情况线程执行完毕后就会结束,不需要手动关闭,例如绝大部分的worker类型的线程。但是有些服务端的后台线程会常驻系统(死循环方式)。

JDK中提供了Thread.stop()方法,不过是一个废弃的方法,原因是:stop()方法过于暴力,强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。例如:Thread.stop()方法在结束线程的时候,会直接终止线程,并且会立即释放这个线程所持有的锁。而这些锁恰恰是用来维持对象一致性的。

更好的结束线程的方式是使用一个变量标识是否需要结束,或者使用中断的方式:

//每次都是执行完一个循环体之后再退出
//线程何时退出由程序员决定
public static class ChangeObjectThread extends Thread {
    volatile boolean stopme = false;

    public void stopMe(){
        stopme = true;
    }
    @Override
    public void run() {
        while (true) {
            if (stopme){
                System.out.println("exit by stop me");
                break;
            }
            //do something
        }
    }
}


//这里使用了中断判定来退出
public static class ChangeObjectThread extends Thread {

    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()){
                System.out.println("exit with interrupted");
                break;
            }
            //do something
        }
    }
}

线程中断

与线程中断有关的,有三个方法:

public void Thread.interrupt()              //中断线程
public boolean Thread.isInterrupted()       //判断是否被中断
public static boolean Thread.interrupted()  //判断是否被中断,并清除中断标志,static
public static void main(String[] args) throws InterruptedException {
    Thread t1=new Thread(){
        @Override
        public void run(){
            while(true){
                // if(Thread.currentThread().isInterrupted()){
                //  System.out.println("Interruted!");
                //  break;
                // }
                Thread.yield();
            }
        }
    };
    t1.start();
    Thread.sleep(2000);
    t1.interrupt();
}

在这里虽然对t1进行了中断,但是在t1中并没有中断处理的逻辑(注释部分),因此即使t1线程被设置了中断状态,但是这个中断不会发生任何作用。

等待(wait)和通知(notify)

为了支持多线程之间的协作,JDK提供了两个非常重要的接口线程等待wait()方法和通知notify()\notifyAll()方法。这两个方法并不是在Thread类中的,而是输出Object类。

这也意味着任何对象都可以调用这两个方法。

public class SimpleWN {
    final static Object object = new Object();
    public static class T1 extends Thread{
        public void run()
        {
            synchronized (object) {
                System.out.println(System.currentTimeMillis()+":T1 start! ");
                try {
                    System.out.println(System.currentTimeMillis()+":T1 wait for object ");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis()+":T1 end!");
            }
        }
    }
    public static class T2 extends Thread{
        public void run()
        {
            synchronized (object) {
                System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");
                object.notify();
                System.out.println(System.currentTimeMillis()+":T2 end!");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
            }
        }
    }
    public static void main(String[] args) {
        Thread t1 = new T1() ;
        Thread t2 = new T2() ;
        t1.start();
        t2.start();
    }
}

上述的代码如果去除掉synchronized块,代码将无法运行。因为出现了java.lang.IllegalMonitorStateException。

首先,这儿要非常注意的几个事实是:

1)任何一个时刻,对象的控制权(monitor)只能被一个线程拥有。无论是执行对象的wait、notify还是notifyAll方法,必须保证当前运行的线程取得了该对象的控制权(monitor)

2)如果在没有控制权的线程里执行对象的以上三种方法,就会报java.lang.IllegalMonitorStateException异常。

3)JVM基于多线程,默认情况下不能保证运行时线程的时序性

基于以上几点事实,我们需要确保让线程拥有对象的控制权。也就是说在T1中执行wait方法时,要保证T1对object有控制权;在T2中执行notify方法时,要保证T2对object有控制权。

挂起(suspend)和继续执行(resume)线程

suspend()和resume()是一对相反的操作,在JDK中已经是废弃的方法。不推荐suspend()去挂起线程的原因是因为suspend()在导致线程暂停的同时,并不会去释放任何资源。

更为严重的是如果resume()在suspend()之前就执行了,那么被挂起的线程可能很难有机会被继续执行。并且线程所持有的资源将不会被释放。

等待结束(join)和谦让(yield)

和字面意思一样。

线程组

public class ThreadGroupName implements Runnable {
    public static void main(String[] args) {
        //建立一个线程组
        ThreadGroup tg = new ThreadGroup("PrintGroup");
        //创建线程的时候将线程加入到线程组,使用了Thread的构造函数:
        //public Thread(ThreadGroup group, Runnable target, String name)
        Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");
        Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");
        t1.start();
        t2.start();
        //线程组中活动线程的总数,但是由于线程是动态的,所以这个只是一个估计值
        System.out.println(tg.activeCount());
        //列出线程组中的线程
        tg.list();
    }

    @Override
    public void run() {
        String groupAndName = Thread.currentThread().getThreadGroup().getName()
                + "-" + Thread.currentThread().getName();
        while (true) {
            System.out.println("I am " + groupAndName);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:

![运行结果](https://img-blog.csdn.net/20171108164802243?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2ljb2ZpZWxk/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 运行结果 线程组还有一个应用就是ThreadGroup.stop()方法,不过其所存在的问题和Thread.stop()一样,因此使用时需要格外谨慎。

守护线程

守护线程是一种特殊的线程,它是系统的守护者,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。与之相对应的是用户线程,用户线程可以认为j是系统的工作线程。如果用户线程全部结束,这也意味这这个程序实际上无事可做了。守护线程要守护的对象已经不存在了,那么整个应用程序就自然应该结束。因此,当一个Java应用内,只有守护线程的时候,Java虚拟机就会自然退出。

简单看下守护线程的使用:

public class DaemonDemo {
    public static class DaemonT extends Thread{
        public void run(){
            while(true){
                System.out.println("I am alive");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t=new DaemonT();
        //必须在start()之前设置为守护线程
        t.setDaemon(true);
        t.start();

        Thread.sleep(2000);
    }
}

注意:守护线程Thread.setDaemon(true)必须要在线程start()之前设置,否则抛出java.lang.IllegalThreadStateException。设置无效,也不影响原来线程作为工作线程继续工作

线程优先级

在java中,使用1到10表示线程优先级(数字越大优先级越高)。一般可以使用Thread类中定义的三个静态标量来表示:


线程优先级
线程优先级


这里写图片描述
Thread.setPriority()例子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值