java多线程学习

1. 进程和线程

1. 1进程和线程的概念

  • 进程是资源分配(CPU、内存)的基本单位,它是程序执行的一个实例,在程序运行时创建;程序运行时系统就会创建一个进程,并为它分配资源,然后把它放入线程队列,等待CPU的调度,当调度到它的时候,就会为它分配CPU时间片,程序开始真正运行。

  • 线程是程序执行的最小单位,它是进行的一个执行流,是CPU调度和分派的基本单位;一个程序的多个线程之间可以并发执行。

2. 线程的实现

2. 1 多线程的实现

多线程的实现有三种方法:实现Runnable接口,继承Thread类,实现Callable接口

2. 1. 1 实现Runnable接口

  • 先来看看Runnable源码:
@FunctionalInterface
public interface Runnable {       // Runnable 接口源码
      public abstract void run();
  }

以下是Runnable接口实现多线程:

public class Test3 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Test2(),"Test2_Thread");
        thread.start();             // 使线程进入线程队列等待调度
    }
}
class Test2 implements Runnable {              
    @Override
    public void run() {                   // 重写run()方法
        System.out.println(Thread.currentThread().getName() + " begin executing! ");
    }
}

Test2类实现了Runnable接口,然后其对象作为参数传给Thread的构造方法,我们再来看看Thread类部分源码:

public class Thread implements Runnable  {   //   java.lang.Thread
    private Runnable target;          // 引用构造方法参数中的Runnable
    private volatile String name;     // 线程名,若不设定则使用默认赋值
    
	public Thread(Runnable target, String name) {      // Thraed类的一个构造方法
        this(null, target, name, 0);
    }
    
    @Override                                // 重写run()方法
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}      

2. 1. 2 继承Thread类

java 8 新特性 lamda 公式 ,也称为闭包。

public class Test3 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
                System.out.println( Thread.currentThread().getName() + " begin executing! ");    // 使用了lamda公式,简洁了代码
        }, "Test3_thread");
        thread.start();
    }
}

2. 1. 2 实现Callable接口

//TODO

2. 2 关于run()和start()的区别

  • run()方法

方法内是线程要做的事情;

  • start()方法

在 start() 方法内部,JVM 将创建一个新的本地线程,并将其与 Thread 对象关联。然后,JVM 调用本地方法,该本地方法会执行新线程的 run() 方法。这样,新线程会在独立的执行流中执行 run() 方法的代码。

2. 3 线程的五种状态

  • 新建 : 线程创建后就为新建状态( 例如 调用构造器 new Thread( ) ),此时具有相应的内存空间和其他资源,但还处于不可运行阶段;

  • 就绪:通过调用线程对象的 start() 方法可以使线程进入就绪阶段,线程加入等待队列。此时的线程已经万事俱备,就等待CPU从等待队列的调度,为其分配时间片;

  • 运行:线程被成功调度并分配处理器资源,线程就进入了运行阶段。此时自动调用线程对象的run()方法;

  • 堵塞:线程在某些特殊情况下,暂停自己的运行状态,如 I/O操作等,或者调用线程的sleep()、wait()、join()方法等

    都可以使线程进入堵塞阶段;当引起阻塞的原因被消除后,线程就能重新回到就绪阶段,等待CPU调度。

  • 终止:线程有三种方式终止自己,终止后不具有运行能力

    1. run()方法执行完,正常结束线程;

    2. 使用stop()方法强行终止线程,此方法直接终止线程(不推荐, Deprecated(since = “1.2”) );

2. 4 多线程常用的操作方法

2. 4. 1 线程的命名和取得

  1. 构造方法传入

    public Thread(Runnable target, String name) { }

  2. 调用setName()方法传入

    public final synchronized void setName(String name) { this.name = name; }

  3. 取得线程名字

    public final String getName() { return name; }

    线程名若不设定则会自动命名 ,使用默认名 "Thread - XXX "

2. 4. 2 线程休眠

public static void sleep(long millis, int nanos) throws InterruptedException { } // 指定睡眠毫秒数,纳秒数

public static native void sleep(long millis) throws InterruptedException; { } // 指定睡眠毫秒数

线程休眠使线程暂停运行,进入堵塞阶段,经过指定毫秒数后,又回到就绪阶段,等待调度;

睡眠中可能会产生中断异常 InterruptedException ,需对其进行异常捕获或处理。

2. 4. 3 线程中断

