Java多线程处理笔记

news2025/1/4 17:16:50

学习视频:598-JavaSE进阶-多线程概述_哔哩哔哩_bilibili


       目录

多线程概述

进程和线程的关系

 多线程并发的理解

分析程序存在几个线程

 实现线程的第一种方式

实现线程的第二种方式 

 采用匿名内部类的方式

线程生命周期

获取线程的名字

 获取当前线程对象

线程的sleep方法 

sleep方法的相关面试题 

终止线程的睡眠

强制终止线程的执行 

线程调度概述

线程调度的方法

线程让位 

线程合并

线程安全

同步代码块synchronized

哪些变量具有线程安全问题 

synchronized出现在实例方法上

synchronized的三种写法

synchronized相关面试题

 死锁概述

开发中如何解决线程安全问题

守护线程概述

      实现守护线程

实现定时器

实现线程的第三种方式 :实现Callable接口(JDK8新特性)


多线程概述

1.什么是线程? 什么是进程?

进程是一个应用程序(1个进程是一个软件)

线程是一个进程中的执行场景/执行单元

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

2.对于java程序来说,当在DOS命令窗口中输入:

java HelloWorld 回车之后

会先启动JVM,而JVM就是一个进程

JVM再启动一个主线程调用main方法

同时再启动一个垃圾回收线程负责看护,回收垃圾.

最起码,现在的java程序中至少两个线程并发。

一个是垃圾回收线程,一个是执行main方法的主线程.


进程和线程的关系

进程可以看做是现实生活当中的公司,线程可以看做是公司当中的某个员工。

        注意:

                进程A和B的内存独立不共享.

                        永劫无间是一个进程

                        原神是一个进程        

                             这两个进程是独立的,不共享资源

                线程A和B:

                        在java语言中,线程A和线程B,堆内存和方法区内存共享.

                        但是栈内存独立,一个线程一个栈

                假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,互补干扰,各自执行各的,这就是多线程并发.

        java中之所以有多线程机制,目的就是为了提高程序的处理效率.


 多线程并发的理解

什么是真正的多线程并发?

        t1线程执行t1的

        t2线程执行t2的        

        t1不会影响t2,t2也不会影响t1,这叫做真正的多线程并发.

对于多核的CPU电脑来说,真正的多线程并发是没问题的.

        4核CPU表示同一个时间点上,可以真正的有4个进程并发执行

单核的CPU表示只有一个大脑:

        不能够做到真正的多线程并发,但是可以做到给人一种"多线程并发”的感觉.

对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于CPU的处理速度极快,

多个线程直接频繁切换执行,会给人一种错觉:貌似多个事情同时在做


分析程序存在几个线程

package thread1;

public class ThreadTest01 {
    public static void main(String[] args) {
        System.out.println("main begin");
        m1();
        System.out.println("main over");


    }

    private static void m1() {
        System.out.println("m1 begin");
        m2();
        System.out.println("m1 over");
    }
    private static void m2() {
        System.out.println("m2 begin");
        m3();
        System.out.println("m2 over");
    }

    private static void m3() {
        System.out.println("m3 execute!");
    }
}


 实现线程的第一种方式

java支持多线程机制,并且java已经将多线程实现了,我们只需要继承就行了.

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法

package thread1;
/*
    实现线程的第一种方式:
        编写一个类,直接继承java.lang.Thread,重写run方法.
 */
public class ThreadTest02 {
    public static void main(String[] args) {
    //新建一个分支线程对象
        MyThread myThread=new MyThread();
        //启动线程
        //t.run; //不会启动线程,不会分配新的分支栈。(这种方式就是单线程)
        //start()方法的作用:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了,这段代码的任务只是为了
        //开辟一个栈空间,只要新的栈空间开出来了,start()方法就结束了,线程就启动成功了.
        //启动成功的线程会自动调用run方法,并且run方法在分支栈的底部(压栈)
        //run方法在分支栈的栈底部,main方法在主栈的栈底部,run和main是平级的.
        myThread.start();
        //这里的代码还是运行在主线程中
        for(int i=0;i<1000;i++)
        {
            System.out.println("主线程--->"+i);
        }
    }
}


