前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JAVA并行-1.Java线程操作

JAVA并行-1.Java线程操作

作者头像
悠扬前奏
发布2019-05-28 20:37:32
4460
发布2019-05-28 20:37:32
举报

1.进程和线程

  • 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统的基础。
  • 面向进程的程序设计中,进程是程序的基本执行实体。
  • 面向线程的程序设计中,进程是线程的容器。
  • 进程是程序的实体,而程序是指令,数据,以及其组织形式的描述。

2.Java中的线程操作

2.1新建线程

2.1.2Thread线程类

  • 关键字new创建一个线程对象,然后调用对象的start()方法:
代码语言:javascript
复制
Thread t1 = new Thread();
t1.start();
  • 线程对象Thread有一个run()方法,start()方法会新建一个线程并让这个线程执行run()方法。
  • 直接使用run()方法,会在当前线程中串行调用run()方法。

2.1.3Runnable接口

也可以用Runnable接口新建线程,它只有一个run()方法,而且默认的Thread.run()就是调用内部的Runnable接口,因此使用Runnable更合理。

代码语言:javascript
复制
public interface Runnable {
    public abstract void run();
}

Thread类有一个构造方法:

代码语言:javascript
复制
public Thread(Runnable target)

默认的Thread.start()方法调用的时候,新的线程就会执行Runnable.run()方法:

代码语言:javascript
复制
public void run(){
    if(target != null){
        target.run();
    }
}

以下代码实现Runnable接口,并将该实例传入Thread,避免重载Thread.run():

代码语言:javascript
复制
public class CreateThread implements Runnable{
    public static void main(String[] args){
        Thread t1 = new Thread(new CreateThread());
        t1.start();
    }

    @Override
    public void run(){
        System.out.println("Here is a Runnable!");
    }
}

2.2 终止线程

一般来说,线程在执行完成之后就会结束。但是也可以手动关闭线程。

2.2.1Thread.stop()

  • Thread.stop()方法可以结束线程,但是是直接终止线程,并立即释放这个线程所持有的锁。
  • 该方法会导致数据不一致的问题,因此已经被标注为废弃,不要使用。

例如,以下代码没有错误信息,但是结果不一致了:

代码语言:javascript
复制
public class StopThreadUnsafe {
    public static User u = new User();

    public static class User {
        int id;
        String name;

        public User() {
            id = 0;
            name = "0";
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User [id = " + id + ", name = " + name + "]";
        }
    }

    public static class ChangeObjectThread extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (u) {
                    int v = (int) (System.currentTimeMillis() / 1000);
                    u.setId(v);
                    // do something else
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    u.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }

    public static class ReadObjectThread extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (u) {
                    if (u.getId() != Integer.parseInt(u.getName())) {
                        System.out.println(u.toString());
                    }
                }
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new ReadObjectThread().start();
        while (true) {
            ChangeObjectThread t = new ChangeObjectThread();
            t.start();
            Thread.sleep(150);
            t.stop();
        }
    }
}

如果要安全的停止一个线程,可以自己决定停止线程的时机,例如将前例中的CreateObjectThread线程中增加一个stopMe()方法:

代码语言:javascript
复制
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;
                }
                synchronized (u) {
                    int v = (int) (System.currentTimeMillis() / 1000);
                    u.setId(v);
                    // do something else
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    u.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }

2.3 中断线程

  • 中断线程并不会使线程立即退出,而是给线程发送通知,线程接到通知后的操作由线程执行决定。
  • JVM中于线程中断有关的方法如下:
    • Thread.interrupt(),是一个实例方法,通知线程中断,即设置中断标志位。中断标志位表示当前线程已经被中断了。
    • Thread.isInterrupted(),也是一个实例方法,判断当前线程是否有中断(通过判断中断标志位)
    • Thread.interrupted(),是一个静态方法,用来判断当前线程的中断状态,同时清除当前线程的中断状态。
  • 方法签名为:
代码语言:javascript
复制
public void Thread.interrupt()  //中断线程
public boolean Thread.isInterrupted()  //判断线程是否中断
public static boolean Thread.interrupted() //判断是否中断,并清除当前中断状态

注意:中断后需要增加中断处理代码,不然中断不会发生作用。

Thread.sleep()函数

在循环体中,出现了sleep()或者wait()等操作,需要通过中断来识别。wait()在下一小节介绍,这里介绍sleep()方法。 Thread.sleep()函数的作用是让当前线程休眠若干时间,其函数签名为:

代码语言:javascript
复制
public static native void sleep(long millis) throws InterruptedException

它会抛出一个InterruptedException异常,不是运行时异常,程序必须捕获并处理他。当线程在sleep()休眠中被中断,这个异常就会产生。 注意:Thread.sleep()方法因为中断抛出异常时,会清除中断标记,如果不加处理,在下一次循环开始时,就无法捕捉这个中断,所以在异常处理中需要再次设置中断标记位。

2.4 等待(wait)和通知(notify)

  • 等待(wait)方法和通知(notify)方法是为了支持多线程的协作而存在的。
  • 这两个方法是在Object类中,即任何对象都能调用这两个方法。
  • Object.wait()方法不能随便调用,需要包含在对应的synchronzied语句中。
  • wait()和notify()方法都需要首先获得目标对象的一个监视器
  • Object.wait()和Thread.sleep()方法都能让线程等待若干时间,区别为:
    • wait()方法可以被唤醒,sleep()方法需要等待时间结束
    • wait()方法会释放目标对象的锁,而sleep()方法不会释放任何资源
  • 其方法签名为:
