java多线程(初级知识)

JavaThread

概述

Thread,英文单词,名词、动词,作名词时意思是“线;螺纹;思路;衣服;线状物;玻璃纤维;路线”,java多线程。

进程是系统运行程序的基本单位,线程是比进程更小、更轻量级的执行单位,每个进程都拥有自己的一块内存空间和变量资源等,然而同一个进程下的多个线程则共享数据和资源,所以不管线程的创建和销毁工作,还是在线程之间切换工作,都要比进程更加轻量级、消耗系统资源更少。

多线程在多个cpu的机器上可以实现。如果只是单cpu,只是让我们看起来是多线程的。实际上运行的是靠cup的调度。而不是同时去执行的。

在jvm中 每一个线程都有自己的java虚拟机栈,本地方法栈,程序计数器。而对应的所有线程共享堆内存和元空间(以前版本也叫方法区)。与上一段文字进程和线程的关系对应。

创建多线程

  • 声明一个Thread类的子类,子类中重写Thread类的run方法。Thread也实现了runnable接口

  • 声明一个实现Runnable接口的类,类中实现run方法。

  • 声明一个实现Callable接口的类(在初级阶段不需要太了解)

    推荐使用runnable接口

    我认为最主要的原因是 使用runnable接口 方便的对同一个资源进行操作。Runnable可以实现多个相同的程序代码的线程去共享同一个资源 Thread类也可以,只是不适合(相当于没做)。当以Thread方式去实现资源共享时,实际上源码内部是将thread向下转型为了Runnable,实际上内部依然是以Runnable形式去实现的资源共享 参考

    1. Thread oop(面向对象)的单继承局限性,不建议使用。

      Runnable接口,避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。

    2. 该类继承了Thread类,就不能继承其他类了,不满足实际的需求

    3. 法一把线程和任务合并在了一起,法二分开了

    4. Runnable更容易与线程池等高级API配合,建议操作任务对象,不建议直接操作线程对象。

    5. Runnable让任务脱离了Thread继承体系,更灵活。Java中,组合优于继承,Runnable和Thread的关系是聚合关系(has-a),而法一是继承关系(is-a)。
      tip:聚合关系就是A类的属性中有B类

重写run方法 调用start方法启动线程,但不是立即执行的。等待cpu调度。

public class ThreadTest1 extends Thread {
    public static void main(String[] args) {
        ThreadTest1 t = new ThreadTest1();
        t.start();
    }

    @Override
    public void run() {
        System.out.println("线程run");
    }
}
public class ThreadTest2 implements Runnable{
    public static void main(String[] args) {
        //Runable是函数式接口,可以用Lambda表达式
        //实现类 放到了Thread中
        Thread thread = new Thread(new ThreadTest2());
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("线程run");
    }
}

Callable的好处:

  1. 可以定义返回值
  2. 可以抛出一个异常
public class CallableTest implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        System.out.println("call方法正在执行");
        //写死了
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTest c = new CallableTest();
        //创建执行服务 创建了一个池子
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> result = executorService.submit(c);
        //获取结果
        Boolean aBoolean = result.get();
        System.out.println(aBoolean);
        //关闭服务
        executorService.shutdown();


    }
}

静态代理

image-20220924201259595

image-20220924201126111

image-20220924210105903

代理类和目标类实现同一个接口,然后在代理类里面注入目标类,在代理类重写的方法里面通过注入的目标类调用目标类的同名方法,在这个方法执行前后添加一些逻辑处理。

目标类只需要专注于自己的事情就可以了

Thread thread = new Thread(() -> System.out.println("线程run"));
thread.start();//线程run

说了这么多, Thread对Runnable target 就是进行了一种静态代理

线程状态和常用方法

http://static.cyblogs.com/20181120173640764.jpg

源码:

public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}

参考地址

参考地址

几个方法的比较

1、Thread.sleep(long millis),线程休眠。一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。

2、Thread.yield(),线程礼让。一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间,也不一定会让其他线程先跑,看cpu调度的心情。就是回去重新竞争。 也可能是礼让没有成功但是时间片用完了

3、thread.join()/thread.join(long millis), 作用就是让其他线程变为等待,直到调用join()方法的线程执行完毕。当前线程里调用其它线程t的wait()方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程t不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程t一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。

4、obj.wait(),当前线程调用对象 的wait()方法,当前线程释放对象锁进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。

5、obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

6、LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。

  • 等待队列里许许多多的线程都wait()在一个对象上,此时某一线程调用了对象的notify()方法,那唤醒的到底是哪个线程?随机?队列FIFO?or sth else?Java文档就简单的写了句:选择是任意性的(The choice is arbitrary and occurs at the discretion of the implementation)。

image-20220924221357148

public class StateTest {
    //查看线程的状态
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("ooooo"+i);
            }
        });
        Thread.State state = thread.getState();
        System.out.println(state); //new

        thread.start();

        state = thread.getState();
        System.out.println(state);//run

        //只要线程没终止 就输出它的状态
        while (state!=Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
        }

    }

}

线程的优先级

thread.setPriority() 方法 从低到高 1到10

先设置优先级再启动

所有线程默认优先级是5,我测试守护线程的默认优先级也是5

优先级并不是一定的,也要看cpu具体的调度

守护线程

线程分为用户线程守护线程
虚拟机必须确保用户线程执行完毕。当然主线程也是用户线程
虚拟机不用等待守护线程执行完毕如,后台记录操作日志,监控内存垃圾回收等待…
当进程中不存在非守护线程了,则守护线程自动销毁

例:人生不过三万天,且行且珍惜。

public class TestDaemon {
    public static void main(String[] args) {
        You you = new You();
        God god = new God();

        Thread godThread = new Thread(god);
        godThread.setDaemon(true); //默认是false
        godThread.start();

        Thread youThread = new Thread(you);
        youThread.start();
    }

}
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("上帝保佑你");
        }
    }
}
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你开心的第"+i+"天");
        }
        System.out.println("====goodbye! world!===");
    }
}

线程同步

同一个资源,多个人都想使用。属于并发 并发指的是多个任务交替进行,而并行则是指真正意义上的“同时进行”。并发程序之间有相互制约的关系。直接制约体现为一个程序需要另一个程 序的计算结果;间接体现为多个程序竞争共享资源,如处理器、缓冲区等。

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象.这时候我们就需要线程同步.线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用

为了保证数据在方法中被访问时的正确性,在访问时加入锁机制
synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可.存在以下问题:

  • 一个线程持有锁会导致其他所有需要此锁的线程挂起;
  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
  • 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题.

方法里面需要修改的内容才需要锁,
锁的太多,浪费资源

参考

常见问题

Runnable为什么不可以直接run

start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程

2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的 相当于没用多线程,在jvm中还是同一个方法栈

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值