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的实现类。
其次,两种方式的内部流程不同:
- 继承Thread的方式:直接通过创建对象的方式创建线程,是直接调用run()方法的。
- 实现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资源的事件。
线程停止
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();
}
}
}