栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java多线程基础

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java多线程基础

多线程 1、线程中的概念 1.1、什么是进程、什么进程

进程是一个应用进程(1个进程就是一个软件)

线程是一个进程中的执行场景、执行单元

一个进程可以启动多个线程。

1.2、进程和线程的关系

在java语言中:

​ 线程A和线程B,堆内存和方法区内存共享。

​ 但是栈内存独立,一个线程一个栈。

java中之所以有多线程机制,目的就是为了提高程序的处理效率。

2、Java中的多线程

java支持多线程机制,并且java已经将多线程实现了,我们只需要继承就行了。

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
package com.caopeng.java.thread;


public class ThreadTest01 {
    public static void main(String[] args) {
        // 这里是main方法,这里的代码属于主线程,在主栈中运行
        // 新建一个分支线程对象
        MyThread myThread = new MyThread();
        // 启动线程
        // start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了
        // 这段代码的任务只是开启了一个新的栈空间,只要新的栈空间开出来,start()方法就结束了,线程就启动成功了
        // 启动成功的线程会自动调用run方法,并且run方法在分支栈的底部(压栈)
        // run方法在分支栈的底部,main方法在主栈的底部,run和main是平级的
//        myThread.run();不会启动线程,不会分配新的分支栈(这种方式就是单线程)
        myThread.start();
        for(int i = 0;i < 1000;i++) System.out.println("主线程--->" + i);
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
//        super.run();
        // 编写程序,这段程序运行在分支线程中(分支栈)
        for(int i = 0;i < 1000;i++) System.out.println("分支线程--->" + i);
    }
}

第二种方式:编写一个类实现java.lang.Runnable接口
package com.caopeng.java.thread;


public class ThreadTest02 {
    public static void main(String[] args) {
        // 创建一个可运行的对象
//        MyRunnable myRunnable = new MyRunnable();
        // 将可运行的对象封装成一个线程对象
//        Thread thread = new Thread(myRunnable);
        // 合并代码
        Thread thread = new Thread(new MyRunnable());
        // 启动线程
        thread.start();
        for(int i = 0;i < 1000;i++) System.out.println("主线程--->" + i);
    }
}

// 这并不是一个线程类,只是一个可运行的类,它号不是一个线程。
class MyRunnable implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i < 1000;i++) System.out.println("分支线程--->" + i);
    }
}

使用匿名内部类:

package com.caopeng.java.thread;


public class ThreadTest03 {
    public static void main(String[] args) {
        // 创建线程对象,采用匿名内部类的方法
        // 直接new 接口
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0;i < 1000;i++) System.out.println("分支线程--->" + i);
            }
        });
        // 启动线程
        thread.start();
        for(int i = 0;i < 1000;i++) System.out.println("主线程--->" + i);
    }
}

线程生命周期

获取线程对象
package com.caopeng.java.thread;


public class ThreadTest05 {
    public static void main(String[] args) {

        // currentThreat就是当前线程对象
        // 这个对象出现再main方法中,所以当前线程就是主线程
        Thread currentThread = Thread.currentThread();
        System.out.println(currentThread.getName());    // main

        // 创建线程对象
        MyThread2 myThread1 = new MyThread2();
        System.out.println(myThread1.getName());    // Thread-0
        // 设置线程对象的名字
        myThread1.setName("t2");
        // 获取线程的名字
        System.out.println(myThread1.getName());    // t2
        // 再创建一个新的线程
        MyThread2 myThread2 = new MyThread2();
        System.out.println(myThread2.getName());    // Thread-1
        // 启动线程
        myThread1.start();                          // t2
        myThread2.start();                          // Thread-1
    }
}


