Java 多线程编程

目录

  • 一、简介
  • 二、线程的生命周期
  • 三、Thread 方法
  • 四、创建线程
    • 1.通过继承Thread类
    • 2.通过实现Runnable接口
    • 3.匿名内部类创建线程
  • 五、线程方法应用
    • 5.1、sleep()
    • 5.2、setDaemon()
    • 5.3、interrupt()方法和stop()方法
    • 5.4、yield()
    • 5.5、join()
    • 5.6、线程优先级
    • 5.7、synchronized锁
    • 5.8、定时器

一、简介

Java多线程是指一个进程在执行过程中可以产生多个更小的程序单元,这些更小的单元称为线程,这些线程可以同时存在,同时运行,一个进程可能包含多个同时执行的线程。多线程是实现并发机制的一种有效手段。在 Java中实现多线程有两种手段,一种是继承Thread类,另一种就是实现Runnable接口。

线程是进程内部比进程更小的执行单元(执行流|程序片段),每个线程完成一个任务,每个进程内部包含了多个线程每个线程做自己的事情,在进程中的所有线程共享该进程的资源。

二、线程的生命周期

Java 多线程编程_第1张图片

  • 新建状态:
    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  • 就绪状态:
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  • 运行状态:
    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  • 阻塞状态:
    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

    • 死亡状态:
      一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

三、Thread 方法

方法 作用
start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
setName(String name) 改变线程名称,使之与参数 name 相同。
setPriority(int priority) 更改线程的优先级。
setDaemon(boolean on) 将该线程标记为守护线程或用户线程。
join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。
interrupt() 中断线程。
isAlive() 测试线程是否处于活动状态。

上述方法是被 Thread 对象调用的,下面表格的方法是 Thread 类的静态方法。

静态方法 作用
yield() 暂停当前正在执行的线程对象,并执行其他线程。
sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
currentThread() 返回对当前正在执行的线程对象的引用。
dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。

静态方法比较常用的就是currentThread、sleep、yield

四、创建线程

1.通过继承Thread类

public class Demo1_thread {
    public static void main(String[] args) {
        Mythread mt = new Mythread();
        mt.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("bbbbbbbbbbbbbbbb");
        }
    }
}
class Mythread extends Thread{//继承Thread
    @Override
    public void run() {//重写run方法
        for (int i = 0; i < 1000; i++) {//将要执行的代码写在run方法中
            System.out.println("aaaaaaaaaaa");
        }
    }
}

上面代码需要重写run方法,在创建线程之后通过start()方法来调用线程。

2.通过实现Runnable接口

public class Demo2_thread {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();//创建子类对象
        Thread tr = new Thread(mr);
        tr.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("bbbbbbbb");
        }
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("aaaaaaaaa");
        }
    }
}

和第一种很类似,也是要重写run方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。

3.匿名内部类创建线程

//匿名内部类
public class Demo3_thread {
    public static void main(String[] args) {
        new Thread("hhhh"){//继承thread类,name代表线程的名称
            @Override
            public void run() {//重写run方法
                for (int i = 0; i < 1000; i++) {
                    System.out.println(this.getName()+"aaaaaaaaaaaaa");
                }
            }
        }.start();
        Thread.currentThread().setName("我是主线程");
        System.out.println(Thread.currentThread().getName());
        for (int i = 0; i < 1000; i++) {
            System.out.println("bbbbbbbbbbbbbb");
        }
        new Thread(new Runnable() {//将runnable的子类对象传递给Thread的构造方法
            @Override
            public void run() {//重写run方法
                for (int i = 0; i < 1000; i++) {//Thread.currentThread()获取当前线程对象
                    System.out.println(Thread.currentThread().getName()+"acccccccccccccca");
                }
            }
        }).start();
    }
}

五、线程方法应用

5.1、sleep()

public class Demo4_threadSleep {
    public static void main(String[] args) throws InterruptedException {
        new Thread(){
            @Override
            public void run(){
                for (int i = 0; i < 20; i++) {
                    try {
                        Thread.sleep(1000);//单位毫秒
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(getName()+i);
                }
            }
        }.start();
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 20; i++) {
            Thread.sleep(1000);//单位毫秒
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

run()方法中的异常只能try catch,因为父类没有抛出异常,子类不能抛出比父类更多的异常。

5.2、setDaemon()

特点:守护线程,等别的线程结束自己自动退出
其它的和普通线程没有什么区别, 一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束。

//守护线程,等别的线程结束自己自动退出
public class Demo5_Daemon {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 2; i++) {
                    System.out.println(getName()+"aaaaaaaa");
                }
            }
        };
        Thread t2 = new Thread("守护线程"){
            @Override
            public void run() {
                while (true){
                    System.out.println(getName()+"bbbbbbbbbbbbbbb");
                }
            }
        };
        t2.setDaemon(true);//开启守护线程
        t1.start();
        t2.start();
    }
}

5.3、interrupt()方法和stop()方法

