java多线程笔记1

学习书籍《java多线程编程核心技术(第二版)-高洪岩》

初识线程

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

在这里插入图片描述
上面解释为:名称为main的线程在实现main()方法中的代码

使用多线程(1.继承Thread类,2.实现Runnable接口)

打开源码我们发现
在这里插入图片描述
Thread类竟然实现了Runnable接口。
所以我们有多种方法创建线程对象,其结果都是一样的,即创建一个线程
1.永Thread类创建对象
2.子线程类MyThread继承父线程类Thread,然后创建对象
3.子线程类实现Rubbable接口,然后创建对象

public class HelloMain {
    public static void main(String[] args) {
        Runnable t1 = new Thread();
        Runnable t2 = new MyThread();
        Thread t3 = new MyThread();
        t3.start();
        System.out.println("hello main");
    }
    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println("MyThread");
        }
    }
}

上面代码永start()方法来启动一个线程,线程启动后会自动调用线程对象中的run()方法,run()方法里面的代码就是线程对象要执行的任务。

结果运行10次都是下面的情况
在这里插入图片描述

对于上面的代码,明明先执行t3.start,再执行sout,但是结果却是先输出sout在执行t3.start
下面给出原因
执行start()时要有以下步骤导致耗时,以至于系统先执行了后面的sout代码
1.告诉jvm创建Thread
2.操作系统开辟内存创建线程对象
3.操作系统调度线程
4.线程被执行

可以使用sleep函数,结果会不同。

查看线程

public class Run3 {
    public static void main(String[] args) {
        for (int i=0;i<5;i++){
            new Thread(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(500000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

创建5个线程,每个线程500秒的sleep
查看方式有三种:
第一种:
打开jkd的安装目录的bin的文件夹
我的是

在这里插入图片描述
jps+jstack.exe
第二种(推荐使用)
双击jmc.exe,关闭欢迎页面,进入你的线程类
我的是

在这里插入图片描述
双击MBean服务器
你能看到java虚拟机的一些参数。
第三种
双击jvisualvm.exe,和第二种差不多

线程随机性的体现

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            System.out.println("thread"+Thread.currentThread().getName());

        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i=0;i<10000;i++){
            System.out.println("main"+Thread.currentThread().getName());
        }
    }
}

部分结果
在这里插入图片描述
start()方法,通知线程规划器,此线程已经准备好,准备调用run方法,系统你来安排时间调用。
同时也说明,线程的启动顺序与线程的执行顺序是不同的。
到此为止,我是这样想的,java虚拟机是一个操作系统的进程,当执行该Test类的main方法时,会创建一个线程,该线程的名字是main,当我们在执行main线程的时候,又创建了一个MyThread,此时的虚拟机进程又会创建一个新的线程同时和已经存在的main线程同时执行

执行start()的顺序不代表执行run()的顺序

public class MyThread extends Thread {
    private int i;

    public MyThread(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(i);
    }
}

public class Test {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread(1);
        MyThread myThread2 = new MyThread(2);
        MyThread myThread3 = new MyThread(3);
        MyThread myThread4 = new MyThread(4);
        MyThread myThread5 = new MyThread(5);
        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
        myThread5.start();
    }
}

结果
在这里插入图片描述

实现runnable接口(推荐使用)

因为runnable是一个接口,所以不能直接new,要用它的实现类来实现
1.创建一个类(可以是Thread的子类)实现Runnable接口
2.创建该实现类的对象
3.用Thread类的构造函数,传入该对象,然后构造一个线程对象
在这里插入图片描述

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("myRunnable");
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("mythread");
    }
}

public class Test {
    public static void main(String[] args) {
        Runnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
//        ----------------------------------
        Thread myThread = new MyThread();
        Thread thread1 = new Thread(myThread);
        thread1.start();
    }
}

结果
在这里插入图片描述

线程共享实例变量和线程不共享实例变量的区别