class MyThread2 extends Thread {
    @Override
    public void run() {
        // 当前线程,谁执行run方法,谁就是currentThread
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
//        for(int i = 0;i < 100;i++) System.out.println("分支线程-->" + i);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ne0bDgYK-1635059132255)(C:UsersCrescent_PAppDataRoamingTyporatypora-user-imagesimage-20211023170447329.png)]

线程的sleep()方法
    package com.caopeng.java.thread;

    
    public class ThreadTest06 {
        public static void main(String[] args) {

            // 让当前线程进入休眠,睡眠5秒
            // 当前线程是主线程
    //        try {
    //            Thread.sleep(1000 * 5);
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }
            // 五秒后执行
    //        System.out.println("Hello World");

            for(int i = 1;i <= 10;i++){
                // 打印一次,休息一秒
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    // 睡眠一秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

package com.caopeng.java.thread;


public class ThreatTest07 {
    public static void main(String[] args) {
        // 创建线程对象
        Thread t = new MyThreat3();
        t.setName("t");
        t.start();

        // 调用sleep方法
        try {
            // 这行代码会让线程t进入休眠状态吗?
            t.sleep(1000 * 5);      // 不会,在执行的时候还是会转化成 Thread.sleep(1000 * 5)
                                            // 这行代码的作用是:让当前代码进入休眠,也就是main线程进入休眠
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(("Hello World"));
    }
}

class MyThreat3 extends Thread{
    @Override
    public void run() {
//        super.run();
        for (int i = 0;i < 1000;i++) System.out.println(Thread.currentThread().getName() + "--->" + i);
    }
}
package com.caopeng.java.thread;


public class ThreatTest08 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable2());
        thread.setName("t");
        thread.start();

        // 希望5秒之后,t线程醒来
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 终端t线程的睡眠,这种中断睡眠的方式依靠了java的异常处理机制。
        thread.interrupt(); // 干扰
    }
}

class MyRunnable2 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---> begin");
        // 子类重写不能抛出更多异常
        // run()在父类中没有抛出任何异常,子类重写不能比父类抛出更多异常
        try {
            Thread.sleep(1000 * 60 * 60 * 24 * 365);    // interrupt会使这里报异常
        } catch (InterruptedException e) {
            // 打印异常信息
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "--->end");
    }
}

强制终止线程
package com.caopeng.java.thread;


public class ThreadTest09 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable3());
        thread.setName("t");
        thread.start();

        // 模拟5秒
        try {
            Thread.currentThread().sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 5秒收强制终止t线程
        thread.stop();      // 容易损坏数据
    }
}

class MyRunnable3 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
合理终止线程
package com.caopeng.java.thread;


public class ThreadTest10 {
    public static void main(String[] args) {
        MyRunnable4 myRunnable4 = new MyRunnable4();
        Thread thread = new Thread(myRunnable4);
        thread.setName("t");
        thread.start();
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 终止线程
        // 你想要什么时候终止t的执行,那么你就吧标记修改为false,就结束了
        myRunnable4.run = false;
    }
}

class MyRunnable4 implements Runnable {
    // 打一个布尔标记
    boolean run = true;
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if(run){
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                // 终止当前进程
                // 在这保存数据
                return;
            }
        }
    }
}

Java中线程调度方法

实例方法:

void setPriority(int newPriority)		// 设置线程的优先级
int getPriority()						// 获取线程优先级
最低优先级	1
默认优先级	5
最高优先级	10
void join()								// 合并线程
class MyThread1 extends Thread{
    public void dosome(){
        Mythread2 t = new Mythread2();
        t.join();	// 当前进程进入阻塞,t线程执行,直到t线程结束,当前线程才可以运行
    }
}
class Mythread2 extends Thread{}

静态方法:

static void yield()			// 让位方法

暂停当前正在执行的线程方法,并执行其它线程

yield()方法不是阻塞方法,让当前线程让位,让给其它线程使用

yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”

package com.caopeng.java.thread;


