线程-06-06

线程构造方法:

设置线程名
线程的任务
设置线程分组

线程的常用属性:

ID,Name…状态
优先级(1-10 默认5)
守护线程(后台线程)

守护线程注意:

1.在守护线程里面创建的线程,默认是守护线程
2.守护线程状态的设置必须放在线程启动之前

start和run方法的区别

run属于普通方法,start是启动线程的方法
run方法可以执行多次,start方法只能执行一次

线程中断:

1.使用全局自定义的变量来终止线程-------->收到终止指令后,会执行完当前任务再终止
2.使用线程提供的终止方法 interrupt 来终止线程---->收到终止指令之后,立马终止
3.使用线程提供的方法 stop 来终止线程(弃用,终止之后,未释放资源,有安全隐患)

终止线程

Thread.interrupted()方法,判断完线程状态之后,就会将线程状态设置为false;
Thread.currentThread.isInterrupted()方法,执行判断线程终止为true之后,

等待一个线程

join()

获取当前线程引用

currentThread()

线程状态
NEW------------------->新建状态,没有调用start()之前的状态
RUNNABLE---------->运行状态,(running执行中,ready就绪(等待cpu时间片))
BLOCKED------------>阻塞状态,多个线程试图获得同一把锁
WAITING-------------->等待状态,没有明确的等待结束时间,调用wait()
TIMED_WAITING—>超时等待状态,有明确的等待时间,如:sleep(xxx)
TERMINATED-------->终止状态
yield()

出让CPU执行权,特点:不一定能成功出让cpu执行权

线程安全

线程不安全:多线程执行中,程序的执行结果和预期的不相符,就叫做线程不安全

线程非安全:
cpu抢占式执行

1.非原子性
编译器优化(代码优化)编译器优化在单线程下没问题,可以提升程序的执行效率,但是在多线程下就会出现混乱,导致线程不安全问题(指令重排序)
2.内存不可见
多个线程修改了同一个变量
volatile 关键字(轻量级解决“线程安全”)
解决:
1.强制内存可见(原理:当操作完变量之后,强制删除线程工作内存中的此变量)
2.禁止指令重排序
注意:
volatile不能解决原子性问题

解决线程安全:

1.CPU抢占调度(不能解决)
2.每个线程操作自己的变量(可能解决)
3.在关键代码上让所有CPU排队执行,加锁;
4.加锁操作的关键步骤
尝试获取 (如果成功拿到锁,加锁,没拿到就等待)
释放锁

Java中解决线程安全问题的方案

1.synchronized 加锁和释放锁(JVM层面的解决方案,自动进行加锁释放锁)
2.Lock 手动锁【程序员自己加锁,释放锁】

Synchronized注意事项:

在进行加锁操作的时候,同一组业务一定是同一个锁对象;

synchronized实现原理:

从操作来看:互斥锁实现 mutex lock
JVM:monitorenter–>monitorexit实现了监视器的加锁和释放锁的操作
Java:
a)锁对象:mutex
b)锁存放的地方:加锁对象的头信息

Lock
注意:一定要把lock()放在try外面
1.如果将lock()方法放在try里面,那么try里面的代码出现异常之后,就会执行finally里的释放锁代码,但还没加锁成功
2.如果将lock()方法放在try里面,那么执行finally的释放锁代码时报的异常就会覆盖try里面的异常

synchronized锁机制是非公平锁
公平锁可以按顺序进行执行,而非公平锁执行的效率更高,是抢占式的
在Java中所有锁默认的策略都是非公平锁

不释放锁会造成死锁

synchronized使用场景:

修饰代码块(加锁对象自定义)
修饰静态方法(加锁对象是当前类对象)
修饰普通方法(加锁对象是当前类的实例)
Lock只能修饰代码块

volatile和synchronized有什么区别

volatile可以解决内存可见性问题和禁止指令重排序,但volatile不能解决原子性问题
synchronized是用来保障线程安全的,也就是说synchronized可以解决任何关于线程安全的问题(5个:抢占式执行,原子性,编译器优化,内存不可见,多个线程修改同一变量)(关键代码排队执行,始终只有一个线程会执行加锁操作)

synchronized和Lock的区别

synchronized既可以修饰代码块,和静态方法或普通方法,lock只能修饰代码块
synchronized只有非公平锁策略,而lock既可以是公平锁,也可以是非公平锁(ReentrantLock默认是非公平锁,也可以通过构造函数声明它为公平锁)
ReentrantLock更加的灵活(如:tryLock)
synchronized是自动加锁释放锁,而RenntrantLock需手动加锁释放锁

