Java线程API,一文总结

线程的创建

继承Thread类

可以通过继承Thread类,再通过new创建对象来创建线程。

	public class MyThread extends Thread {
    
    
	    private int count = 5;
	
	    @Override
	    synchronized public void run() {
    
    
	        super.run();
	        count--;
	        System.out.println("由 " + this.currentThread().getName() + " 计算,count=" +count);
	    }
	}
    // 启动线程
	public class Run {
    
    
    public static void main(String[] args) {
    
    
        MyThread myThread = new MyThread();
        Thread a = new Thread(myThread, "A");
        a.start();
    }
}

实现Runnable接口

可以通过实现Runnable接口,再以构造参数的形式传递到Thread的构造方法中进行创建线程。

	public class MyRunnable implements Runnable {
    
    
	    @Override
	    public void run() {
    
    
	        System.out.println("运行中");
	    }
	}
	// 启动线程
	public class Run {
    
    
	    public static void main(String[] args) {
    
    
	        Thread thread = new Thread(new MyRunnable());
	        thread.start();
	        System.out.println("运行结束");
	    }
	}

两种方式的区别比较

首先,Thread是实现了Runnable接口的,所以理论上,Thread的构造方法既可以传递Thread对象,也可以传递Runnable的实现类。
其次,两种方式的内部流程不同:

  1. 继承Thread的方式:直接通过创建对象的方式创建线程,是直接调用run()方法的。
  2. 实现Runnable接口:调用Thread的run方法,再调用init()方法,对target进行赋值,然后调用run()方法,进行判空,不为空,执行target的run方法,也就执行目标的run方法。
    最后,由于java的单继承,有很多局限,可以采用实现Runnable接口进行扩展。

对线程并发安全问题的处理

同步代码块、同步方法、和锁,详细在后面文章写。

Java线程API

currentThread()

该方法,返回当前代码被哪个线程调用。
我刚刚开始还没有理解多线程,也就没有理解这个方法的放回值,为什么同在一个对象中,确是不同的线程所调用。
因为构造方法被主函数所调用创建,而run方法是新的线程所调用的,故返回值不同。

	public class MyThread extends Thread{
    
    
	    public MyThread() {
    
    
	        System.out.println("构造方法打印:"+Thread.currentThread().getName());
	    }
	
	    @Override
	    public void run() {
    
    
	        System.out.println("run打印:" + Thread.currentThread().getName());
	    }
	}

	public class Run2 {
    
    
	    public static void main(String[] args) {
    
    
	        MyThread myThread = new MyThread();
	        myThread.run();
	        myThread.start();
	    }
	}

start()与run()

start方法创建一个线程,自动调用run方法;而调用run方法立即执行,不启动新的线程。

isAlive()

该方法功能是判断当前线程是否存活。

	public class MyThread extends Thread {
    
    
	    @Override
	    public void run() {
    
    
	        System.out.println("run=" + this.isAlive());
	    }
	}
	
	public class Run {
    
    
	    public static void main(String[] args) {
    
    
	        MyThread myThread = new MyThread();
	        System.out.println("begin == " + myThread.isAlive());
	        myThread.start();
	        try {
    
    
	            Thread.sleep(5);
	        } catch (InterruptedException e) {
    
    
	            e.printStackTrace();
	        }
	        System.out.println("end == " + myThread.isAlive());
	    }
	}

sleep()

该方法暂停线程执行进入Blocked状态,把cpu片段让出给其他线程。

	public class MyThread2 extends Thread {
    
    
	    @Override
	    public void run() {
    
    
	        try {
    
    
	            System.out.println("run threadName="+this.currentThread().getName()+" begin =" + System.currentTimeMillis());
	            Thread.sleep(2000);
	            System.out.println("run threadName=" + this.currentThread().getName()+" end =" + System.currentTimeMillis());
	        }catch (Exception e){
    
    
	            System.out.println(e.getMessage());
	        }
	    }
	}
	
	public class Run1 {
    
    
	    public static void main(String[] args) {
    
    
	        MyThread2 myThead = new MyThread2();
	        System.out.println("begin =" + System.currentTimeMillis());
	        myThead.start();
	        System.out.println("end =" + System.currentTimeMillis());
	    }
	}

yield()

该方法的作用是放弃当前CPU资源,但不会放弃同步锁,并且,无法确定下一次申请CPU资源的事件。

扫描二维码关注公众号,回复: 13376125 查看本文章

线程停止

Java中有三种方法可以使正在运行的线程终止运行:

  • 使用退出标志,使线程正常退出。
  • 使用stop方法强行终止线程,但不推荐。
  • 使用interrupt方法中断线程。

使用interrupt方法中断线程

可以调用interrupt方法,相当于给Thread设立一个中断标志,线程仍然可以执行,这时候,我们可以通过interrupted()方法或isInterrupted()方法进行判断状态,从而进行线程停止。