public class ThreadTest11 {
    public static void main(String[] args) {
//        System.out.println("最高优先级: " + Thread.MAX_PRIORITY);
//        System.out.println("最低优先级: " + Thread.MIN_PRIORITY);
//        System.out.println("默认优先级: " + Thread.NORM_PRIORITY);
//        最高优先级: 10
//        最低优先级: 1
//        默认优先级: 5

        // 获得当前线程对象,获取当前线程优先级
//        Thread thread = Thread.currentThread();
//        System.out.println(thread.getName() + "线程的默认优先级是: " + thread.getPriority());
        // main线程的默认优先级是: 5

//        Thread thread1 = new Thread(new MyRunnable5());
//        thread1.start();
        // Thread-0线程的默认优先级是: 5

        // 设置主线程的优先级为1
        Thread.currentThread().setPriority(1);
        Thread thread1 = new Thread(new MyRunnable5());
        thread1.setPriority(10);
        thread1.start();
        for (int i = 0; i < 10000; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }

    }
}

class MyRunnable5 implements Runnable {
    @Override
    public void run() {
        // 获取线程的优先级
//        System.out.println(Thread.currentThread().getName() + "线程的默认优先级是: " + Thread.currentThread().getPriority());
        for (int i = 0; i < 10000; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

package com.caopeng.java.thread;


public class TreadTest12 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable6());
        thread.setName("t");
        thread.start();

        for (int i = 1; i <= 10000; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

class MyRunnable6 implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 10000; i++) {
            // 每100个让位1次
            if(i % 100 == 0) Thread.yield();
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

package com.caopeng.java.thread;


public class ThreadTest13 {
    public static void main(String[] args) {
        System.out.println("main begin...");

        Thread thread = new Thread(new MyRunnable7());
        thread.setName("t");
        thread.start();

        // 合并线程
        try {
            thread.join();  // t合并到当前线程,当前线程受阻塞,t线程执行,直到结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main over....");
    }
}

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

线程安全

什么时候存在线程安全问题呢?

  • 多线程并发
  • 有共享数据
  • 共享数据有修改行为

满足以上三个条件,就会存在线程安全问题

怎么解决?

线程排队执行,不能并发。称为:线程同步机制

会牺牲一定的效率,但是安全。

模拟取钱出错

Account.java

package com.caopeng.java.thread.threadsafe;


public class Account {
    // 账户
    private String accountId;
    // 余额
    private double balance;

    public Account() {
    }

    public Account(String accountId, double balance) {
        this.accountId = accountId;
        this.balance = balance;
    }

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    // 取款方法
    public void withDraw(double money){
        // 取款前的余额
        double before = this.balance;
        // 取款后的余额
        double after = before - money;
        try {
            // 模拟网络延迟
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 跟新余额
        this.setBalance(after);
    }
}

AccountThread.java

package com.caopeng.java.thread.threadsafe;


public class AccountThread extends Thread{
    // 两个线程共享一个账户对象
    private Account account;

    // 通过构造方法传递过来账户信息
    public AccountThread(Account account) {
        this.account = account;
    }

    // 取款
    @Override
    public void run() {
//        super.run();
        // 假设取款5000
        double money = 5000;
        // 取款
        account.withDraw(money);
        System.out.println(Thread.currentThread().getName() + "对账户" + account.getAccountId() + "取款: "+ money+ " 成功,余额为:" + account.getBalance());
    }
}

Test.java

package com.caopeng.java.thread.threadsafe;


public class Test {
    public static void main(String[] args) {
        // 创建账户对象
        Account account = new Account("act-001",10000);
        // 两个线程共享一个账户
        AccountThread thread1 = new AccountThread(account);
        AccountThread thread2 = new AccountThread(account);
        // 启动取款
        thread1.start();
        thread2.start();
    }
}

解决

使用Synchronized关键字

package com.caopeng.java.thread.threadsafe2;


public class Account {
    // 账户
    private String accountId;
    // 余额
    private double balance;

    public Account() {
    }

    public Account(String accountId, double balance) {
        this.accountId = accountId;
        this.balance = balance;
    }

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    
    // 取款方法
    public void withDraw(double money){
        // 以下几行代码必须是线程排队,不能并发
        // 一个线程把这里的代码执行完,另一个线程才能进来
        synchronized(this){
            double before = this.balance;
            double after = before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setBalance(after);
        }

    }
}

package com.caopeng.java.thread.threadsafe2;


public class AccountThread extends Thread{
    // 两个线程共享一个账户对象
    private Account account;

    // 通过构造方法传递过来账户信息
    public AccountThread(Account account) {
        this.account = account;
    }

    // 取款
    @Override
    public void run() {
//        super.run();
        // 假设取款5000
        double money = 5000;
        // 取款
        account.withDraw(money);
        System.out.println(Thread.currentThread().getName() + "对账户" + account.getAccountId() + "取款: "+ money+ " 成功,余额为:" + account.getBalance());
    }
}

package com.caopeng.java.thread.threadsafe2;


public class Test {
    public static void main(String[] args) {
        // 创建账户对象
        Account account = new Account("act-001",10000);
        // 两个线程共享一个账户
        AccountThread thread1 = new AccountThread(account);
        AccountThread thread2 = new AccountThread(account);
        // 启动取款
        thread1.start();
        thread2.start();
    }
}

有线程安全问题的变量

Java中有三大变量。

实例变量:堆

静态变量:方法区

局部变量:栈

局部变量一定没有线程安全问题,因为局部变量不共享,一个线程一个栈。

synchronized出现在实例方法上
package com.caopeng.java.thread.threadsafe3;


public class Account {
    // 账户
    private String accountId;
    // 余额
    private double balance;

    public Account() {
    }

    public Account(String accountId, double balance) {
        this.accountId = accountId;
        this.balance = balance;
    }

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    // 取款方法

    
    public synchronized void withDraw(double money){
        double before = this.balance;
        double after = before - money;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setBalance(after);
    }
}

synchronized的三种写法:

  • 同步代码块

    synchronized(线程共享对象){
        同步代码块
    }
    
  • 实例方法上使用synchronized,表示共享对象一定是this,并且同步代码块是整个方法体

  • 在静态方法上使用synchronized,表示找类锁,类锁只有一把,不管创建了多少个对象,只有一把类锁

Synchronized面试题 题一:
package com.caopeng.java.thread.exam1;


public class Exam01 {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        Thread thread1 = new MyThread(myClass);
        Thread thread2 = new MyThread(myClass);

        thread1.setName("t1");
        thread2.setName("t2");

        thread1.start();

        try {
            // 保证thread1先执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.start();
    }
}

class MyThread extends Thread {
    private MyClass myClass;

    public MyThread(MyClass myClass) {
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if(Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass{
    public synchronized void doSome(){
        System.out.println("doSome begin...");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over...");
    }

    public void doOther(){
        System.out.println("doOther begin...");
        System.out.println("doOther over...");
    }
}
// 不需要,因为 doOther 没有 synchronized 不需要对象锁

题二:
package com.caopeng.java.thread.exam2;


public class Exam01 {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        Thread thread1 = new MyThread(myClass);
        Thread thread2 = new MyThread(myClass);

        thread1.setName("t1");
        thread2.setName("t2");

        thread1.start();

        try {
            // 保证thread1先执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.start();
    }
}

class MyThread extends Thread {
    private MyClass myClass;

    public MyThread(MyClass myClass) {
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if(Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass{
    public synchronized void doSome(){
        System.out.println("doSome begin...");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over...");
    }

    public synchronized void doOther(){
        System.out.println("doOther begin...");
        System.out.println("doOther over...");
    }
}
// 需要,因为 doOther 有 synchronized 需要对象锁,但是对象锁此时已经被 thread1拿走了
// 需要thread1执行完doSome方法,才能拿到对象锁

题三:
package com.caopeng.java.thread.exam2;


public class Exam01 {
    public static void main(String[] args) {
        MyClass myClass1 = new MyClass();
        MyClass myClass2 = new MyClass();
        Thread thread1 = new MyThread(myClass1);
        Thread thread2 = new MyThread(myClass2);

        thread1.setName("t1");
        thread2.setName("t2");

        thread1.start();

        try {
            // 保证thread1先执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.start();
    }
}

class MyThread extends Thread {
    private MyClass myClass;

    public MyThread(MyClass myClass) {
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if(Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass{
    public synchronized void doSome(){
        System.out.println("doSome begin...");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over...");
    }

    public synchronized void doOther(){
        System.out.println("doOther begin...");
        System.out.println("doOther over...");
    }
}
// 不需要,即使两个方法都有 synchronized 修饰,但是两个线程占有的对象不一样
// 有两个 MyClass 类型的对象,thread1拿走的是myClass1这个对象的对象锁,不影响thread2占有myClass2的对象锁

题四:
package com.caopeng.java.thread.exam2;


public class Exam01 {
    public static void main(String[] args) {
        MyClass myClass1 = new MyClass();
        MyClass myClass2 = new MyClass();
        Thread thread1 = new MyThread(myClass1);
        Thread thread2 = new MyThread(myClass2);

        thread1.setName("t1");
        thread2.setName("t2");

        thread1.start();

        try {
            // 保证thread1先执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.start();
    }
}

class MyThread extends Thread {
    private MyClass myClass;

    public MyThread(MyClass myClass) {
        this.myClass = myClass;
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("t1")){
            myClass.doSome();
        }
        if(Thread.currentThread().getName().equals("t2")){
            myClass.doOther();
        }
    }
}

class MyClass{
    public synchronized static void doSome(){
        System.out.println("doSome begin...");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over...");
    }

    public synchronized void doOther(){
        System.out.println("doOther begin...");
        System.out.println("doOther over...");
    }
}
// 需要,因为 synchronized 出现在静态方法上,此时锁的是类锁
// 不管有几个对象,MyClass这个类的类锁只有这一个

死锁
package com.caopeng.java.thread.deadlock;


public class DeadLock {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        // 两个线程共享o1,o2
        Thread myThread1 = new MyThread1(o1,o2);
        Thread myThread2 = new MyThread2(o1,o2);

        myThread1.start();
        myThread2.start();
    }
}

class MyThread1 extends Thread {
    Object o1;
    Object o2;

    public MyThread1(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized(o1){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized(o2){

            }
        }
    }
}

class MyThread2 extends Thread {
    Object o1;
    Object o2;

    public MyThread2(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized(o2){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized(o1){

            }
        }
    }
}
守护线程

java语言中线程分为两大类:

  • 用户线程
  • 守护线程(后台线程)
    • 垃圾回收线程

守护线程的特点:一般用户线程是一个死循环,所有的用户线程只要结束,守护线程自动结束。

主线程main就是一个用户线程。

守护线程一般用在什么地方呢?

​ 每天00:00的时候系统数据自动备份

​ 这个需要使用到定时器,并且我们可以将定时器设置为守护线程。

​ 一直在那看着,每到00:00的时候就自动备份一份,所有的用户线程如果结束了,守护线程自动退出,没必要进行数据备份了。

package com.caopeng.java.thread;


public class ThreadTest14 {
    public static void main(String[] args) {
        Thread thread = new DateThread();
        thread.setName("备份数据的线程");
        // 启动线程之前,将线程设置成守护线程
        thread.setDaemon(true);
        thread.start();

        // 主线程,主线程是用户线程
        for(int i = 0;i < 10;i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

class DateThread extends Thread {
    @Override
    public void run() {
        int i = 0;
        // 即使是死循环,但由于该进程是守护进程,当用户进程结束,守护线程自动终止
        while(true){
            System.out.println(Thread.currentThread().getName() + "--->" + (++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

定时器

定时器的作用:

​ 每隔特定的时间,执行特定的程序

package com.caopeng.java.thread;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;


public class TimerTest {
    public static void main(String[] args) throws ParseException {
        // 创建定时器对象
        Timer timer = new Timer();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstDate = sdf.parse("2021-10-24 14:09:00");
        // 指定定时任务
        timer.schedule(new LogTimerTask(),firstDate,1000 * 10);
    }
}

// 编写一个定时任务类
class LogTimerTask extends TimerTask {

    @Override
    public void run() {
        // 需要执行的任务
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdf.format(new Date());
        System.out.println(time+" 完成了一次备份");
    }
}

实现线程的第三种方式

这种方式可以获取到线程返回值,之前的两种方式无法获得到线程的返回值。

实现Callable接口

package com.caopeng.java.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; // JUC包下,属于Java的并发包,Java新特性


public class ThreadTest15 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 第一步:创建一个"未来任务类"对象
        FutureTask futureTask = new FutureTask(new Callable(){
            @Override
            public Object call() throws Exception {     // call()方法相当于run方法,只不过这个有返回时
                // 线程执行一个任务,执行之后可能会一有一个执行结果
                // 模拟执行
                System.out.println("call method begin");
                Thread.sleep(1000 * 10);
                System.out.println("call method end");
                int a = 100;
                int b = 200;
                return a+b; // 自动装箱
            }
        });

        // 创建一个线程对象
        Thread thread = new Thread(futureTask);

        // 启动线程
        thread.start();

        // 这里是main方法,这是在主线程
        // 在主线程中,怎么获取到t线程的返回结果?
        // get()方法的执行,会导致当前线程的阻塞.
        Object o = futureTask.get();        // 会导致当前线程阻塞,到call()方法执行完后,才能拿到执行的执行结果的返回值
        System.out.println(o);
    }
}

关于Object类中的wait方法和notify方法
  1. wait方法和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法,因为这两个方法是Object类自带的

  2. wait方法作用

    Object o = new Object();
    o.wait();
    表示:让正在o对象上活动的线程进入等待,无期限等待,知道被唤醒为止
    

  3. notify方法的作用

    唤醒正在o对象上等待的线程
    
生产者消费者模式

package com.caopeng.java.thread;

import java.util.ArrayList;
import java.util.List;


public class ThreadTest16 {
    public static void main(String[] args) {
        // 共享一个仓库
        List list = new ArrayList();
        // 创建两个线程
        // 生产者线程
        Thread producer = new Thread(new Producer(list));
        // 消费者线程
        Thread consumer = new Thread(new Consumer(list));

        producer.setName("producer");
        consumer.setName("consumer");
        producer.start();
        consumer.start();
    }
}

// 生产线程
class Producer implements Runnable {
    private List list;

    public Producer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        // 一直生产
        while(true){
            // 给list加锁
            synchronized (list) {
                // 不为空
                if(!list.isEmpty()){
                    //进入等待状态,当前线程进入等待状态,并且释放 List集合的锁
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                // 仓库为空
                }else{
                    Object o = new Object();
                    list.add(o);
                    System.out.println(Thread.currentThread().getName() + "--->" + o);
                    // 唤醒消费者进行消费
                    list.notify();
                }
            }
        }
    }
}

// 消费线程
class Consumer implements Runnable{
    private List list;

    public Consumer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        // 一直消费
        while(true){
            synchronized(list){
                // 仓库空了,消费者进程进行等待
                if(list.isEmpty()){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    // 可以消费
                    Object o = list.remove(0);
                    System.out.println(Thread.currentThread().getName() + "--->" + o);
                    list.notify();
                }
            }
        }
    }
}

一直生产
while(true){
// 给list加锁
synchronized (list) {
// 不为空
if(!list.isEmpty()){
//进入等待状态,当前线程进入等待状态,并且释放 List集合的锁
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 仓库为空
}else{
Object o = new Object();
list.add(o);
System.out.println(Thread.currentThread().getName() + “—>” + o);
// 唤醒消费者进行消费
list.notify();
}
}
}
}
}

// 消费线程
class Consumer implements Runnable{
private List list;

public Consumer(List list) {
    this.list = list;
}

@Override
public void run() {
    // 一直消费
    while(true){
        synchronized(list){
            // 仓库空了,消费者进程进行等待
            if(list.isEmpty()){
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                // 可以消费
                Object o = list.remove(0);
                System.out.println(Thread.currentThread().getName() + "--->" + o);
                list.notify();
            }
        }
    }
}

}

[外链图片转存中...(img-YQndGccp-1635059132268)]





















































转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/352351.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号