java线程的创建

前言

本文章部分笔记来自自高诗岩的《java多线程编程核心技术》书。

进程

进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动,是程序在一个数据及上运行的过程,它是系统进行资源分配和调度的一个独立单位。

线程

线程可以理解为在进程中独立运行的子任务,例如,QQ.exe运行时,很多的子任务也在同时运行,如好友视频线程、下载文件线程、传输数据线程、发送表情线程等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,传文件、听音乐、发送图片表情等这些功能都有对应的线程在后台默默地运行。

使用线程

继承Thread

public class MyTread extends Thread{
    @Override
    public void run(){
        super.run();
        System.out.println("MYThread");
    }
}
public class Main {
    public static void main(String[] args) {
        MyTread mythreads = new MyTread();
        mythreads.start();
        System.out.println("结束");
    }
}

线程随机性

线程的随机输出是因为CPU将时间片分给不同的线程,线程获得时间片后就执行任务,所以这些线程在交替地执行并输出导致输出结果呈现乱序的现象。

.start()的顺序不代表.run()的顺序

线程.start()的顺序并不代表.run()的顺序,和我们平常代码从上到下顺序执行不太一样。

实现Runnable

我们都知道java支持单继承,使用继承Thread类来开发多线程应用程序在设计上是有局限性的,所以在有些时候,我们可以继承一个类并用implement Runnable接口。

实例共享造成的非线程安全问题

当不共享时,多个线程都是访问各自的实例变量,对各自的变量进行操作, 不让多个线程访问同一个实例变量。
当共享数据时,即多个线程可以访问同一个实例变量。

public class Main {

    public static void main(String[] args) {
        MyTread mythreads = new MyTread();
        Thread a = new Thread(mythreads,"A");
        Thread b = new Thread(mythreads,"B");
        Thread c = new Thread(mythreads,"C");
        Thread d = new Thread(mythreads,"D");
        Thread e = new Thread(mythreads,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();

    }
}
public class MyTread extends Thread{
    private int count =5;
    @Override
    public void run(){
        super.run();
        count--;
        System.out.println("由"+ currentThread().getName()+"计算,count"+count);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传下上传(i3xhdy0ykYwG-1651566575379)(file://C:\Users\大勇\AppData\Roaming\marktext\images\2022-04-29-16-40-01-image.png)(C:\Users\大勇\AppData\Roaming\marktext\images\2022-04-29-16-40-01-image.png)]
我们预期希望时可以递减的,结果不应该重复,但是很明显,这样已经出现了所谓的非线程安全问题。

典型场景:5个销售员卖产品,在售出一个产品后,应该在剩余物品上进行减1操作,这个时候就需要对多个线程进行同步的操作。

我们可以在run方法前面加上一个synchronized关键字,这样线程在对这个订单数量减少时就需要排队进行了

线程常用方法:

currentThread() 方法,返回代码段正在被哪个线程调用。

isAlive() 方法,判断当前的线程是否存活。

sleep() 方法,在指定的时间内让当前"正在执行的线程"休眠(暂停执行)

getId()获取线程的唯一标识

判断线程是否为停止状态

1)public static boolean interrupted():测试currentThread()是否已经中断。

2)public boolean this.isInterrupted():测试this关键字所在类的对象是否已经中断。

interrupted():测试当前线程是否已经中断,线程的中断状态由该方法清除。

两个方法的区别:

1)this.interrupted():测试当前线程是否已经是中断状态,执行后具有清除状态标志值为false的功能。

2)this.isInterrupted():测试线程Thread对象是否已经是中断状态,不清除状态标志。

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

利用

stop()方法已经是作废的方法,因为如果暴力性地强制让线程停止,一些请理性工作得不到完成,或者数据添加不完整。

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

暂停线程suspend()和resume()

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

两者的缺点:独占

当使用方法不当,极易造成公共同步对象被独占,其他线程就无法访问公共同步对象的结果。

当一个线程对一个对象的操作一直都没有结束,其他线程就不能访问这个对象,假如这个线程操作这个对象永远进入suspend()状态,其他线程就不能再访问这个对象了。

缺点二:数据不完整

在使用两个方法时容易出现线程暂停,进而导致数据不完整的情况。

yield方法

这个方法是放弃当前的CPU资源,让其他任务去占用CPU执行时间,放弃时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

线程的优先级

线程可以划分优先级,优先级较高的线程得到CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务,其实就是让高优先级的线程获得更多的CPU时间片。

通过setPriority()方法设置线程的优先级

在java中,线程的优先级一般分为1~10共10个等级。

线程优先级的继承特性

在Java中,线程的优先级具有继承性

A启动B,A和B的优先级是一样的

优先级的规律性

setPriority()方法设置线程的优先级

一般情况下,高优先级的线程总是大部分先执行完,但是不代表高优先级的线程全部先执行完。

优先级的随机性

优先级较高的线程不一定每一次都先执行完。即:优先级高的线程并不一定每一次都先执行完run()中的任务,线程优先级与输出顺序无关。

守护线程

Java中有两种线程:一种是用户线程,也称非守护线程;另一种是守护线程。

当线程中不存在非守护线程,守护线程自动销毁。典型的守护线程就是垃圾回收机制。当最后的一个用户线程销毁了,守护线程退出,进程随即结束了。

public class Run {
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.setDaemon(true);
            thread.start();
            Thread.sleep(10000);
            System.out.println("我离开thread对象也不再打印了,也就是停止了!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class MyThread extends Thread {
    private int i = 0;

    @Override
    public void run() {
        try {
            while (true) {
                i++;
                System.out.println("i=" + (i));
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

传入setDaemon(true)的线程即为守护线程,所以这里面我们可以看出main是属于用户线程,怎么样可以快速判断出来呢,比如我们将这一行代码注释掉,那么这里在main线程结束后,但是MyThread线程还没有结束,即用户线程还么有结束,这时守护线程就不会终止,所以注释掉后,运行起来这个线程就不会终止了。

对象及变量的并发访问

非线程安全问题会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是读取到的数据其实是被更改过的。而线程安全是指获得实例变量的值是经过同步处理的,不会出现脏读的现象。

方法内的变量为线程安全

非线程安全问题存在于实例变量中,对于方法内的私有变量,不存在非线程安全问题

解决方案:

当两个线程同时访问同一个对象的同步方法一定是线程安全的。当线程进入synchronized声明的方法时就会上锁,得等这个方法执行完成后,下一个线程才会进入synchronized声明的方法中。

synchronized方法

调用用关键字synchronized声明的方法一定是排队进行运行的。另外,需要牢牢记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么就没有同步的必要。

当一线程调用一个对象的synchronized类型方法时,其他线程可以调用该对象的的非synchronized方法。

当在一个方法添加synchronized并不是锁方法,而是锁当前的类的对象。在Java中,“锁”就是对象,“对象”可以映射成“锁”,哪个线程拿到这把锁,哪个线程就可以执行这个对象的synchronized同步方法。

synchronized拥有重入锁的功能,当一个线程得到一个对象的锁后,再次请求此对象锁时可

以得到该对象锁的,意思就是当我们在一个synchronized方法内调用其他方法块时,是可以得到锁的。

“可重入锁”是指自己还可以获取自己的内部锁。

synchronized方法和synchronized(this)代码块都是锁定当前对象

总结

简单的一个学习笔记,记录一下。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值