JAVA-多线程2

一.JAVA中Thread类创建线程的方式

1.1 创建一个类,通过继承(class)Thread,重写run方法

package thread;
 
class MyThread extends Thread{
    @Override
    public void run() {
        while(true) {
            System.out.println("hello world");
            try {
                //这里只能try catch,不能throws
                //此处是方法重写,对于父类的run方法;来说,就没有throw xxx 异常这样的设定
                //在重写的时候,也就不能throws异常了
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
 
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread= new MyThread();
        myThread.start();
        while(true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

1.2  创建一个类,实现(实现interface)Runnable,重写run方法

package thread;
 
class MyRunnable implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
 
//使用Runna的方式创建线程
public class Demo2 {
    public static void main(String[] args) {
        MyRunnable runnable=new MyRunnable();
        Thread t=new Thread(runnable);
        //将runnab作为构造方法的参数传递到Thread的构造方法里
        t.start();
 
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

1.3 继承Thread,重写run,属于匿名内部类

package thread;
//通过匿名内部类,创建线程
public class Demo3 {
    public static void main(String[] args) {
        Thread t= new Thread(){
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t.start();
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

针对上述代码中run()这个构造方法的理解:

        1)创建一个子类,这个子类继承自Thread,但是这个子类,是没有名字的(匿名),另一方面,这个类的创建,是在Demo3这个类里面

        2)在子类中重写了run这个方法

        3)创建了该子类的实例,并且使用了t这个引用来指向


1.4 实现Runnable,重写run,属于匿名内部类

匿名内部类,指优先级队列,指定比较规则

package thread;

public class Demo4 {
    public static void main(String[] args) {
        Thread t= new Thread(new Runnable(){
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();
    }
}

new Runnable(),此处是创建Runnable的子类,通过构造方法传给Thread


1.5 使用lambda表达式

package thread;
//使用lambda表达式
public class Demo5 {
    public static void main(String[] args) {
        Thread t= new Thread(() ->{
           while(true){
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        t.start();
    }
}

lambda表达式,本质上就是一个"匿名函数"

这样的匿名函数,主要就可以用来回调函数来使用

经常会用到回调函数的场景: 

        1.服务器开发:服务器收到一个请求,触发一个对应的回调函数

        2.图形界面开发:用户的某个操作,触发一个对应的回调


二. Thread类及常见方法

2.1 Thread类和常见构造方法

方法说明
 Thread创建线程对象
 Thread(Runnable target)使用Runnable对象创建线程对象
3   Thread(String name)创建线程对象,并命名
4   Thread(Runnable target,String name)使用Runnable对象创建线程和对象
 Thread(ThreadGroup,Runnable target)线程可以用来分组管理,分好的组即为线程组,了解即可
  • 例1:Thread t1=new Thread();
  • 例2:Thread t2=new Thread(new MyRunnable());
  • 例3:Thread t3=new Thread("这是我的名字");
  • 例4:Thread t4=new Thread(new MyRunnable(),"这是我的名字");

2.1 方法3的实例

package thread;

public class Demo6 {
    public static void main(String[] args) {
        Thread t= new Thread(() ->{
           while(true){
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        },"myThread");
        t.start();
    }
}

 2.2 Thread的几个常见属性

属性获取方式
1  IDgetId()
名称getName()
3  状态getState()
优先级getPriority()
5  是否后台进程isDaemon()
是否存活isAlive()
7  是否被中断isInterupted()

2.2 属性1

        线程的标识(在JVM这里给线程设定的标识)

        类似于一个人可以有多个名字,一个线程也可以有好多个名字

  1. JVM有一个身份标识
  2. pthread库(系统给程序员提供的操作线程的api,也有一个线程的身份标识)
  3. 内核里,针对线程的pcb还有身份标识

        以上三点都是相互独立


 2.2 属性3和4

        java中的线程状态和操作系统中有一定从差异的

        设置/获取优先级,作用不是很大

        线程的调度,主要还是系统内核来负责

        系统调度的速度实在是太快了


2.2 属性5

        后台线程(守护线程),后台线程不影响进程结束

        前台线程,前台线程会影响到进程结束,如果前台没执行完,进程是不会结束的

        一个进程中所有的前台线程都执行完,退出了,此时即使存在后台线程仍然没执行完,也会随着进程一起结束

        创建的线程,默认是前台线程,通过setDaemon显示的设置成后台

package thread;

public class Demo7 {
    public static void main(String[] args) {
        Thread t=new Thread(() ->{
           while(true){
               System.out.println("hello thread");
           }
        });
        //设置成后台
        t.setDaemon(true);
        t.start();
        //使进程展现3秒钟,可以让进程结束得慢一点
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.2 属性6

        Thread对象,对应的线程(系统内核中)是否存活

        Thread对象的生命周期,并不是和系统中的线程完全一致

        一般就是Thread对象,先创建好,手动调用start,内核才真正创建出现线程

        消亡的时候,可能是thread对象,先结束了生命周期(没有指向这个对象),也可能是thread对象还在,内核中的线程把run执行完了,就结束了


2.3 启动一个线程-start()

在系统中,真正创建出线程(1和2在系统内核完成)

  1. 创建出pcb 
  2. 把pcb加入到对应链接
package thread;

public class Demo8 {
    public static boolean isQuit=false;
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(() ->{
            while(!isQuit){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        //主线程这里执行一些其他逻辑之后,要让线程结束
        Thread.sleep(3000);
        //这个代码就是在修改前面的设定的标志位
        isQuit=true;
        System.out.println("把t线程终止");
    }
}

1)程序员手动设置标志位,通过这个手动设置的标志位,来让run尽快结束

问题:为社么isQuit写作成员变量,就能被访问到,写作局部变量就会报错,是否有别的方法让局部变量不报错

        lambda可以捕获到外面的变量

        既然lambda表达式,执行时机是更靠后的

        这就导致后续真正执行lambda的时候,局部变量isQuit是否已经被销毁

        这种情况是客观存在的,让lambda去访问一个已经被销毁的变量很明显是不合适的

        lambda引入了“变量捕获”的机制

        lambda内部看起来是在直接访问外部的变量,其实本质上是把内部的变量复制了一份,到lambda里面(这样可以解决生命周期的问题)

        但是,变量捕获这里有个限制

        要求捕获的是final(至少看起来是final)

        boolea isQuit =false;修改为final boolea isQuit =false;则不会报错

package thread;

public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        final boolean isQuit=false;
        Thread t=new Thread(() ->{
            while(!isQuit){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        System.out.println("把t线程终止");
    }
}

2)直接Thread类,给我们提供好了现成的标志位,不用手动去设置标志位

package thread;

public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(() -> {
           while (!Thread.currentThread().isInterrupted()){
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
                   break;
               }
           }
        });
        t.start();
        Thread.sleep(3000);
        //把上述的标志位设置为true
        t.interrupt();
    }
}

break

表示遇到错误直接跳出循环

Thread.currentThread()

获取到当前线程对象(Thread)

Thread提供了静态方法,currentThread

在哪个线程调用这个方法,就能够获取到哪个线程的引用 

isInterrupted

Thread对象内部提供了一个标志位(boolea)

true:线程应该要结束        false:线程不必结束 


2.4 等待一个线程 .join()

        多个线程是并发的,具体的执行过程 ,都是由操作系统进行调度的

        操作系统的调度线程的过程,是“随机的”,无法确定线程执行的先后顺序

        上述随机性,程序员不太喜欢,更喜欢的是“确定”的东西

        等待线程,就是一种规划 线程结束 顺序的手段

        举个例子:

        A、B两个线程,希望B先结束,A后结束,此时就可以让A线程中调用 B.join() 的方法

        此时,B线程还未执行完,A线程就会进入“阻塞”状态,就相当于给B留下了执行的时间,B执行完毕后,A再从阻塞状态中恢复过来,并且继续往后执行

        如果A执行到B.join()的时候,B已经执行完了,A就不必阻塞了,直接往下执行就行了

阻塞:让代码暂时不继续执行了(该线程暂时不去cpu上参与调度)

 sleep也能让线程阻塞,阻塞是有时间限制的

join的阻塞,则是“死等”、“不见不散” 


package thread;

public class Demo10 {
    public static void main(String[] args) {
        Thread b=new Thread(() ->{
           for(int i=0;i<5;i++){
               System.out.println("hello b");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
            System.out.println("b结束了");
        });
        Thread a=new Thread(() ->{
           for(int i=0;i<3;i++){
               System.out.println("hello a");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }

            System.out.println("a结束了");
        });
        b.start();
        a.start();
    }
}

 

 a先结束,b后结束


package thread;

public class Demo10 {
    public static void main(String[] args) {
        Thread b=new Thread(() ->{
           for(int i=0;i<5;i++){
               System.out.println("hello b");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
            System.out.println("b结束了");
        });
        Thread a=new Thread(() ->{
           for(int i=0;i<3;i++){
               System.out.println("hello a");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
            try {
            //如果此时还未执行完毕,b.jion就会产生阻塞的情况
                b.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("a结束了");
        });
        b.start();
        a.start();
    }
}

 加上b.join()时,则是b先结束,a后结束

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值