19 多线程

3 篇文章 0 订阅

进程

进程是一个应用程序(1个进程是一个软件)。线程是一个进程中的执行场景/执行单元。

线程

一个进程可以启动多个线程。

4.2、对于java程序来说,当在DoC命令窗口中输入: java Helloworld回车之后。 会先启动JVM,而JVM就是一个进程。JVM再启动一个主线程调用 main方法。 同时再启动一个垃圾回收线程负责看护,回收垃圾。最起码,现在 的java程序中至少有两个线程并发, 一个是垃圾回收线程,一个是执行main方法的主线程。

注意:
进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!)
    魔兽游戏是一个进程
    酷狗音乐是一个进程
    这两个进程是独立的,不共享资源。
线程A和线程B呢?
    在java语言中:
        线程A和线程B,堆内存和方法区内存共享。但是栈内存独        定,**一个线程一个栈**。
​

1629959323804

1629959749326

1629960716196

第一种方法

1629960694852

run()方法内存图

1629961093776

start方法内存图

1629961233827

//此程序有先有后,且控制台只有一个,

第二种方式

新建一个class ,实现java.lang.Runnable

implements Runnable

1629961667066

第3种方式

匿名内部类,直接new接口Runnable

线程生命周期

1629963195661

获取线程的名字

1629964133998

Mythread t=new Mythread();
t.setName("线程1");//如果不改名字t.getName();得到的是THread-0
​
t.start();

1.

1629963687675

哪个线程执行的就是当前线程

1629963791862

线程的sleep() 方法

1629964610377

1629965420300

终止线程的睡眠interrupt(),不是中断线程

关于重写的方法,子类重写的方法不能比父类的方法抛出更多的异常

先是t.interrupt();之后然后,Thread.sleep()出异常 然后捕捉异常!然后程序再继续往下走!

1629968744374

1629968687925

强行终止线程的睡眠

t.stop();

这种方式存在很大的缺点:容易丢失数据。因为这种方式是直接杀死线程

这种比较合理,通过打一个布尔标记

1629969504707

1629969579683

线程优先级

Thread.MAX_PRIORITY 最高优先级 10
Thread.MIN_PRIORITY 最低优先级 1
Thread.NORM_PRIORITY 默认优先级5
Thread.currentThread().getName()     当前线程的名字线程
Thread.currentThread().getPriorty()  获取当前线程的优先级
Thread t=new Thread(new MyRunnable5()); 默认优先级都是5
优先级比较高的只是抢到的cpu时间片相对多一点。
Thread.currentThread().setPriorty(1);设置当前的主线程的优先级为1
​

线程让位yield()

Thread.yield();
public void run(){
    for(int i=0;i<10000;i++){
        if(i%100==0){
            Thread.yield();  //当前线程暂停一下,让给主线程
        }
            System.out.println(Thread.currentThread().getName()+i)
    }
}

线程合并

1630054387683

线程安全(重点)!!

以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。

程序要放在多线程的并发环境下运行,更需要关注的这些数据在多线程并发的环境下是否安全

什么时候数据在多线程并发的环境下会存在安全问题呢? 三个条件: 条件1:多线程并发。条件2:有共享数据。 条件3:共享数据有修改的行为。 满足以上3个条件之后,就会存在线程安全问题。

怎么解决线程安全问题呢? 当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在线程安全问题,怎么解决这个问题? 线程排队执行。(不能并发)。用排队执行解决线程安全问题。这种机制被称为:线同步机制。 怎么解决线程安全问题呀? 使用"线程同步机制"。

专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行怎么解决线程安全问题呀? 使用"线程同步机制”。 线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全第一,只有数据安全了,我们才可以谈效率。

异步编程模型: 线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,谁也不需要等谁,,这种编程模型叫做:异步编程模型。其实就是:多线程并发效率较高。 异步就是并发。 同步编程模型: 线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型。 效率较低。线程排队执行。 同步就是排队。

所以得线程同步,共享同一个资源的时候。

使用线程同步问题,解决线程安全问题

线程同步代码块,后面这个小括号中传的这个数据是相当管家你的。这个数据必须是多线程共享的数据,才能大道多线程排队。

()中的对象,是不是t12345共享的对象,是不是都有.有的话就同步

synchronized(){

}

哪些线程需要排队那么就填那些 多线程共享的资源对象。

1630057586181

1630058434124

1630058483003

1630058917843

在方法体内!

1630059391041

”abc“实际上没有意义,别的线程操作别的账户,完全没有意义,浪费了效率

this的这个对象没上锁,锁的是这个方法!!

1630059858786

1630060256457

1630060561288

synchronized 放在实例方法上锁的一定是this

出现在实例方法上表示整个方法体都需要同步,可能会无故扩大同步范围,导致程序的执行效率降低。所以不常用

如果同步的是整个方法体的时候,建议使用这个

类锁一个类只有一把,用同一个类的时候要等!

静态方法是类锁,类锁不管创建了几个对象,类锁只有一把.

class Myclass

public synchronized static void doSome()

public synchronized static void doOther()

1630078517310

总结

死锁

synchronized在开发中最好不要嵌套使用