代码语言:javascript
复制
public final void wait() throws InterruptedException
public final native void notify()

线程调用object.wait()方法,它会进入到object的等待队列。当object.notify()方法被调用时,对象会在线程队列中,随机选择一个线程,将其唤醒。 注意:这个选择是非公平的,完全随机。 使用例子:

代码语言:javascript
复制
package temp;

public class SimpleWN {
    final static Object object = new Object();
    public static class T1 extends Thread{
        public void run() {
            //获得object对象锁
            synchronized(object) {
                System.out.println(System.currentTimeMillis() + ": T1 Start!");
                try {
                    System.out.println(System.currentTimeMillis() + ": T1 is wait for obejct!");
                    // 释放object的对象锁
                    object.wait();
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + ": T1 is end");
            }
        }
    }
    
    public static class T2 extends Thread {
        public void run() {
            synchronized(object) {
                System.out.println(System.currentTimeMillis() + ": T2 Start!");
                System.out.println("notify one thread!");
                // 释放object对象锁
                object.notify();
                System.out.println(System.currentTimeMillis() + ": T2 end!");
                try {
                    // 休眠结束之后,才释放对象锁,T1才能继续执行
                    Thread.sleep(2000);
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
                
            }
        }
    }
    
    public static void main(String[] args) {
        Thread t1 = new T1();
        Thread t2 = new T2();
        t1.start();
        t2.start();
    }
}

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

  • 被挂起(suspend)的线程,需要等到resume()操作后才能继续执行
  • 这两个方法已经标注为废弃,不建议使用
  • 废弃的原因是suspend()方法在导致线程暂停的同时,并不会释放任何锁资源,其他任何要范围被它暂时使用的锁,都无法正常运行。而被挂起的线程状态仍然是Runnable,影响对系统状态的判断。

以下例子会导致系统锁死:

代码语言:javascript
复制
public class BadSuspend {
    public static Object u = new Object();
    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");
    
    public static class ChangeObjectThread extends Thread{
        public ChangeObjectThread(String name) {
            super.setName(name);
        }
        
        @Override
        public void run() {
            synchronized(u) {
                System.out.println("in " + getName());
                Thread.currentThread().suspend();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException{
        // t1
        t1.start();
        Thread.sleep(100);
        t2.start();
        t1.resume();
        // 此时t2的状态依然是RUNNABLE
        t2.resume();
        t1.join();
        t2.join();
    }
}

为了达到相同的目的,可以用如下方法(使用wait()和notify()):

代码语言:javascript
复制
public class GoodSuspend {
    public static Object u = new Object();
    
    public static class ChangeObjectThread extends Thread{
        // 标记变量,表明线程是否被挂起
        volatile boolean suspendme = false;

        public void suspendMe() {
            suspendme = true;
        }
        
        public void resumeMe() {
            suspendme = false;
            synchronized (this){
                notify();
            }
        }
        
        @Override
        public void run() {
            while(true) {
                synchronized(this) {
                    // 检查线程是否被挂起
                    while(suspendme) {
                        try{
                            wait();
                        }
                        catch(InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                synchronized(u) {
                    System.out.println("in ChangeObjectThread");
                }
                Thread.yield();
            }
        }
    }
    
    public static class ReadObjectThread extends Thread{
        @Override
        public void run() {
            while(true) {
                synchronized(u) {
                    System.out.println("in ReadObjectThread");
                }
                Thread.yield();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException{
        // 实例化线程对象
        ChangeObjectThread t1 = new ChangeObjectThread();
        ReadObjectThread t2 = new ReadObjectThread();
        // 运行线程对象
        t1.start();
        t2.start();
        Thread.sleep(1000);
        // 挂起t1线程
        t1.suspendMe();
        System.out.println("suspend t1 2 sec");
        Thread.sleep(2000);
        System.out.println("resume t1");
        t1.resumeMe();
    }
}

2.6 等待线程结束(join)和谦让(yield)

  • join签名如下,有两种:
代码语言:javascript
复制
// 阻塞当前线程,直到目标线程执行完毕
public final void join() throws InterruptedException

 // 最多等待millies毫秒,之后继续执行
public final synchronised void join(long millis) throws InterruptedException
  • join 的本质是调用线程wait()方法在当前线程对象实例上,它使得调用线程在当前线程对象上等待,被等待的线程会在调用结束前调用notifyAll()通知所有等待线程继续执行。JDK中join()实现的核心代码为:
代码语言:javascript
复制
while(isAlive()){
  wati(0);
}

基础join()例子:

代码语言:javascript
复制
package temp;

public class JoinMain {
    public volatile static int i = 0;

    public static class AddThread extends Thread {
        @Override
        public void run() {
            for (; i < 1000000; i++)
                ;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        AddThread at = new AddThread();
        System.out.println(i);
        at.start();
        System.out.println(i);
        at.join();
        // join()保证最后输出的是1000000
        System.out.println(i);
    }
}
  • yield()方法会使当前线程让出CPU,重新进行资源分配。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.12.21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.进程和线程
  • 2.Java中的线程操作
    • 2.1新建线程
      • 2.1.2Thread线程类
      • 2.1.3Runnable接口
    • 2.2 终止线程
      • 2.2.1Thread.stop()
      • 2.3 中断线程
      • 2.4 等待(wait)和通知(notify)
    • 2.5 挂起(suspend)和继续执行(resume)线程
    • 2.6 等待线程结束(join)和谦让(yield)
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档