class  MyThread extends Thread{
    @Override
    public void run()
    {
     //编写程序,这段程序运行在分支线程(分支栈)中。
        for(int i=0;i<1000;i++)
        {
            System.out.println("分支线程--->"+i);
        }
    }
}


实现线程的第二种方式 

package thread1;
/*
    实现线程的第二种方式,编写一个类实现java.lang.Runnable接口
 */
public class ThreadTest03 {
    public static void main(String[] args) {
//    //创建一个可运行对象
//        MyRunnable r=new MyRunnable();
//    //将可运行的对象封装成一个线程对象
//        Thread t=new Thread(r);
       Thread t=new Thread(new MyRunnable());//合并代码
    //启动线程
    t.start();
        for(int i=0;i<1000;i++)
        {
            System.out.println("主线程--->"+i);
        }

    }
}

//这并不是一个线程类,是一个可运行的类,它还不是一个线程
class  MyRunnable implements Runnable
{
    @Override
    public void run() {
        for(int i=0;i<1000;i++)
        {
            System.out.println("分支线程--->"+i);
        }
    }
}

注意:第二种方式实现接口比较常用,因为一个类实现了接口还可以继续去继承其他的类,更灵活


 采用匿名内部类的方式

package thread1;

public class ThreadTest04 {
    public static void main(String[] args) {
        //创建线程对象,采用匿名内部类的形式
        //这是通过一个没有名字的类,new 出来的对象
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<1000;i++)
                {
                    System.out.println("t线程--->"+i);
                }
            }
        });
    //启动线程
        t.start();
        for(int i=0;i<1000;i++)
        {
            System.out.println("main线程--->"+i);
        }
    }
}


线程生命周期

        新建状态

        就绪状态

        运行状态

        阻塞状态

        死亡状态


获取线程的名字

 

package thread1;
/*
    1.怎么获取当前线程对象?
    2.获取线程对象的名字
    3.修改线程对象的名字
 */
public class ThreadTest05 {
    public static void main(String[] args) {
        //创建线程对象
        MyThread2 t=new MyThread2();
        //设置线程的名字
//        t.setName("tt");
        //获取线程的名字
      String tname=t.getName();
        System.out.println(tname);//tt

        MyThread2 t2=new MyThread2();
        System.out.println(t2.getName());
        //启动线程
        t.start();

    }
}
class  MyThread2 extends Thread
{
    @Override
    public void run() {
        for (int i=0;i<100;i++)
        {
            System.out.println("分支线程--->"+i);
        }
    }
}


 获取当前线程对象

package thread1;
/*
    1.怎么获取当前线程对象?
    2.获取线程对象的名字
    3.修改线程对象的名字
 */
public class ThreadTest05 {
    public static void main(String[] args) {
        //currentThread就是当前线程对象
        //这个代码出现在main方法当中,所以当前线程就是主线程
        Thread currentThread=Thread.currentThread();
        System.out.println(currentThread);

        //创建线程对象
        MyThread2 t=new MyThread2();
        //设置线程的名字
//        t.setName("tt");
        //获取线程的名字
      String tname=t.getName();
        System.out.println(tname);//tt

        MyThread2 t2=new MyThread2();
        System.out.println(t2.getName());
        //启动线程
        t.start();
        t2.start();
    }
}
class  MyThread2 extends Thread
{
    @Override
    public void run() {
        for (int i=0;i<100;i++)
        {
            //currentThread就是当前线程对象,就是t.start()的t
            Thread currentThread=Thread.currentThread();
            System.out.println(currentThread.getName()+"--->"+i);
        }
    }
}


线程的sleep方法 

package thread1;
/*
关于线程的sleep()方法:
    static void sleep(long millis)
    1.静态方法:Thread.sleep(1000);
    2.参数是毫秒
    3.作用:让当前线程进入休眠,进入"阻塞状态",放弃占有CPU时间片,让给其它线程使用。
        出现在A线程中,A线程就会进入休眠
 */
public class ThreadTest06 {
    public static void main(String[] args) {
    //让当前线程进入休眠5s
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //5秒之后执行这里的代码
        System.out.println("hello,world!");
    }
}