public class Demo12_interrupt {
    public static void main(String[] args) {
        Mytd t = new Mytd();
        t.setName("t");
        t.start();
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
        t.interrupt();
        //t.stop(); //强行终止线程
        //缺点:容易损坏数据  线程没有保存的数据容易丢失
    }
}
class Mytd extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        try {
            Thread.sleep(1000 * 60 * 60 * 24 * 365);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"结束执行");
    }
}

5.4、yield()

暂停当前正在执行的线程对象,并执行其他线程
yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
注意:在回到就绪之后,有可能还会再次抢到。

public class Demo13_yield {
    public static void main(String[] args) {
        MyThread t1 = new MyThread("线程1");
        MyThread t2 = new MyThread("线程2");
        MyThread t3 = new MyThread("线程3");
        t1.start();
        t2.start();
        t3.start();
    }
}
class MyThread extends Thread{
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            if(i==30){
                //当循i环到30时,让线程让步
                Thread.yield();
                //1、回到抢占队列中,又争夺到了执行权
                //2、回到抢占队列中,没有争夺到执行权
            }
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}

5.5、join()

这个方法的作用是先将当前线程挂起,待其他线程结束后在执行当前线程的代码;
Java 多线程编程_第2张图片

//加入线程,等待指定线程结束后在执行
public class Demo6_join {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(getName()+"aaaaaaaa");
                }
            }
        };
        Thread t2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    if (i == 2) {
                        try{
                            t1.join(1);//插队1毫秒
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    System.out.println(getName()+"bbbbbbbbbbbbbbb");
                }
            }
        };
        t1.start();
        t2.start();

    }
}

5.6、线程优先级

  • getPriority():获取线程优先级
  • setPriority:设置线程优先级
public class Demo14_Priority {
    public static void main(String[] args) {
        MyThread2  t1 = new MyThread2("线程1");
        MyThread2  t2 = new MyThread2("线程2");
        MyThread2  t3 = new MyThread2("线程3");
        //默认等级是5
        System.out.println(t1.getPriority());
        System.out.println(t2.getPriority());
        System.out.println(t3.getPriority());
        System.out.println(Thread.MAX_PRIORITY);//10
        System.out.println(Thread.NORM_PRIORITY);//5
        System.out.println(Thread.MIN_PRIORITY);//1
        //设置t1线程的优先级为6
        t1.setPriority(6);
        System.out.println(t1.getPriority());
    }
}

5.7、synchronized锁

  • 一把锁只能同时被一个线程获取,没有获得锁的线程只能阻塞等待
  • synchronized修饰的方法,无论方法正常执行完毕还是抛出异常,都会释放锁
    每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例如锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象公用同一把锁
/*
* 同步代码块
*有多段代码执行时希望一段代码执行的过程中cpu不要切换到其他线程工作,所以就加同步
* */
public class Demo7_Synchronized {
    public static void main(String[] args) {
        final Printer p1 = new Printer();
        new Thread(){
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(getName());
                    p1.print1();
                }
            }
        }.start();
        new Thread(){
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(getName());
                    p1.print2();
                }
            }
        }.start();
    }
}
class Printer {
    Demo d = new Demo();
    public void print1() {
        synchronized (d) {//同步 代码块锁对象,锁对象可以是任意的,随便创建一个对象都可以,不可以匿名内部类
            System.out.print("h1");
            System.out.print("h2");
            System.out.print("h3");
            System.out.print("h4");
            System.out.print("h5");
            System.out.print("\r\n");
        }
    }

    public void print2() {
        synchronized (d) {
            System.out.print("h6");
            System.out.print("h7");
            System.out.print("h8");
            System.out.print("h9");
            System.out.print("\r\n");
        }
    }
}
class Demo{

}

应用场景:例如火车卖票,多人抢票的时候就会出现数据安全问题,比如现在只有100张票,每个人在抢的时候肯定要遵守先到先得的原则,当一个人开始抢的时候就给他分配一把锁,然后用完之后释放这把锁给下一个人,有锁的人才能进行抢票,这样就解决了并发的问题。

//火车卖票
public class Demo8_Synchronized {
    public static void main(String[] args) {
        MyTicket mt = new MyTicket();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}
class MyTicket implements Runnable{
    private int ticket = 100;
    public void run() {
        while (true){
            synchronized (MyTicket.class) {
                if (ticket == 0) {
                    break;
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "这是第" + ticket-- + "号票");
            }
        }
    }
}

5.8、定时器

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

public class Demo11_Timer {
    public static void main(String[] args) throws InterruptedException {
        Timer t = new Timer();
        t.schedule(new MyTimer(),new Date(122,9,7,13,37,30),3000);//每过三秒执行一次
        while (true){
            Thread.sleep(1000);
            System.out.println(new Date());
            System.gc();//将编程垃圾的定时器进行回收
        }
    }
}
class MyTimer extends TimerTask{
    @Override
    public void run() {
        System.out.println("起床吃饭");
    }
}

你可能感兴趣的:(Java基础,java,jvm,开发语言)