private volatile boolean interrupted;   // 中断状态位
public void interrupt()     // 使interrupted为true,抛出异常后立即又把设置interrupted为false
public boolean isInterrupted()  { return  interrupted; }    // 测试线程是否已经中断。线程的中断状态不受该方法的影响。
public static boolean interrupted()    // 测试当前线程是否已经中断。线程的中断状态 由该方法清除。

interrupt() 方法使 interrupted 为 true,也就是设置中断位为true,线程会时不时的检测这个中断位,一旦检测 interrupted 为 false,则 抛出interruptedException,中断的结果使堵塞的线程重新回到就绪状态。

interrupt()方法的作用不是终止线程,不会中断一个正在运行的线程。而是改变中断状态,使处于堵塞状态的线程产生interruptedException异常(需提前进行好异常处理),让线程提前结束堵塞状态,所以使线程堵塞的方法( wait()、join()、sleep() )都会有throws interruptedException。

Thread.interrupt()方法: 作用是中断线程。将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程 
interrupt()方法只是改变中断状态,不会中断一个正在运行的线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程检查到中断标识,就得以退出阻塞的状态。
更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。

2. 4. 4 线程合并

public final void join() throws InterruptedException     

调用join()的线程将在所处线程前执行,所在的线程进入堵塞阶段

public class Test3 {
    public static void main(String[] args) throws InterruptedException {
        
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread execute!");
                    sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        thread.join();               // main线程受阻等待thread执行完再执行
        for(int i = 0;i < 10;i++){
            System.out.print(i + " ");
        }
    }
}

2. 4. 5 线程让步

public static native void yield();
yield()做的是让当前运行线程回到就绪状态,给其他线程运行的机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。如果线程数很少,可能会无效果。

2. 4 . 6 线程优先级

线程优先级与线程的调度有关,个人优先级是主观意向,实际被调度的几率还是和性能有关。

 public static final int MIN_PRIORITY = 1;
 public static final int NORM_PRIORITY = 5;         // 主线程默认优先级为5
 public static final int MAX_PRIORITY = 10;

 public final void setPriority(int newPriority)        // 获取优先级
 public final int getPriority()                    // 设置优先级

3. 线程同步和死锁问题

3. 1 同步

同步方法和同步代码块

3. 1线程通讯

wait() 和 notifyAll() 、 notifyAll()

3. 2 守护线程,非守护线程(用户线程)

private boolean daemon = false;   // 默认线程为非守护线程

通过调用 setDaemon() 方法来设置守护线程

 public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) 
            throw new IllegalThreadStateException(); // 线程死亡后调用此方法抛出异常
        }  
        daemon = on;   // 源码通过调用setDaemon()方法来设置 daemon 的值
    }

join() 线程对象调用,当前线程等待其执行完在执行

public class Test {
    public static void main (String[] args)throws Exception {
        thread2();
    }
    public static void thread1(){
        Thread daemonThread = new Thread(()->{
            for(int i = 0;i < 10;i++){
                System.out.println("非守护线程(用户线程)执行");
            }
        });
        daemonThread.setDaemon(true);
        for(int i=0;i<10;i++){
            System.out.println("主线程执行");
        }
        System.out.println(Thread.currentThread().getName() + "执行结束");
    }
    public static void thread2() throws Exception{
        Thread thread1 = new Thread(()->{
            for(int i = 0;i < 10;i++){
                System.out.println("Thread1 execute!");
            }
        });
        thread1.start();
        Thread thread2  = new Thread(()->{
            try {
                thread1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i = 0;i < 100;i++){
                System.out.println("Thread2 execute!");
            }
        });
        thread2.start();
        Thread thread3  = new Thread(()->{
            try {
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i = 0;i < 100;i++){
                System.out.println("Thread3 execute!");
            }
        });
        thread3.start();
    }
}

4. 锁

synchronized作用在实例方法上锁对象, synchronized作用在静态方法上锁类

synchronized(){}代码块,锁对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJ69E0UG-1605695535625)(E:\QQFile\1833475904\FileRecv\MobileFile\CSDN_1603966901791.jpg)]

package multieThread;
class A{
    private static int b = 100;
    public static void fun(){      // 静态方法
        synchronized (A.class){    // 锁类
            while(b > 0){
                b--;
                System.out.println(b);
            }
        }
    }
}
public class Test1 implements Runnable {
    private static int a;
    @Override
    public void run() {
        A.fun();
    }
    public static void main(String[] args) {
        Test1 test1 = new Test1();
        new Thread(test1).start();
        new Thread(test1).start();
        new Thread(test1).start();
    }
}

同步方法使用this锁 , 静态同步方法使用当前class文件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值