sleep方法的相关面试题 

package thread1;

public class ThreadTest07 {
    public static void main(String[] args) {
    //创建线程对象
        Mythread3 t=new Mythread3();
        t.setName("t");
        t.start();

        //调用sleep方法
        try {
            // 问题 :这行代码会让线程t进入休眠状态吗?
            t.sleep(1000*5);//在执行的时候还是会转换成:Thread.sleep(1000*5)
                                 //这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠
                                 // 这行代码出现在main方法中,main线程睡眠

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //5秒之后这里才会执行
        System.out.println("hello world");

    }
}


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

终止线程的睡眠

package thread1;
/*
    sleep睡眠太久了,如果希望半道上醒来,你应该怎么办?也就是说怎么终止/叫醒一个正在睡眠的线程
    注意:这个不是中断线程的执行,而是中断线程的睡眠
 */
public class ThreadTest08 {
    public static void main(String[] args) {
    Thread t=new Thread(new MyRunnable2());
    t.setName("t");
    t.start();

    //希望5s之后,t线程醒来
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            //打印异常信息
            e.printStackTrace();
        }
    // 中断t线程的睡眠 (中断睡眠的方式依靠了java的异常处理机制)
        t.interrupt();// 干扰,一盆冷水过去!


    }
}
class  MyRunnable2 implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"----> begin");
        try {
            //睡眠一年
            Thread.sleep(1000*60*60*24*365);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"----> end");
    }


}


强制终止线程的执行 

package thread1;

public class ThreadTest10 {
    public static void main(String[] args) {
        MyRunable4 r=new MyRunable4();
    Thread t=new Thread(r);
    t.setName("t");
    t.start();
    //模拟5s
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    //终止线程
    r.run=false;

    }
}

class  MyRunable4 implements Runnable
{
    boolean run=true;//布尔标记

    @Override
    public void run() {
    for(int i=0;i<10;i++) {
        if (run) {
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        }
        else
        {
            //终止当前线程
            return;
        }
    }

    }
}


线程调度概述

1.常见的线程调度模型有哪些?

        抢占式调度模型:

                那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。

                java采用的就是抢占式调度模型。        

        均分式调度模型:

                平均分配CPU时间片,每个线程占有的CPU时间片时间长度一样。平均分配,

             一切平等。

                有一些编程语言,线程调度模型采用的是这种方式。


线程调度的方法

实例方法:

        void setPriority(int newPriority)  设置线程的优先级

        int getPriority()  获取线程的优先级

        最低优先级1

        默认优先级5

        最高优先级10

静态方法:

        static void yield() 让位方法

        暂停当前正在执行的线程对象,并执行其他线程

        yield()方法不是阻塞方法,让当前线程让位,让给其他线程使用.

        yield()方法的执行会让当前线程从"运行状态"回到"就绪状态"

实例方法:

        void join()

        合并线程

        class MyThread1  extends Thread{

        public void doSome()

{

        MyThread2 t=new MtThread2();

        t.join();  // 当前线程进入阻塞,t线程执行,直到t线程结束,当前线程才可以执行

}

}

class MyThread2 extends Thread{

}

package thread1;
/*
关于线程的优先级
 */
public class ThreadTest11 {
    public static void main(String[] args) {
        System.out.println("最高优先级 "+Thread.MAX_PRIORITY);
        System.out.println("最低优先级 "+Thread.MIN_PRIORITY);
        System.out.println("默认优先级 "+Thread.NORM_PRIORITY);

        //获取当前线程对象,获取当前线程的优先级
        Thread currentThread=Thread.currentThread();
        //main默认优先级:5
//        System.out.println(currentThread.getName()+"线程的默认优先级是 :"+currentThread.getPriority());
        Thread t=new Thread(new MyRunnable5());
        t.setPriority(10);
        t.setName("t");
        t.start();
        //优先级较高的,抢到CPU的时间相对多一些(处于运行状态占的时间多一些)
        for (int i=0;i<1000;i++)
        {
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }


    }
}

class  MyRunnable5 implements  Runnable{