1630117349833

1630117270688

聊一聊,我们以后开发中应该怎么解决线程安全问题?
是一上来就选择线程同步吗?synchronized
​
不是,synchronized会让程序的执行效率降低,用户体验不好。系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择线程同步机制。
第一种方案:尽量使用局部变量代替"实例变量和静态变量"。
​
第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,对象不共享,就没有数据安全问题了。
​
第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized了。线程同步机制。
比如秒杀功能!限定数量

守护线程

java语言中线程分为两大类: 一类是:用户线程 一类是:守护线程(后台线程) 其中具有代表性的就是:垃圾回收线程(守护线程)。

守护线程的特点: 一般守护线程是一个死循环,所有的用户线程只要结束守护线程自动结束

注意:主线程main方法是一个用户线程

守护线程用在什么地方呢? 每天00:00的时候系统数据自动备份。 这个需要使用到定时器,并且我们可以将定时器设置为守护线程。 一直在那里看着,没到00:00的时候就备份一次。所有的用户线程 如果结束了,守护线程自动退出,没有必要进行数据备份了。

代码模拟

守护线程需要在main中setDaemon,只要main(用户线程)一结束,守护线程就结束了!!
class BakeDataThread extends Thread{
    public void run(){
        //即使是死循环,当由于该线程是守护者,当用户线程结束,守护线程自动终止.
        int i=0;
        while(true){
            System.out.println(Thread.currentThread().getName()+(++i));
            Thread.sleep(1100);
        }
    }
}

1630130521332

定时器

定时器的作用:(鸡妈后台 间隔特定的时间,执行特定的程序。

每周要进行银行账户的总账操作。 ​ 每天要进行数据的备份操作。

在实际的开发中,每隔多久执行一段特定的程序,这种需求是很常见的, ​ 那么在java中其实可以采用多种方式实现: ​ ​ 可以使用sleep方法,睡眠,设置睡眠时间,没到这个时间点醒来,执行 ​ 任务。这种方式是最原始的定时器。(比较low)

在java的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来 用。 ​ 不过,这种方式在目前的开发中也很少用,因为现在有很多高级框架都是支持 ​ 定时任务的。

在实际的开发中,目前使用较多的是Spring框架中提供的SpringTask框架(底层是定时器),sleep方法太原始了不能当作定时器 ​ 这个框架只要进行简单的配置,就可以完成定时器的任务。

定时任务TimerTask implements Tunnable 是个线程,也是个抽象类,没办法new 只能被子类继承

抽象都可以使用匿名内部类,抽象类 或者接口

Timer timer=new Timer();
​

1630131525425

实现线程的第三种方式FutureTask,实现Callable

这种方式实现的线程可以获取线程的返回值。
        之前讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。
​
        思考:
            系统委派一个线程去执行一个任务,该线程执行完任务之后,可能
            会有一个执行结果,我们怎么能拿到这个执行结果呢?
                使用第三种方式:实现Callable接口方式。
​

效率比较低,但是能拿到线程的执行结果

public class FutureTask<V> implements RunnableFuture<V> 
public interface RunnableFuture<V> extends Runnable, Future<V> 
多态
Runnable ..= FutureTask ..

1630132527811

1630132636281

Object类中的wait和notify方法(生产者和消费者模式)

第一:wait和notify方法不是线程对象的方法,是java中任何一个java对象
		都有的方法,因为这两个方式是Object类中自带的。
			wait方法和notify方法不是通过线程对象调用,
			不是这样的:t.wait(),也不是这样的:t.notify()..不对。
		
		第二:wait()方法作用?
			Object o = new Object();
			o.wait();

			表示:
				让正在o对象上活动的线程进入等待状态,无期限等待,
				直到被唤醒为止。
				o.wait();方法的调用,会让“当前线程(正在o对象上
				活动的线程)”进入等待状态。

		第三:notify()方法作用?
			Object o = new Object();
			o.notify();

			表示:
				唤醒正在o对象上等待的线程。
			
			还有一个notifyAll()方法:
				这个方法是唤醒o对象上处于等待的所有线程。

1630204365535

public class ThreadTest1 {
    public static void main(String[] args) {
        List list=new ArrayList();
        Thread t1=new Thread(new Producer(list));
        Thread t2=new Thread(new Comsumer(list));
        t1.setName("生产者");
        t2.setName("消费者");
        t1.start();
        t2.start();

    }
}
class Producer implements Runnable{
    List list;

    public Producer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        while (true){
            synchronized (list){
                if(list.size()>0){
                    try {
                        list.wait();//如果生产满了,让当前线程等待.释放掉list锁.让消费者线程占锁开始消费!!!
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Object o = new Object();
                list.add(o);
                System.out.println(Thread.currentThread().getName()+o);
                list.notifyAll();//唤醒等待的线程

            }//这个方法体结束才释放锁

        }

    }
}
class Comsumer implements Runnable{
    List list;

    public Comsumer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        while (true){
            synchronized (list){
                if(list.size()==0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Object remove = list.remove(0);
                System.out.println(Thread.currentThread().getName()+remove);
                list.notifyAll();//唤醒等待的
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值