线程中start()方法和run()方法的区别

Java中的线程是通过Java.lang.Thread实现的,可以通过实例化Thread一个对象来创建一个线程,然后调用start()启动,也可以通过Thread的一个特定的方法run()启动线程。区别如下:

实例化+start():实例化一个thread对象,然后调用start()启动,此时线程处于运行状态中(ready就绪),等待CPU调度,然后再执行线程里面的run()方法,启动结束。不用等待我们的run方法执行完成就可以继续执行下面的代码

**直接使用run()方法:**直接调用run()方法,启动结束
run方法是thread里面的一个普通的方法,如果直接调用run()方法,此时它会运行在主线程中。由于只有一个主线程,所以如果有两个线程都是直接调用run()方法,那么他们的执行顺序一定是按顺序执行的,并没有实现多线程的目的。

**对比:**start()方法来启动线程,真正实现了多线程,run()方法当作普通方法的方式调用

线程状态
在Java当中,线程通常有五种状态:创建、运行、阻塞、等待、终止

NEW------------------->新建状态,没有调用start()之前的状态
RUNNABLE---------->运行状态,(running执行中,ready就绪(等待cpu时间片))
BLOCKED------------>阻塞状态,
WAITING-------------->等待状态,没有明确的等待结束时间,调用wait()
TIMED_WAITING—>超时等待状态,有明确的等待时间,如:sleep(xxx)
TERMINATED-------->终止状态

代码对比

public class ThreadDemo5 {
    public static final boolean flag=false;
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
        t1.start();
    }
}

调用start()方法,线程名是默认的

public class ThreadDemo5 {
    public static final boolean flag=false;
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
        t1.run();
    }
}

调用run(),线程名是main

在这里插入图片描述

没有用start()方法而是用了run()方法
这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。如果调用start(),线程执行的结果可能是乱序的

/**
 * 三个线程
 * 打印十行ABC
 */

public class ThreadDemo1 {public static void main(String[] args) throws InterruptedException {
        show();
    }

    private static void show() throws InterruptedException {
        for(int i=0;i<10;i++) {

            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.print("A");
                }
            });
            t1.run();
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.print("B");
                }
            });
            t2.run();
            Thread t3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("C");
                }
            });
            t3.run();
        }
	}
}


线程终止方式

1.使用全局自定义的变量来终止线程,定义全局变量来控制线程终止会执行完当前手头的任务之后再终止。
2.使用线程提供的终止方法interrupt来终止进程,在收到终止指令之后会立马结束执行。
3.使用线程提供的方法 stop 来终止线程(弃用,终止之后,未释放资源,有安全隐患)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于多线程编程,ThreadLocal类是一个非常有用的工具。它提供了线程本地变量的机制,使得每个线程都可以独立地存储和获取自己的变量副本。这在一些场景下非常有用,下面是一些ThreadLocal类的应用技巧: 1. 线程上下文信息传递:在多线程环境中,有时需要在线程之间传递一些上下文信息,例如用户身份认证信息、语言环境等。使用ThreadLocal类可以很方便地实现这一目的,每个线程都有自己独立的上下文信息副本,互不干扰。 2. 线程安全的数据存储:在多线程环境中,如果多个线程需要共享一份数据,可以使用ThreadLocal类来维护每个线程的局部副本。这样每个线程都可以独立地修改自己的副本,而不会影响其他线程的数据。 3. 避免传参:有些情况下,我们需要在多个方法之间传递某些参数,但是参数传递会增加代码复杂性。使用ThreadLocal类可以避免传参,每个方法可以直接从ThreadLocal中获取需要的参数值。 4. 事务管理:在一些事务场景中,我们可能需要在同一个线程中执行多个数据库操作,并保证这些操作在同一个数据库事务中。使用ThreadLocal类可以在线程中维护一个数据库连接对象,确保多个操作都使用同一个连接,实现事务的一致性。 需要注意的是,ThreadLocal类虽然提供了线程本地变量的机制,但并不是万能的解决方案。在使用ThreadLocal时,需要注意内存泄漏的问题,及时清理不再使用的资源。 希望以上内容对您有帮助!如需了解更多多线程编程相关内容,可以参考CSDN中张孝祥的相关文章。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值