每日一练进击大厂【DAY4】并发编程1

78 阅读4分钟

一、线程和进程的区别

进程是操作系统分配资源的最小单元;线程是CPU的最小执行单元,也是进程中的一个顺序执行流程。 一个程序至少有一个进程,一个进程至少有一个线程。

二、并发和并行

并发:同一时间段上,同时执行,可以单核CPU来完成。 并行:同一时间点上,同时执行,必须多核CPU才能完成。

三、线程的创建方式

1、实现Runnable接口,重写run方法

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

public class TestRunnable {
    public static void main(String[] args) {
        //创建MyRunnable对象,表示线程要执行的功能
        MyRunnable runnable = new MyRunnable();
        //创建线程
        Thread thread = new Thread(runnable, "我的线程1");
        //启动线程
        thread.start();

        for (int i = 0; i < 50; i++) {
            System.out.println("main--------" + i);
        }

    }

}

2、继承Thread类,重写run方法

public class MyThreadDemo1 extends Thread{

    @Override
    public void run() {

    }
    public static void main(String[] args) {
        MyThreadDemo1 myThreadDemo1 = new MyThreadDemo1();
        Thread thread = new Thread(myThreadDemo1);
        thread.start();
    }
}

3、实现Callable接口,重写call方法

public class MyThreadCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        return 1+1;
    }
    
    public static void main(String[] args) {
        MyThreadCallable myThreadCallable = new MyThreadCallable();
        //把callable对象转成可执行任务
        FutureTask<Integer> task = new FutureTask<>(myThreadCallable);
        Thread thread = new Thread(task);
        thread.start();
        try {
            System.out.println(task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

四、线程的安全问题

  1. 原子性问题:当一个线程执行一系列程序指令操作的时候,它应该是不可中断的,一旦中断,在多线程的视角来看,这一系列的程序指令会出现前后执行结果不一致的问题。
  2. 可见性问题:多线程环境下,由于读和写是发生在不同的线程里面,有可能出现某个线程共享变量的修改,对其他线程不是实时可见的。
  3. 有序性问题:程序编写的指令顺序和最终CPU运行的指令顺序可能出现不一致的现象,这种现象称为指令重排序,有序性也会导致可见性问题。

五、线程的生命周期

  1. new:初始化状态
  2. runnable:运行状态
  3. waiting:阻塞状态(无限期等待)
  4. timed_waiting:阻塞状态(限期等待)
  5. blocked:阻塞状态,只有在遇到synchronized锁的时候,且没有抢占到锁,线程才会进入该状态。
  6. terminated:终止状态

六、start()方法和run()方法的区别

只有调用了start()方法,才会启动线程,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行,如果只是调用了run()方法,那么代码还是同步执行,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。

七、Runnable接口和Callable接口的区别

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹的去执行run()方法中的逻辑代码而已。 Callable接口中的call()方法是有返回值的,是一个泛型,Future、FutureTask配合可以用来获取异步执行的结果。

八、sleep()方法和wait()方法区别

sleep()方法

  1. 属于Thread类中的方法,
  2. 不释放对象锁
  3. 可以设置等待时间,监控状态依然保持着,当指定时间到了会自动恢复运行状态。

wait()方法

  1. 属于Object类中的方法
  2. 会释放对象锁
  3. 不会设置等待时间,只有针对此对象调用notify()方法后本线程才会重新获取对象锁进入运行状态,或者notifyAll()方法唤醒全部wait的线程。

九、sleep()方法和yield()方法有什么区别

  1. sleep()方法给其他线程运行机会时,不考虑线程的优先级,都是抢占式的;yield()方法只会给相同优先级或者更高优先级的线程以运行的机会。
  2. 线程执行sleep()方法后转入阻塞状态;执行yield()方法会转入就绪状态。
  3. sleep()方法声明抛出InterruptedException异常;yield()方法没有声明任何异常。
  4. sleep()方法比yield()方法具有更好的可移植性。

十、notify()和notifyAll()有什么区别

  1. notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
  2. notify()唤醒一个正在等待该对象的线程;notifyAll()唤醒所有正在等待该对象的线程。
  3. notify()可能导致死锁;notifyAll()不会导致死锁。
  4. notifyAll()可以唤醒所有处于wait状态的线程,使其重新进入锁的竞争队列中;notify()只能唤醒一个。

总结

等等。我还有一句话要说,生活已经很累了,但是要记得爱自己。