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

图解Java多线程设计模式——Java多线程基础

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

图解Java多线程设计模式——Java多线程基础

文章目录

简介

线程的启动

线程启动(1)——利用Thread类的子类线程启动(2)——利用Runnable接口利用ThreadFactory新启动线程 线程的暂停线程的互斥处理

synchronized方法(同步方法)synchronized代码块

▶synchronized实例方法和synchronized代码块▶synchronized静态方法和synchronized代码块 线程的协作

wait方法——将线程放入等待队列notify方法——从等待队列中取出线程notifyAll方法——从等待队列中取出所有线程


简介

通过《图解Java多线程设计模式》学习多线程的使用。
《图解Java多线程设计模式》资源下载

线程的启动

    利用Thread类的子类的实例创建启动线程。

    利用Runnable接口的实现类的实例启动线程。

线程启动(1)——利用Thread类的子类

创建PrintThread类重写run()方法

public class PrintThread extends Thread {
    private String message;
    public PrintThread(String message) {
        this.message = message;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(message);
        }
    }
}

利用PrintThread类启动两个线程:

public class TestThread {
    public static void main(String[] args) {
        new PrintThread("Good!").start();
        new PrintThread("Nice!").start();
    }
}

线程启动(2)——利用Runnable接口

Runnable接口包含在java.lang包中,声明如下:

public interface Runnable{
	public abstract void run();
}

Runnable接口的实现类必须要实现run方法。

创建Printer类实现Runna接口

public class Printer implements Runnable{
    private String message;
    public Printer(String message) {
        this.message = message;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(message);
        }
    }
}

利用Printer类启动两个线程:

public class TestThread {
    public static void main(String[] args) {
        Runnable runnable=new Printer("Good!");
        Thread thread1 =new Thread(runnable);
        thread1.start();
        
        new Thread(new Printer("Nice!")).start();
    }
}

利用ThreadFactory新启动线程
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class TestThread {
    public static void main(String[] args) {
        ThreadFactory factory = Executors.defaultThreadFactory();
        factory.newThread(new Printer("Good!")).start();
        factory.newThread(new Printer("Nice!")).start();
    }
}

✨小知识: 线程的终止

​ java程序的终止是指除守护线程(Daemon Thread)以外的线程全部终止。守护线程是执行后台作业的线程。我们可以通过setDaemon方法把线程设置为守护线程。


线程的暂停

线程Thread类中的sleep方法能够暂停线程的运行。sleep也就是**”休眠“**的意思。sleep方法是Thread类的静态方法。

Thread.sleep(1000)

执行这条语句的线程暂停约1000毫秒(约1秒)。

✨小知识:指定到纳秒(ns)单位

​ 在sleep方法中,停止时间也可以指定到纳秒(10-9秒)单位,语法如下。

​ Thread.sleep(毫秒,纳秒);

​ 不过,通常情况下Java平台运行环境无法实现这么精准的控制。具体的精度程度依Java平台运行环境而不同。

✨小知识:如何唤醒

​ 如果中途唤醒被Thread.sleep休眠的线程,可以使用interrupt方法。————后面会有内容。


线程的互斥处理

线程A和线程B之间互相竞争而引起的与预期相反的情况称为数据竞争或竞态条件。线程互斥就是为了避免这种情况的发生。例如:如果一个线程正在执行某一部分操作,那么其他线程就不可以再执行这部分操作。

Java使用关键字synchronized来执行线程的互斥处理。

synchronized方法(同步方法)

如果声明一个方法时,在前面加上关键字synchronized,那么这个方法就只能由一个线程运行。这种方法称为synchronized方法,也称为同步方法

Bank(银行)类中的deposit(存款)和withdrew(取款)这两个方法就是synchronized方法。

public class Bank{
    private int money;
    private String name;
    
    public Bank(String name,int money){
        this.name = name;
        this.money = money;
    }
    //存款
    public synchronized void depoist(int m){
        money += m;
    }
    //取款
    public synchronized boolean withdrew(int m){
        if(money >= m){
            money -= m;
            return true;//取款成功
        } else {
            return false;//取款失败
        }
    }
}

一个实例中的synchronized方法每次只能有一个线程运行。如果一个线程正在运行synchronized方法,其他线程需要排队等候。每个实例拥有一个独立的锁。

✨小知识:锁和监视

​ 线程的互斥机制称为监视(monitor)。另外,获取锁有时也叫做”拥有(own)监视“或”持有(hold)锁“。

​ 当前线程是否已获取某一个对象的锁可以通过Thread.holdsLock方法来确认。当前线程已获取对象obj的锁时,可使用assert来像下面这样表示出来:

​ assert Thread.holdsLock(obj);

synchronized代码块

如果只是想让方法中的某一部分由一个线程运行,而非整个方法,可以使用synchronized代码块,格式如下:

synchronized (表达式){
    ...
}

其中**”表达式“**为获取锁的实例。synchronized代码块用于精准控制互斥处理的执行范围。

▶synchronized实例方法和synchronized代码块

synchronized实例方法是使用this的锁来执行线程的互斥处理的。

//synchronized实例方法
synchronized void method(){}

//synchronized代码块
void method(){
    synchronized (this){
        ...
    }
}
▶synchronized静态方法和synchronized代码块

synchronized静态方法是使用该类的类对象的锁来执行线程的互斥处理的。

class Something{
	static synchronized void method(){ 
		...
    }
}

class Something{
	static void method(){
    	synchronized (Something.class){
    		...
    	}
    }
}

线程的协作 wait方法——将线程放入等待队列

wait(等待)方法会让线程进入等待队列。执行下面这个语句。

obj.wait()

那么,当前线程便会暂停运行,并进入实例obj的等待队列中。这叫做“线程正在obj上wait”。

若要执行wait方法,线程必须持有锁(这是规则)。但如果线程进入等待队列,便会释放其实例的锁。

✨小知识:等待队列

​ 等待队列是一个虚拟的概念。它既不是实例中的字段,也不是用于获取正在实例上等待的线程的列表的方法。

notify方法——从等待队列中取出线程

notify(通知)方法会将等待队列中的一个线程取出。假设执行下面这条语句。

obj.notify()

那么obj的等待队列中的一个线程便会被选中唤醒,然后退出等待队列。

同wait方法一样,执行notify方法,线程必须持有要调用的实例的锁。

✨小知识:执行notify后的线程状态
notify唤醒的线程并不会在执行notify的一瞬间重新运行。因为在执行notify的那个线程还持有着锁,其他线程无法获取这个实例的锁。

notifyAll方法——从等待队列中取出所有线程

notifyAll(通知大家)方法会将等待队列中的所有线程都取出来。执行下面这条语句。

obj.notifyAll()

在obj实例的等待队列中休眠的所有线程都会被唤醒。同样,执行notifyAll方法的线程持有锁。

✨小知识:如果线程未持有锁会怎么样

​ 如果未持有锁的线程调用wait、notify和notifyAll,会抛出异常:java.lang.IllegalMonitorStateException

java.util.concurrent包提供了便于多线程编程的可复用性高的类。

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

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

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