JAVA并行-1.Java线程操作

2019-05-28 20:37:32 浏览数 (1)

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,重新进行资源分配。

0 人点赞