欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Java多线程编程核心技术】1.Java多线程技能-笔记总结

程序员文章站 2022-05-06 07:58:15
...

相关链接:
【Java多线程编程核心技术】1.Java多线程技能-笔记总结
【Java多线程编程核心技术】2.对象及变量的并发访问(上)-笔记总结
【Java多线程编程核心技术】2.对象及变量的并发访问(下)-笔记总结
【Java多线程编程核心技术】3.线程间通信 -笔记总结
【Java多线程编程核心技术】4.Lock的使用-笔记总结
【Java多线程编程核心技术】5.定时器Timer-笔记总结
【Java多线程编程核心技术】6.单例模式与多线程-笔记总结
【Java多线程编程核心技术】7.拾遗增补-笔记总结

进程与多线程的概率及线程优点

【Java多线程编程核心技术】1.Java多线程技能-笔记总结
按【Ctrl+Shift+Esc】 弹出,点击进程选项,这里面的东西就是一个个进程。
进程是受操作系统管理的基本运行单元(原文)
线程则可以理解成为进程中独立运行的子任务,例如在QQ.exe运行时,就有很多子任务在同时允许。如:好友视频线程、下载文件线程、传输数据线程等。
多线程的优势:可以充分的利用计算机cpu的资源。相较于单任务环境,多线程可以来回在多个线程任务间进行切换,避免了在单任务环境下某个任务等待自己所期待的事件而不能运行时长时间的占用cpu.

使用多线程

一个进程正在运行时至少也有一个进程正在运行,例如学习Java时的用到的第一个方法-main()方法,也是通过一个叫作main的线程执行的

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }

输出:main  //这里的main和main方法没有任何关系,仅仅只是同名而已

通过继承Thread类或实现Runnable接口:

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("extends Thread");
    }
}
    public static void main(String[] args) {
        MyThread mythread = new MyThread();
        mythread.start();
        System.out.println("运行结束!");
    }

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("implements Runnable!");
    }
}
    public static void main(String[] args) {
        Runnable runnable=new MyRunnable();
        Thread thread=new Thread(runnable);
        thread.start();
        System.out.println("运行结束!");
    }
输出:
运行结束!
extends Thread! 或 implements Runnable!

1.代码的运行结果与代码执行顺序或调用顺序是无关的
2.如果多次调用start()方法,则会出现异常Exception in thread “main” java.lang.IllegalThreadStateException
3.调用start()方法,通知“线程规划器”此线程已经准备就绪,等待调用线程对应的run()方法,如果直接在main()方法里调用run()方法,那就是main线程去执行run方法里的代码,需等run方法执行完以后才能去执行后面的代码,不具备异步执行的效果。
4.执行start()方法的顺序不代码线程启动的顺序
5.Thread类也实现了Runnable接口

Thread构造函数:

public Thread( );
public Thread(Runnable target);
public Thread(String name);
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, Runnable target);
public Thread(ThreadGroup group, String name);
public Thread(ThreadGroup group, Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name, long stackSize);

public Thread(Runnable target) 这里不光可以传入一个Runnable接口的对象,还可以传入一个Thread类的对象,也就是完全可以将一个Thread对象中的run方法交由其他的线程进行调用!!!

synchronized关键字:实现同步效果,可以在任意对象及方法上加锁,加锁的这段代码称为“互斥区”或“临界区”。
非线程安全:多个线程对同一个对象的同一个实例进行操作时会出现值被更改、值不同步的情况,进而影响程序执行流程。

print()方法在内部是同步的:

public void println(String x){
synchronized(this){
    print(x);
    newLine();
}
}

常用API介绍

currentThread():返回代码段正在被哪个线程调用的信息 //Thread.currentThread().getName()
新创建的线程的默认名字为:Thread-X,X代表数字,如Thread-0,Thread-1,Thread-2

isAlive:测试线程是否处于活动状态,就是线程已经启动且尚未终止。

