java多线程-多线程技能

继承Thread类

一个类继承Thread类后创建新线程

package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 22:30
 */
public class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("MyThread");
    }
}
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 15:12
 */
public class Run3 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("运行结束!");
    }
}

运行结果可以看出,新线程创建比运行结束!文字输出要慢

运行结束!
MyThread

如果让主线程休眠200ms,那样顺序将会反过来

package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 15:12
 */
public class Run3 {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        // 主线程休眠三秒
        Thread.sleep(200);
        System.out.println("运行结束!");
    }
}

运行结果

MyThread
运行结束!

使用常见的命令分析现成的信息

  1. 使用jps+jstack,在java的bin目录下打开cmd,运行以下代码后输入jps即可看到现成的状态,输入jstack -l 进程id后可以看指定id的状态
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 22:40
 */
public class Run {
    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();
        }
    }
}
  1. 利用官方工具jmc查看
  2. 利用工具jvisualvm查看

线程随机性的展现

package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 22:50
 */
public class MyThread2 extends Thread{
    @Override
    public void run() {
        for(int i =0; i<10000;i++){
            System.out.println("run="+Thread.currentThread().getName());
        }
    }
}
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 22:51
 */
public class Run2 {
    public static void main(String[] args) {
        MyThread2 thread2 = new MyThread2();
        thread2.setName("thread2");
        thread2.start();
        for (int i = 0; i < 10000; i++) {
            System.out.println("main=" + Thread.currentThread().getName());
        }
    }
}

通过输出可见,main和thread2两个线程随机打印

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

实现Runnable接口

package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 23:03
 */
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("运行中");
    }
}
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 23:04
 */
public class Run4 {
    public static void main(String[] args) {
        Runnable runnable =new MyRunnable();
        Thread thread =new Thread(runnable);
        thread.start();
        System.out.println("运行结束");
    }
}

使用Runnable接口实现多线程的优点

可以实现接口来达到“多继承”的功能

实现Runnable接口与继承Thread类的内部流程

实现Runnable接口法在执行过程上相比继承Thread法稍微复杂一些

实例变量共享造成的非线程安全问题与解决方案

  1. 不共享数据的情况
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 23:22
 */
public class MyThread3 extends Thread{
    private int count = 5;

    public MyThread3(String name) {
        super();
        this.setName(name);
    }

    @Override
    public void run() {
        super.run();
        while (count>0){
            count--;
            System.out.println("由"+ currentThread().getName()+"计算,count="+count);
        }
    }
}
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 23:26
 */
public class Run5 {
    public static void main(String[] args) {
        MyThread3 a = new MyThread3("A");
        MyThread3 b = new MyThread3("B");
        MyThread3 c = new MyThread3("C");
        a.start();
        b.start();
        c.start();
    }
}

从运行结果可见,他们的count值是非共享的,每个线程单独负责自己的count

由C计算,count=4
由A计算,count=4
由B计算,count=4
由A计算,count=3
由C计算,count=3
由A计算,count=2
由C计算,count=2
由B计算,count=3
由C计算,count=1
由A计算,count=1
由C计算,count=0
由B计算,count=2
由A计算,count=0
由B计算,count=1
由B计算,count=0
  1. 共享数据的情况
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 23:22
 */
public class MyThread4 extends Thread{
    private int count = 5;

    @Override
    public void run() {
        super.run();
        while (count>0){
            count--;
            System.out.println("由"+ currentThread().getName()+"计算,count="+count);
        }
    }
}
package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 23:30
 */
public class Run6 {
    public static void main(String[] args) {
        MyThread4 myThread4 = new MyThread4();
        Thread a = new Thread(myThread4,"A");
        Thread b = new Thread(myThread4,"B");
        Thread c = new Thread(myThread4,"C");
        Thread d = new Thread(myThread4,"D");
        Thread e = new Thread(myThread4,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}

这样的话多个线程将会共用数据,因为不加锁,会导致一系列问题,改进方案如下:添加关键字synchronized在run()方法前

package mycode.chapter1;

/**
 * @author 29467
 * @date 2022/9/2 23:22
 */
public class MyThread4 extends Thread{
    private int count = 5;

    // 添加关键字synchronized
    @Override
    synchronized public void run() {
        super.run();
        while (count>0){
            count--;
            System.out.println("由"+ currentThread().getName()+"计算,count="+count);
        }
    }
}

Servlet技术造成的非线程安全问题与解决方案

多线程调用servlet会导致赋值时机错误问题,可在线程调用的servlet方法前添加synchronized关键字加锁解决


留意System.out.println()出现的非线程安全问题

println()方法在内部是同步的,作用范围仅限内部


currentThread()方法

返回代码段正在被哪个线程调用


isAlive()方法

判断当前的线程是否存活


sleep(long millis)方法

sleep()方法能让“正在执行的进程”休眠x毫秒


sleep(long million, int nanos)方法

毫秒+纳秒


StackTraceElement[] getStackTrace()方法

返回一个表示该线程的堆栈跟踪元素数组。第一个元素表示栈顶,是该数组中的最新方法调用,最后一个元素是栈底,表示最早的方法调用。


static void dumpStack()方法

将当前线程的堆栈跟踪信息输出至标准错误流。


static Map<Thread,StackTraceElement[]> geyAllStackTrace()方法

返回所有活动线程的堆栈跟踪的一个映射。


getId()方法

可以取得线程的唯一标识


停止线程

  1. interrupted():测试currentThread()是否已经中断,执行后具有清除状态标志值的功能
  2. isInterrupted():测试this关键字所在类的对象是否已经中断,不清除状态标志值

用stop()方法暴力停止线程

该方法并不能确定停止的位置,调用时会抛出java.lang.ThreadDeath异常,不需要显式捕捉。


使用stop()释放锁给数据造成不一致的结果


使用"return"语句停止线程的缺点与解决方案

多个判断条件下,使用return会导致代码冗余,推荐抛异常throw new InterruptedException()


暂停线程

使用suspend()方法暂停线程,使用resume()方法来恢复线程的执行。


suspend()方法与resume()方法的缺点—独占

在独占期间,其他线程无法访问


suspend()与resume()方法的缺点—数据不完整


yield()方法

yield()方法的作用是放弃当前的CPU资源,让其他任务去占用CPU执行时间,放弃的时间不确定


线程的优先级

优先级较高的线程获得的cpu资源更多,设置线程的优先级用setPriority()方法,优先级分为1-10个等级,超出范围会报错


线程优先级的继承特性

线程的优先级具有继承性,线程继承后优先级相同


优先级的规律性

优先级可以用setPriority()方法设置,cpu尽量给优先级较高的线程


优先级的随机性

优先级高的线程并不是先执行完毕


优先级对线程运行速度的影响

优先级高的运行速度快


守护线程

Java中分为用户线程(非守护线程),和守护线程
当进程中不存在非守护线程了,则守护线程自动销毁
凡是调用setDaemon(true)代码并且传入值的线程才是守护线程

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值