    @Override
    public void run() {
        for (int i=0;i<1000;i++) {
            System.out.println(Thread.currentThread().getName() + "---->" + i);
        }

    }
}


线程让位 

package thread1;
/*
让位,当前线程暂停,回到就绪状态,让给其他线程
静态方法:Thread.yield();
 */
public class ThreadTest12 {
    public static void main(String[] args) {
    Thread t=new Thread(new MyRunnable6());
        t.setName("t");
        t.start();
        for (int i=1;i<=10000;i++)
        {
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}
class  MyRunnable6 implements  Runnable{

    @Override
    public void run() {
        for (int i=1;i<=10000;i++)
        {
            if (i%100==0)
            {
                Thread.yield();
            }
            System.out.println(Thread.currentThread().getName()+"---->"+i);
        }
    }
}


线程合并

package thread1;
/*
线程合并:
 */
public class ThreadTest13 {
    public static void main(String[] args) {
        System.out.println("main begin");
        Thread t=new Thread(new MyRunnable7());
        t.setName("t");
        t.start();

        //合并线程
        try {
            t.join();//t 合并到当前线程中,当前线程受阻塞,t线程执行直到结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println("main over");

    }
}

class MyRunnable7 implements  Runnable
{
    @Override
    public void run() {
        for (int i=0;i<100;i++)
        {
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}

 在内存上线程合并并不是意味着两个栈合并了,而是协调关系。

当前线程阻塞,t线程执行,t线程执行完了当前线程才可以执行.


线程安全

什么时候数据在多线程并发的环境下会存在安全问题?

        条件1:多线程并发

        条件2:   有共享数据

        条件3:   共享数据有修改行为

满足上述三个条件后,就会存在线程安全问题

怎么解决线程安全问题?

        线程排队执行(不能并发)(线程同步机制)

线程同步:线程不能并发了,需要排队执行,尽管排队执行会牺牲一部分效率,但是安全第一

异步编程模型:

        线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,谁也不需要等谁,这种编程模型叫做:异步编程模型。其实就是:多线程并发(效率较高)

异步--并发

同步编程模型:

        线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,       必须等待t1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型.效率较低,线程排队

执行。

同步--排队


Account类

package bean;


import static java.lang.Thread.sleep;

/*
银行账户
 */
public class Account {
    private String  actno;//账号

    private  double balance;//余额

    public Account() {
    }

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
    //取款方法
    public  void withdraw(double money)
    {
        //t1和t2并发这个方法,(t1和t2是两个栈,两个栈操作堆中同一个对象)
        //取款之前的余额
        double before=this.getBalance();
        //取款之后的余额
        double after=before-money;
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //更新余额
        this.setBalance(after);

    }

}

 AccountThread类
 

package bean;

public class AccountThread extends  Thread{
    //两个线程必须共享同一个账户对象
    private Account act;

    //通过构造方法传递过来账户对象
    public AccountThread(Account act) {
        this.act=act;
    }

    public void run()
    {
        // run方法的执行表示取款操作
        //假设取款5000
        double money=5000;
        //取款
        act.withdraw(money);
        System.out.println(Thread.currentThread().getName()+"对账户"+act.getActno()+"取款成功,余额"+act.getBalance());
    }

}

测试类

package bean;

public class ThreadTest14 {
    public static void main(String[] args) {
        // 创建账户对象(只创建1个)
        Account act=new Account("act-01",10000);
        // 创建2个线程
        Thread t1=new AccountThread(act);
        Thread t2=new AccountThread(act);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();


    }
}

存在问题,2个人都取5000,余额应该为0;


同步代码块synchronized

 

import static java.lang.Thread.sleep;

/*
银行账户
 */
public class Account {
    private String  actno;//账号

    private  double balance;//余额

    public Account() {
    }

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
    //取款方法
    public  void withdraw(double money)
    {
        // 以下这几行代码必须是线程排队的,不能并发
        // 一个线程把这里的代码全部执行结束后,另一个线程才能进来
        /*
            线程同步机制的语法:
                synchronized()
                {
                //线程同步代码块
                }
                synchronized后面小括号中传的这个"数据"是相当关键的.
                这个数据必须是多线程共享的数据,才能达到多线程排队
                ()中写什么?
                    那要看你想让哪些线程同步
                    假设t1、t2、t3、t4、t5 5个线程
                    你只希望t1 t2 t3排队, t4 t5不需要排队。怎么办?
                    你一定要在()中写一个t1 t2 t3共享的对象。而这个对象对于 t4 t5 来说不是共享的。

            这里的共享对象是: 账户对象
            账户对象是共享的,那么this 就是账户对象吧!!!
            不一定是this,这里这要是多线程共享的那个对象就行.

            在java语言中,任何一个对象都有"一把锁",其实这把锁就是标记(只是把它叫做锁。)
            100个对象100把锁,1个对象一把。
            一下代码的执行原理:
                1.假设t1和t2并发,开始执行以下操作和时候,肯定有一个先有一个后
                2.假设t1先执行了,遇到了synchronized,这个时候自动找"后面共享对象“的对象锁,
                找到之后,并占有这把锁,然后执行同步代码块中的程序,在程序执行过程中一直都是占有这把锁的,直到
                同步代码块中的代码结束,这把锁才会释放
                3.假设t1已经占有这把锁,此时t2也遇到synchronized关键字,也会取占有后面共享对象的这把锁,结果这把锁被t1占有,t2只能在
                同步代码块外面等待t1的结束,直到t1把同步代码块执行结束了,t1会归还这把锁,此时t2终于等到这把锁,然后t2占有这把锁之后进入
                同步代码块执行,这样就达到了线程排队执行.
                    注意:共享对象一定要选好!这个共享对象一定是你需要排队执行的这些线程对象所共享的。

         */
        synchronized (this) {
            double before = this.getBalance();
            //取款之后的余额
            double after = before - money;
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //更新余额
            this.setBalance(after);
        }

    }

}

synchronized()括号里面只要是t1和t2共享的对象就行,比如 括号里填obj也可以

当然,若是填"abc",则所有的对象都共享了,一个人取款,要让全天下的人都等待,应该让机制变成只有对应账户A的人取款才需要等待正在取款账户A的人操作完毕再取款。


哪些变量具有线程安全问题 

 java中有三大变量

实例变量:在堆中

静态变量: 在方法区

局部变量: 在栈中

以上三大变量中:                

        局部变量永远都不会存在线程安全问题

        因为局部变量不共享(一个线程一个栈)

 实例变量在堆中,堆只有一个.

静态变量在方法区,方法区只有1个。

堆和方法区都是多线程共享的,所以可能存在线程安全问题。

局部变量+常量:不会有线程安全问题。

成员变量:可能会有线程安全问题。

如果使用局部变量的话,建议使用:StringBuilder.

因为局部变量不存在线程安全问题,选择StringBuilder,StringBuffer效率比较低。        


synchronized出现在实例方法上

这个时候synchronized锁的一定是this 

如果共享的对象是this,并且需要同步的代码块是整个方法体,

建议使用这种方式


synchronized的三种写法

第一种:同步代码块

        灵活

        synchronized(线程共享对象)

        {

        同步代码块;

        }

第二种:在实例方法上使用synchronized

        表示共享对象一定是this

        并且同步代码块是整个方在法体

第三种:在静态方法上使用synchronized

        表示找类锁

        类锁永远只有一把

        就算创建了100个对象,类锁也只有一把。

对象锁:1个对象一把


synchronized相关面试题

package bean;
/*
doOther()方法的执行需要等待doSome()方法的结束吗?
    不需要,它不是synchronized修饰的
 */
public class Exaxm01 {
    public static void main(String[] args) {
    Myclass mc=new Myclass();
    Thread t1=new MyThread(mc);
    Thread t2=new MyThread(mc);
    t1.setName("t1");
    t2.setName("t2");

    t1.start();
        try {
            Thread.sleep(1000);//这个睡眠的作用是为了保证t1先执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();



    }
}
class MyThread extends Thread{
    private Myclass mc;
    public MyThread(Myclass mc)
    {
        this.mc=mc;
    }

    @Override
    public void run() {
    if (Thread.currentThread().getName().equals("t1"))
    {
        mc.doSome();
    }
    if (Thread.currentThread().getName().equals("t2"))
    {
        mc.doOther();
    }

    }
}

class Myclass{
    public synchronized void doSome()
    {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");

    }
    public void doOther()
    {
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

doOther()方法前面加上 synchronized  需要等待

 new两个Myclass对象(两把锁)不需要等待

 synchronizedc搞静态方法(类锁)需要等待

 


 死锁概述

 

public class DeadLock {
    public static void main(String[] args) {
    Object o1=new Object();
    Object o2=new Object();
    //t1和t2两个线程共享o1,o2
        Thread t1=new MyThread1(o1,o2);
        Thread t2=new MyThread2(o1,o2);
    t1.start();
    t2.start();

    }
}
class  MyThread1 extends Thread{
    Object o1;
    Object o2;
    public MyThread1(Object o1,Object o2)
    {
        this.o1=o1;
        this.o2=o2;
    }

    @Override
    public void run() {
        synchronized (o1)
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2)
            {

            }
        }


    }
}

class MyThread2 extends  Thread{
    Object o1;
    Object o2;
    public MyThread2(Object o1,Object o2)
    {
        this.o1=o1;
        this.o2=o2  ;
    }

    @Override
    public void run() {
        synchronized (o2)
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1)
            {

            }
        }
    }
}

发生死锁,10年后还是这样


开发中如何解决线程安全问题

一上来就用synchronized让线程同步吗?

     不是,synchronized会让程序的执行效率降低,用户体验不好,系统的用户吞吐量降低,用户体验差,在不得已的情况下再选择线程同步机制。

第一种方案:尽量使用 局部变量 代替 实例变量 静态变量

第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了.

(一个线程对应1个对象,100个线程对应100个对象,对象不共享,就没有数据安全问题了)

第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候只能选择synchronized,线程同步机制。


守护线程概述

        java语言中线程分为两大类:

        一类是:用户线程

        一类是:守护线程(后台线程)

其中具有代表性的就是:垃圾回收线程(守护线程)

        守护线程的特点:

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

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

        守护线程用在什么地方?

                每天00:00的时候系统数据自动备份

                这个需要使用到定时器,并且我们可以将定时器设置为守护线程

                一直在那里看着,每次到00:00的时候就备份一次。所有的用户线程如果结束了,守护线程自动退出,没有必要进行数据备份了。

      实现守护线程

package bean;

public class ThreadTest15 {
    public static void main(String[] args) {
        Thread t=new BakDataThread();
        t.setName("备份数据的线程");
        //启动线程之前,将线程设置为守护线程
        t.setDaemon(true);
        t.start();
        //主线程:主线程是用户线程
        for(int i=0;i<10;i++)
        {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }
}

class  BakDataThread extends Thread{
    @Override
    public void run() {
        int i=0;
        while (true)
        {
            System.out.println(Thread.currentThread().getName()+"--->"+(++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 主线程结束后,备份数据的线程也跟着结束


实现定时器

定时器的作用:间隔特定的时间,执行特定的程序。


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest {
    public static void main(String[] args) throws Exception{
        //创建定时器对象

        Timer timer=new Timer();
        //Timer timer=new Timer(true);//守护线程方式

        //指定定时任务
        //timer.schedule(定时任务,第一次执行时间,间隔多久执行一次)
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      Date firstime=sdf.parse("2022-12-10 00:16:00");
        timer.schedule(new logTimerTask(),firstime,1000*10);


    }
}
class  logTimerTask extends TimerTask{
    @Override
    public void run() {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strtime=sdf.format(new Date());
        System.out.println(strtime+":成功完成了一次数据备份 ");

    }
}


实现线程的第三种方式 :实现Callable接口(JDK8新特性)

前面两种方式是无法获取线程返回值的,因为run()方法返回void。

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class ThreadTest16 {
    public static void main(String[] args) throws Exception{
    //第一步:创建一个"未来任务类"对象
        FutureTask task=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {//call()方法相当于run()方法.只不过这个有返回值
                // 线程执行一个任务,执行之后可能会有一个执行结果
                // 模拟执行
                System.out.println("call method begin");
                Thread.sleep(1000*10);
                System.out.println("call method end");
                int a=100;
                int b=200;
                    return a+b;//自动装箱(300变成Integer)
            }
        });
        //创建线程对象
    Thread t=new Thread(task);

    //启动线程
        t.start();

        //怎么在主线程中获取t线程的返回结果
        Object obj=task.get();
        System.out.println("线程执行结果:"+obj);

        //main方法这里的程序要想执行,必须等待get()方法的结束
        //而get()方法可能需要很久,因为get()方法是为了拿另一个线程的执行结果
        //另一个线程执行是需要时间的.
        System.out.println("hello world!");


    }
}

 还是两个栈,java虚拟机调度

这种方式的优点:可以获取线程的执行结果

这种方式的缺点:   效率比较低,在获取t线程执行结果的时候,当前线程受阻塞,效率较低.


以上是今天的视频笔记。学习如逆水行舟,不进则退

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/76958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cloud Stream 结合rocketmq

Spring Cloud Stream 结合rocketmq 官方网址&#xff1a;https://github.com/alibaba/spring-cloud-alibaba/wiki/RocketMQ 你可以在这个地址上下载到相关示例项目&#xff0c;配置项等相关信息 spring-cloud-stream 文档&#xff08;这个地址似乎只有集合kafaka和rabbit的示…

Akka 学习(六)Actor的监督机制

目录一 监督机制1.1 错误隔离性1.2 系统冗余性1.3 Actor的监督1.3.1 监督体系1.3.2 理解1,3.3 监督策越一 监督机制 1.1 错误隔离性 在学习Akka如何对失败情况进行响应之前&#xff0c;先了解一些在分布式应用程序中都应该遵循的通用策略&#xff1a;隔离错误。假设每个组件都…

【刷题-数组篇】狂刷力扣三十题,“数组”嘎嘎乱写 | 2022 12-5到12-9

前言 &#xff08;12月5日&#xff09;突然想起了很久以前别人&#xff08;具体来源已经记不清了&#xff09;传给我的一套题单。网上的题单不少&#xff0c;光收藏可不行&#xff0c;关键还得下手。 这套题单的题目数量为300出头&#xff0c;什么时候刷完我还没有明确计划&a…

web前端大作业 (仿英雄联盟网站制作HTML+CSS+JavaScript) 学生dreamweaver网页设计作业

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

2.IOC之xml配置

1.使用IDEA创建工程 2.引入项目使用的依赖 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.2.RELEASE</version></dependency> </depe…

英文外链代发怎么做有效果?英文外链购买平台

英文外链代发怎么做有效果&#xff1f; 答案是&#xff1a;选择权重较好的GPB外链 我们首先要知道一个观点&#xff0c;什么样的外链才有效果&#xff1f; 1.英文外链网站的有一定的权重&#xff0c;可高可低&#xff0c;但一定要有权重&#xff0c;数值指标可以参考MOZ的Do…

10.AOP之xml配置

1.使用IDEA创建工程 2.引入项目使用的依赖 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.2.RELEASE</version></dependency><depend…

HPPH偶联无机纳米材料/白蛋白/白蛋白纳米粒/抗体/量子点/活性基团/荧光/细胞膜的研究

小编分享了HPPH偶联无机纳米材料/白蛋白/白蛋白纳米粒/抗体/量子点/活性基团/荧光/细胞膜的研究知识&#xff0c;一起来看&#xff01; HPPH偶联无机纳米材料/白蛋白纳米粒的研究&#xff1a; HPPH 具有的光动力活性的作用光谱以及靶向性&#xff0c;对组织的穿透率&#xff0…

Android基础学习(十九)—— 进程与线程

1、进程 程序和进程的区别&#xff1a;&#xff08;1&#xff09;程序是静态的&#xff0c;就是存放在磁盘里的可执行文件&#xff0c;就是一系列的指令集合&#xff1b;&#xff08;2&#xff09;进程是动态的&#xff0c;是程序的一次执行过程&#xff0c;同一程序多次执行会…

物联网开发笔记(58)- 使用Micropython开发ESP32开发板之控制2.90寸电子墨水屏模块

一、目的 这一节我们学习如何使用我们的ESP32开发板来控制2.90寸电子墨水屏模块。 二、环境 ESP32 2.90寸 电子墨水屏模块 Thonny IDE 几根杜邦线 接线方法&#xff1a; 三、墨水屏驱动 此处注意注意&#xff1a;不同的型号、不同厂家的墨水屏驱动方式有些不同&#xff0c;…

VIIF:自监督:自适应:GAN

Self-supervised feature adaption for infrared and visible image fusion &#xff08;红外和可见光图像融合的自监督特征自适应&#xff09; 总述&#xff1a;首先&#xff0c;我们采用编码器网络来提取自适应特征。然后&#xff0c;利用两个具有注意机制块的解码器以自我…

【扫描PDF】如何将颜色淡的扫描PDF颜色变深,便于阅读??PDF中文字太淡怎么加深?汇总网上已有的方法,一波小结

一、问题背景 如果你扫描得到的PDF&#xff0c;像下图一样文字颜色非常淡&#xff0c;看起来不舒服&#xff0c;需要加深处理&#xff0c;就烦请看我下面的几个解决方法&#xff0c;都是从网上汇总得到&#xff0c;加上自己的实践和体会总结。 二、Adobe Acrobat DC PDF扫描…

20221209英语学习

今日新词&#xff1a; receiver n.收受者; 收件人; 接待者; (电话)听筒, 耳机; 收音机; (电视)接收机; 接收器; 接球手 annoy n.同“annoyance” delight n.快乐&#xff0c;愉快 railroad n.铁路, 铁道, 铁路公司, 铁路系统 brilliance n.光辉, 【光】辉度, 漂亮, (名声)…

3.IOC之注解配置

1.编写Spring框架核心配置文件applicationContext.xml 在项目目录“/src/main/resources”下新建applicationContext.xml文件&#xff0c;具体代码如下。 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework…

Google如何增加外链?谷歌外链自动化靠谱吗?

Google如何增加外链&#xff1f; 答案是&#xff1a;循序渐进增加免费开放性注册的外链和GPB外链 我们在发布Google外链的时候&#xff0c;总想找捷径&#xff0c;通过软件工具自动发布外链来提高网站排名和流量&#xff0c;加快SEO优化进度&#xff0c;缩短时间成本&#xf…

ChatGPT:构建与人类聊天一样自然的机器人

ChatGPT&#xff1a;构建与人类聊天一样自然的机器人 —— ChatGPT 文章目录ChatGPT&#xff1a;构建与人类聊天一样自然的机器人 —— ChatGPT1 官网2 注册OpenAI账号3 使用ChatGPT3.1 普通聊天3.2 生成代码3.3 写诗3.4 解一道算法题4 ChatGPT中文版VsCode 插件5 一些体会Hi&a…

浅析即时通讯开发之RTMP数据传输协议的实时流媒体

近年来,随着网络带宽的提升,以及多媒体压缩编码技术的发展,流媒体技术得到了非常广泛的应用。全球的流媒体市场正在以极高的速度向前发展,并逐步取代了以文本和图片为主的传统互联网。根据Cisco的VisualNetworkingIndex(VNI)统计,2005年流媒体流量仅占全球互联网总流量的5%,而到…

【玩转c++】c++模板和泛型编程

本期主题&#xff1a;c模板和泛型编程博客主页&#xff1a;小峰同学分享小编的在Linux中学习到的知识和遇到的问题小编的能力有限&#xff0c;出现错误希望大家不吝赐身为程序员&#xff0c;不会有人没女朋友吧&#xff01;&#xff01; 目录 &#x1f341;1.泛型编程 &#x…

ChatGPT 是何方神圣?为什么这么猛?

哈喽&#xff0c;大家好&#xff0c;我是木易巷&#xff01; 本篇文章给大家介绍一下这个很猛的玩意&#xff1a;ChatGPT &#xff01;&#xff01;&#xff01; 什么是ChatGPT &#xff1f; 在12月初&#xff0c;人工智能实验室OpenAI发布了一款名为ChatGPT的自然语言生成式…

【Pytorch】第 5 章 :解决多臂老虎机问题

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…