package mythread;
public class CountOperate extends Thread {
    public CountOperate() {
        System.out.println("CountOperate---begin");
        System.out.println("Thread.currentThread().getName()="
                + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="
                + Thread.currentThread().isAlive());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("this.isAlive()=" + this.isAlive());
        System.out.println("CountOperate---end");
    }
    @Override
    public void run() {
        System.out.println("run---begin");
        System.out.println("Thread.currentThread().getName()="
                + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="
                + Thread.currentThread().isAlive());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("this.isAlive()=" + this.isAlive());
        System.out.println("run---end");
    }
}
public class Run {
    public static void main(String[] args) {
        CountOperate c = new CountOperate();
        Thread t1 = new Thread(c);
        System.out.println("main begin t1 isAlive=" + t1.isAlive());
        t1.setName("A");
        t1.start();
        System.out.println("main end t1 isAlive=" + t1.isAlive());
    }
}

输出结果:
CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
main begin t1 isAlive=false
main end t1 isAlive=true
run---begin
Thread.currentThread().getName()=A
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end

sleep():在制定的毫秒数内让当前“正在执行的线程”休眠(暂停执行) //Thread.sleep(2000)
getId(): 取得线程的唯一标识。
yield():放弃当前的cpu资源,将它让给其他的任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

停止线程

interrupt():在当前线程中打了一个停止的标记,并不是真正的停止线程
interrupted():测试当前线程是否已经中断,执行后具有将状态标志清除为false的功能 //public static boolean interrupted()
isInterrupted():测试线程是否已经中断,但不清除状态标志 //public boolean isInterrupted()

异常法-停止线程

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        try {
            for (int i = 0; i < 500000; i++) {
                if (this.interrupted()) {
                    System.out.println("已经是停止状态了!我要退出了!");
                    throw new InterruptedException();
                }
                System.out.println("i=" + (i + 1));
            }
            System.out.println("我在for下面");
        } 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(2000);
            thread.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}
i=146942
i=146943
i=146944
i=146945
i=146946
i=146947
i=146948
end!
已经是停止状态了!我要退出了!
进MyThread.java类run方法中的catch了!
java.lang.InterruptedException

     at sort.MyThread.run(MyThread.java:12)

在沉睡中停止(sleep()+interrupt()):

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        try {
            System.out.println("run begin");
            Thread.sleep(200000);
            System.out.println("run end");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中被停止!进入catch!当前线程状态:"+this.isInterrupted());
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(200);
            thread.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }

}
run begin
end!
在沉睡中被停止!进入catch!当前线程状态:false
java.lang.InterruptedException: sleep interrupted
     at java.lang.Thread.sleep(Native Method)

     at sort.MyThread.run(MyThread.java:9)

在sleep状态下停止某一线程,会进入catch语句,并清除停止状态值,使之变成false。
同样,先interrupted(),再sleep(),也是同样的效果

暴力停止线程-stop()
当调用stop()时会抛出java.lang.ThreadDeath异常
stop()已经作废,1.可能让一些清理性的工作得不到完成 2.会锁定的对象进行了“解锁”,导致得不到同步处理,导致数据不一致。

用return停止线程(interrupt()+return):

public class MyThread extends Thread {
    @Override
    public void run() {
            while (true) {
                if (this.isInterrupted()) {
                    System.out.println("停止了!");
                    return;
                }
                System.out.println("timer=" + System.currentTimeMillis());
            }
    }
}

建议使用“抛异常”的方法来实现停止线程,因为在catch块中还可以将异常向上抛,使线程停止的事件得以传播

暂停线程

suspend():暂停线程
resume():恢复线程
缺点
1.独占:极易造成公共的同步对象的独占,使其他线程无法访问公共同步对象(尤其注意当与println()方法搭配使用时)
2.不同步

线程的优先级

设置方法-setPriority()
优先级分为1~10,默认值为5 (NORM_PRIORITY=5)

线程优先级的继承特性:例如A线程启动B线程,则B线程的优先级与A是一样的
优先级具有规则性:CPU尽量将执行资源让给优先级比较高的线程
优先级具有随机性:不要把优先级与运行结果的顺序作为衡量的标准,优先级较高的线程并不一定每一次都先执行完run()方法中的任务,线程的优先级与调用run()方法顺序无关

优先级越高,相对越快

守护线程

守护线程:Java中有两种线程,用户线程与守护线程(典型的应用就是垃圾回收器),当进程中不存在非守护线程了,则守护线程自动销毁//这里介绍gc比较少,后面在总结《深入理解Java虚拟机》时会更详细的介绍
Daemon的作用:为其他线程的运行提供便利服务

Mythread thread=new Mythread();
thread.setDaemon(true); //设置为守护线程