1.线程不共享变量情况(每个线程都有自己的变量,其他线程修改同样的变量时,自己的变量值不变)
public class MyThread implements Runnable{
    private String name;//线程名称
    private int count = 3;//表示该线程内的变量,与其他线程无关

    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        while (count>0){
            System.out.println(this.name+":"+count);
            count--;
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Runnable runnable1 = new MyThread("A");
        Runnable runnable2 = new MyThread("B");
        Runnable runnable3 = new MyThread("C");

        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);
        Thread thread3 = new Thread(runnable3);

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

结果
在这里插入图片描述

1.线程共享变量情况(多个线程访问一个变量),非线程安全:多个线程对同一对象中的变量进行操作时出现的值不同步现象
public class MyThread1 implements Runnable{
    private int count = 5;
    @Override
    public void run() {
    	//先执行count--,再执行sout
        System.out.println("count:"+count--);
    }
}

public class Test1 {
    public static void main(String[] args) {
        Runnable runnable = new MyThread1();
        Thread threadA = new Thread(runnable, "A");
        Thread threadB = new Thread(runnable, "B");
        Thread threadC = new Thread(runnable, "C");
        Thread threadD = new Thread(runnable, "D");
        Thread threadE = new Thread(runnable, "E");

        threadA.start();
        threadB.start();
        threadC.start();
        threadD.start();
        threadE.start();
    }
}

结果,不仅共享了一个变量,而且线程不安全
在这里插入图片描述
如果在run方法加上synchronized关键字,让其排队执行,一个线程调用run()方法之前,先判断run方法有没有被加上锁,如果有锁,则不断尝试申请这把锁,必须等其他线程调用run执行完并且释放锁之后,才可以执行该方法,并为该方法加上锁。多个线程都会取申请这把锁。
结果:
在这里插入图片描述

线程安全,抢票

public class MyThread extends Thread {
    private int count = 5;
    @Override
    public void run() {
        super.run();
            --count;
            System.out.println(this.currentThread().getName()+"得到一张,还剩"+count+"张");
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread, "1");
        Thread thread2 = new Thread(myThread, "2");
        Thread thread3 = new Thread(myThread, "3");
        Thread thread4 = new Thread(myThread, "4");
        Thread thread5 = new Thread(myThread, "5");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }
}

结果
在这里插入图片描述
解决办法,
在这里插入图片描述
在这里插入图片描述
加上synchronized,使多个线程在执行run的时候,以排队的方式进行处理。互斥锁。
如果申请不到锁就会自循环。多个线程同时抢锁。

currentThread()方法:该方法表示正在被哪个线程调用

public class MyThread implements Runnable{
    public MyThread() {
        System.out.println("构造方法的执行线程:"+Thread.currentThread().getName());
    }
    @Override
    public void run() {
        System.out.println("线程run方法的执行线程:"+Thread.currentThread().getName());
    }
}

public class Test {
    public static void main(String[] args) {
        Runnable runnable = new MyThread();
        Thread thread = new Thread(runnable,"thread_name_xxx");
        thread.start();
    }
}

结果:
在这里插入图片描述

isAlive方法:判断当前线程是否存活(线程已经启动尚未终止即为存活状态)

 public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(this.isAlive());
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        System.out.println("begin"+myThread.isAlive());
        myThread.start();
        Thread.sleep(1000);
        System.out.println("end="+myThread.isAlive());
    }
}

在这里插入图片描述

sleep(long毫秒)方法:在指定的时间内让【当前正在执行的线程:Thread.currentThread()返回的线程】暂停执行

也有slee(long毫米,int纳秒)

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":"+ System.currentTimeMillis());
        try {
            this.sleep(1000);
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+ System.currentTimeMillis());
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();
        System.out.println(Thread.currentThread().getName()+":"+ System.currentTimeMillis());
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName()+":"+ System.currentTimeMillis());
    }
}

结果:如果调用类是Thread,可以用this.sleep或者Thread.sleep
如果是非Thread类(包含Runnable的继承类),只能用Thread.sleep
在这里插入图片描述

停止与启动(start)线程

一般不用Thread.stop(),用Thread.interrupt()方法。
stop:该方法不安全,并且已经作废
interrupt:不会终止一个正在运行的线程,需要加入一个判断才可以完成线程的停止

 public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 100000; i++) {
            System.out.println(i);
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000);
        myThread.interrupt();
        System.out.println("zzzzzzz");
    }
}