interrupted()与isInterrupted()的区别

  • interrupted方法被static所修饰,一般通过Thread.interrupted进行调用,但其源码本质是通过interrupted()调用的currentThread.isInterrupted(true)。
  • interrupted()方法测试当前线程是否为中断状态后,返回状态并将状态删除,致使两次调用该方法,返回值不一致。而isInterrupted()不会删除中断状态。
	public class MyThread extends Thread {
    
    
	    @Override
	    public void run() {
    
    
	        super.run();
	        for (int i = 0; i < 80000; i++) {
    
    
	            if (Thread.interrupted()){
    
    
	                System.out.println("已经是停止状态了,退出!");
	                break;
	            }
	            System.out.println("i=" + (i+1));
	        }
	    }
	}
	
	public class Run {
    
    
	    public static void main(String[] args) {
    
    
	        try {
    
    
	            MyThread thread = new MyThread();
	            thread.start();
	//            Thread.sleep(100);
	//            thread.interrupt();
	//            Thread.currentThread().interrupt();
	            System.out.println("是否停止1?=" + thread.interrupted());
	            System.out.println("是否停止2?=" + thread.interrupted());
	        }catch (Exception e){
    
    
	            e.printStackTrace();
	        }
	        System.out.println("end!");
	    }
	}
	
	
	public class Run3 {
    
    
	    public static void main(String[] args) {
    
    
	        try {
    
    
	            MyThread thread = new MyThread();
	            thread.start();
	//            Thread.sleep(5000);
	            thread.interrupt();
	            System.out.println("是否停止1?=" + thread.isInterrupted());
	            System.out.println("是否停止2?=" + thread.isInterrupted());
	        }catch (Exception e){
    
    
	            e.printStackTrace();
	        }
	        System.out.println("end!");
	    }
	}

异常法

虽然通过interrupt()方法可以标记线程中断,配合Thread.interrupted()可以判断线程的中断状态,但是,仅仅如此的话,如果判断语句之外,还有代码,仍然会被执行。

public class MyThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        super.run();
        for (int i = 0; i < 500000; i++) {
    
    
            if (Thread.interrupted()){
    
    
                System.out.println("停止!退出!");
                break;
            }
            System.out.println("i=" + (i+1) );
        }
        System.out.println("我被输出,如果此代码是for继续执行,线程并未停止");
    }
}

public class Run {
    
    
    public static void main(String[] args) {
    
    
        try{
    
    
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(50);
            thread.interrupt();
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

可以看到,虽然循环语句结束了,但是最后的sout还是输出了,代表线程还未完全停止。为解决这个问题,可以采用异常的方式,进行线程停止的控制。

public class MyThread extends Thread {
    
    
    @Override
    public void run() {
    
    
        super.run();
        try {
    
    
            for (int i = 0; i < 500000; i++) {
    
    
                if (Thread.interrupted()){
    
    
                    System.out.println("已经是停止状态了!退出!");
                    throw new InterruptedException();
                }
                System.out.println("i=" + (i+1));
            }
            System.out.println("还执行吗?");
        }catch (InterruptedException e){
    
    
            System.out.println("进MyThread.java类run方法的catch了!");
            e.printStackTrace();
        }
    }
}

public class Run {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(50);
            thread.interrupt();
        }catch (Exception e){
    
    
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

值得注意的是,sleep()与interrupt()无法一起使用,无论是先执行sleep()还是先执行interrupt(),都会出现异常。

stop()暴力停止线程

stop()方法可以强行终止线程,但由于其副作用太严重,已经是作废的方法。
stop()可能会造成以下后果:

  • 最后的清理型工作无法完成,资源无法释放。
  • 由于stop()在停止线程后,还会将锁一并解除,很容易造成数据不一致的后果。

return终止法

由于此方法编码不太优雅,就不进行展示了,也是与interrupt方法进行结合,只不过将异常换成reuturn;但因为异常可以使用fianlly进行一些普世的资源操作,相比而言,优雅不少。

暂停线程

suspent()与resume()方法结合,前一为线程暂停,后一为线程恢复。
由于这两个方法使用会造成一些问题,也被废弃了。
那么会造成什么问题呢?

  • 资源独占
  • 数据不完整

资源独占

如果线程抢占了线程安全的资源后,被suspent()调用,那么这个资源就被它永远抢占,无法解锁,有可能造成死锁。

public class SynchronizedObject {
    
    
    synchronized public void printString() {
    
    
        System.out.println("begin");
        if (Thread.currentThread().getName().equals("a")){
    
    
            System.out.println("a线程被永远suspend了");
            Thread.currentThread().suspend();
        }
        System.out.println("end!");
    }
}

public class Run {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            final SynchronizedObject obj = new SynchronizedObject();

            Thread thread = new Thread() {
    
    
                @Override
                public void run() {
    
    
                    obj.printString();
                }
            };
            thread.setName("a");
            thread.start();

            Thread.sleep(1000);

            Thread thread1 = new Thread(){
    
    
                @Override
                public void run() {
    
    
                    System.out.println("thread1 启动了,但无法进入printString()方法!仅打印一个begin");
                    System.out.println("因为printString()方法被a线程锁定并且永远被suspend暂停了");
                    obj.printString();
                }
            };
            thread1.start();
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }
}

数据不完整

很可能由于线程暂停后,其他线程取数据时,出现错误,数据不完整。

public class MyObject {
    
    
    private String username = "1";
    private String password = "11";

    public void setValue(String u, String p){
    
    
        this.username = u;
        if (Thread.currentThread().getName().equals("a")){
    
    
            System.out.println("暂停a线程!");
            Thread.currentThread().suspend();
        }
        this.password = p;
    }

    public void printUsernamePwd(){
    
    
        System.out.println(username + " " + password);
    }
}

public class Run {
    
    
    public static void main(String[] args) {
    
    
        try{
    
    
            MyObject myObject = new MyObject();
            Thread thread = new Thread(){
    
    
                @Override
                public void run() {
    
    
                    myObject.setValue("a", "aa");
                }
            };
            thread.setName("a");
            thread.start();

            Thread.sleep(500);

            Thread thread1 = new Thread(){
    
    
                @Override
                public void run() {
    
    
                    myObject.printUsernamePwd();
                }
            };
            thread1.start();
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_44157349/article/details/121063433