结果
在这里插入图片描述
interrupt并不能马上停止线程,而是在当前线程中做了一个停止的标记。

this.interrupt():测试当前线程是否中断,执行后清除标志位,返回false
this.isInterrupt();测试线程对象是否中断,不清除标志位。

停止线程方法1(异常法)建议使用

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("stoped");
                    throw new InterruptedException();
                }
                System.out.println(i);
            }
            System.out.println("for");
        }catch (InterruptedException e){
            System.out.println("catch");
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) {
        try {
            MyThread myThread = new MyThread();
            myThread.start();
            Thread.sleep(1000);
            myThread.interrupt();
        }catch (InterruptedException e){
            System.out.println("main catch");
            e.printStackTrace();
        }
    }
}

线程停止-异常法

1.停止不了的线程(用interrupt方法),例如

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
            if(this.isInterrupted()){
                System.out.println("线程已经停止,我要break了");
                break;
            }
            System.out.println(i);
        }
        System.out.println("虽然线程已经停止,但是我还能运行");
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();
        Thread.sleep(500);
        thread.interrupt();
        System.out.println("这是main线程");
    }
}

结果:虽然我们调用了,interrupt方法,但是for之外的代码还是继续运行,break无效
在这里插入图片描述
2.异常法(抛出异常)停止线程,还是调用interrupt方法

public class MyThread extends Thread{
    @Override
    public void run() {
        try {
            for (int i = 0; i < 500000; i++) {
                if(this.isInterrupted()){
                    System.out.println("线程已经停止,我要break了");
                    throw new InterruptedException("手动抛出异常");
                }
                System.out.println(i);
            }
            System.out.println("虽然线程已经停止,但是我还能运行");
        }catch (InterruptedException e){
            System.out.println("进入MyThread类的catch方法");
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args){
        try {
            Thread thread = new MyThread();
            thread.start();
            Thread.sleep(500);
            thread.interrupt();
        }catch (InterruptedException e){
            System.out.println("进入Test类的catch方法");
            e.printStackTrace();
        }
        System.out.println("这是main线程");
    }
}

结果:
在这里插入图片描述

线程在sleep状态时执行interrupt方法,或者在执行interrupt方法之后又执行sleep方法,总之:不管这两个方法调用顺序如何,都会进入异常

1.先sleep后interrupt

public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            System.out.println("线程在sleep,执行interrupt,进入InterruptedException");
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
        thread.interrupt();
    }
}

结果:
在这里插入图片描述
2.先interrupt后sleep

public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 500000; i++) {
                System.out.println(i);
            }
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            System.out.println("线程在sleep,执行interrupt,进入InterruptedException");
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
        thread.interrupt();
    }
}

结果:
在这里插入图片描述

return可以停止线程

暂停与恢复线程

suspend暂停,resume恢复 已经作废
可以用wait notify notifyAll(后面单独整理)

yield,放弃当前线程的cpu资源(有可能刚刚放弃,又马上自动获取cpu资源)

public class MyThread extends Thread {
    @Override
    public void run() {
        long begintime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < 20000000; i++) {
//            Thread.yield();
            count = count+i;
        }
        long endtime = System.currentTimeMillis();
        System.out.println(endtime-begintime);
    }
}

public class Test {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}

没有yield时,耗时7毫秒
有yield时,耗时2056毫秒

线程优先级1-10 正常5,

thread.setPriority();
1.优先级有继承性:A线程启动B线程,则B的优先级与A一样

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("MyThord:"+this.getPriority());
    }
}

public class Test {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
        System.out.println("main:"+thread.getPriority());
    }
}

结果:
在这里插入图片描述
2.可以使用thread.setPriority();或者JDK默认数来设置优先级,数字越大,优先级越高
在这里插入图片描述

3.优先级高的先执行,但不是先全部执行完再执行优先级低的代码。只是cpu尽量将资源让给高优先级的线程。

守护线程(后面待研究)

1.java有两种线程:守护线程和用户线程(非守护线程)
2.用户线程:main,或自己创建的线程;守护线程:GC线程
3.当线程中不存在用户线程,守护线程也就自动销毁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值