一篇文章带你Java多线程入门

目录
  • 多线程的四种创建方式
    • 1.继承Thread类
    • 2.实现Runnable接口
    • 3.实现Callable接口
    • 4.使用线程池
  • 线程的优先级
  • 测试Thread中常用的方法
  • 线程的生命周期
  • 多线程的同步控制
    • 1.同步代码块
    • 2.同步方法
    • 3.同步锁
  • 线程通信
    • wait/notify模式
    • sleep和wait的异同
  • 总结

多线程的四种创建方式

1.继承Thread类

 /*
 * 多线程的创建,方式一:继承Thread类
 * 1.创建一个继承于Thread类的子类
 * 2,重写Thread类的run()  将线程操作写在run方法中
 * 3.创建Thread类的子类的对象
 * 4.通过对象调用start()
 */
//创建一个继承于Thread类的子类
public class MThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i%2==0)
                System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public class Threadtest {
	public static void main(String[] args) {
		//3.创建Thread类的子类的对象
		MThread t1=new MThread();
		//4.通过此对象调用start()
		t1.start();		//start方法来实现启动线程,并调用run方法,从而真正实现了多线程。同时run方法它只是一个普通的函数方法,不需要线程调用start方法也可以调用它.

	}
}

2.实现Runnable接口

 /*
	1.创建一个实现了Runnable接口
    2.实现类去实现Runable接口的类
    3.创建实现类的对象
    4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
    5.通过Thread类的对象调用start()
  */
class  window implements Runnable{
	private int ticket =100;
	public void run() {
		while(true) {
			if(ticket >0) {
				System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
				ticket--;
			}else
				break;

		}
	}
}
public class RunnableTest {
	public static void main(String[] args) {
		window w=new window();
		//三个线程用的同一个window,都执行同一个window的run,因此在设置票的数量时不需要设置为static
		Thread t1=new Thread(w);
		Thread t2=new Thread(w);
		Thread t3=new Thread(w);
		t1.setName("窗口一");
		t2.setName("窗口二");
		t3.setName("窗口三");
		t1.start();
		t2.start();
		t3.start();

	}
}

3.实现Callable接口

/*
 * 创建线程方式三  实现Callable接口
 * 实现Callable接口的方式强于实现Runnable接口的方式
 *1.call()可以有返回值。
 *2.call()可以抛出异常。
 *3.callable支持泛型。
 */

//1.创建一个实现Callable的实现类
class NumThread implements Callable{
	//2.实现call方法,将此线程需要执行的操作声明在call()方法中
	public Object call() throws Exception{
		int sum=0;
		for(int i=0;i<=100;i++) {
			if(i%2==0) {
				System.out.println(i);
				sum+=i;
			}
		}
		return sum;
	}
}
public class CallableWay {
	public static void main(String[] args) {
		//3.创建Callable接口实现类的对象
		NumThread numThread =new NumThread();
		//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask对象
		FutureTask futureTask =new FutureTask(numThread);
		//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
		new Thread(futureTask).start();
		try {
			//6.获取Callable中call()的返回值
			//get()返回值即为FutureTask构造参数Callable实现类重写的call()的返回值
			Object sum = futureTask.get();
			System.out.println("总和为:"+sum);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

4.使用线程池

/*
 * 创建线程的方式四:使用线程池
 * 好处:
 * 1.提高响应速度(提高了创建新线程的时间)
 * 2.降低资源的消耗(重复利用线程池中线程,不需要每次都创建)
 * 3.便于线程管理
 */
class NumberThread implements Runnable{
		public void run() {
			for(int i=0;i<=100;i++) {
				if(i%2==0) {
					System.out.println(Thread.currentThread().getName()+":"+i);
				}
			}
		}
}
public class Thread4 {
	public static void main(String[] args) {
		//1.提供指定线程数量的线程池
		ExecutorService service =Executors.newScheduledThreadPool(10);
		//2.执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
		service.execute(new NumberThread()); //适用于Runnable
		//service.submit(); //适用于Callable
		//3.关闭线程池
		service.shutdown();
	}

}

线程的优先级

MAX_PRIORITY:10

MIN_PRIORITY:1

NORM_PRIORITY:5 —>默认优先级

如何获取和设置当前线程优先级:

getpriority()		//获取线程的优先级
setpriority(int p)		//设置线程的优先级

说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上来讲,高优先级的线程高概率的情况下被执行,并不意味着只有高优先级的线程执行完后,低优先级的线程才执行。

测试Thread中常用的方法

1.star():启动当前线程;调用当前线程的run()
2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThread():静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yield():释放当前线程cpu的执行权,各个线程重新“竞争”7.join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完之后,线程a才结束阻塞状态
8.stop():已过时。当执行此方法时,强制结束当前线程
9.sleep(long millitime):让当前线程“睡眠”指定millitime毫秒。在指定的时间内,当前线程是处于阻塞状态
10.isAlive():判断当前线程是否存活

线程的生命周期

多线程的同步控制

1.同步代码块

synchronized(同步监视器){
 	// 需要被同步的代码
 }

下面展示实现Runnable接口的情况。

class TicketWindow implements Runnable {
    private int ticket = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

public class ThreadSync {
    public static void main(String[] args) {
        TicketWindow ticketWindow = new TicketWindow();

        Thread thread1 = new Thread(ticketWindow);
        Thread thread2 = new Thread(ticketWindow);
        Thread thread3 = new Thread(ticketWindow);

        thread1.setName("售票窗口1");
        thread2.setName("售票窗口2");
        thread3.setName("售票窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

下面展示继承Thread类的情况。

class TicketWindow1 extends Thread {    private static int ticket = 100;    private static Object obj = new Object();    @Override    public void run() {        while (true) {            synchronized (obj) {                if (ticket > 0) {                    try {                        Thread.sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");                    ticket--;                } else {                    break;                }            }        }    }}public class ThreadSync1 {    public static void main(String[] args) {        Thread thread1 = new TicketWindow1();        Thread thread2 = new TicketWindow1();        Thread thread3 = new TicketWindow1();        thread1.setName("售票窗口1");        thread2.setName("售票窗口2");        thread3.setName("售票窗口3");        thread1.start();        thread2.start();        thread3.start();    }}class TicketWindow1 extends Thread {
    private static int ticket = 100;
    private static Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

public class ThreadSync1 {
    public static void main(String[] args) {
        Thread thread1 = new TicketWindow1();
        Thread thread2 = new TicketWindow1();
        Thread thread3 = new TicketWindow1();

        thread1.setName("售票窗口1");
        thread2.setName("售票窗口2");
        thread3.setName("售票窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

对比二者不同二者不同在于synchronized(同步监视器)中的同步监视器,

实现Runnable接口的情况下,同步监视器不需要用static,因为Runnable接口的实现类只被创建一次,三个线程的同步监视器是同一个。而继承Thread类的情况下,同步监视器如不声明为static则被声明了三次,三个线程的同步监视器不是同一。

2.同步方法

访问修饰符 synchronized 返回值 方法名(参数列表) {
	// 同步代码块
}

下面展示实现Runnable接口的情况。

class TicketWindow implements Runnable {
    private int ticket = 100;

    @Override
    public void run() {
        while (ticket > 0) {
            sellTicket();
        }
    }

    public synchronized void sellTicket() {
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
            ticket--;
        }
    }
}

下面展示继承Thread类的情况。

class TicketWindow1 extends Thread {
    private static int ticket = 100;

    @Override
    public void run() {
        while (ticket > 0) {
            sellTicket();
        }
    }

    /**
     * 通过继承Thread类实现的多线程,同步方法必须为静态方法,因为非静态的同步方法,同步监视器为this,
     *         Thread thread1 = new TicketWindow1();
     *         Thread thread2 = new TicketWindow1();
     *         Thread thread3 = new TicketWindow1();
     * 而上述的this不唯一,因此无法实现对共享资源的互斥访问。
     */
    public static synchronized void sellTicket() {
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
            ticket--;
        }
    }
}

3.同步锁

// 1 获得一个锁
Lock lock = new ReentrantLock();

// 2 加锁
lock.lock();
// 同步代码块

// 3 解锁
lock.unlock();

下面展示代码实现。

class TicketWindowLock implements Runnable {
    private int ticket = 100;
    // 1 获得一个锁
    private Lock lock = new ReentrantLock();
    /**
     * private ReentrantLock lock = new ReentrantLock(true);
     * 带参数的构造方法:ReentrantLock(boolean fair); ==> 公平锁
     *      所谓公平锁:假设现在三个卖票线程按 1、2、3 顺序先后到达并争取锁,但是窗口1获得了锁并卖票,
     *      窗口2、3等待,等窗口1释放锁后,窗口2、3再按顺序获得锁并卖票,保证按照先到先得的顺序获得锁,
     *      以此保证公平性。
     *
     *      若是使用无参构造方法获得锁,则不保证公平性。同样的,三个卖票线程按 1、2、3 顺序先后到达并
     *      争取锁,但是窗口1获得了锁并卖票,窗口2、3等待,窗口1释放锁后,有可能再次获得锁并卖票,窗
     *      口2、3仍然等待,不保证公平性。
     */

    @Override
    public void run() {
        while (true) {

            try {
                // 2 上锁
                lock.lock();

                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + " 卖出第 " + ticket + " 张票");
                    ticket--;
                } else {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 3 解锁。使用 try catch finally,将解锁操作放在finally语句块中,保证锁一定会被释放
                lock.unlock();
            }
        }
    }
}

synchronized与lock锁有何异同

  • 相同:二者都是用来解决线程安全问题
  • 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器,lock需要手动的打开释放。

线程通信

wait/notify模式

涉及到的三个方法

1.wait():一旦执行此方法,当前线程就会进入阻塞状态,并且释放同步监视器。

2.notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就优先唤醒高优先级的线程

3.notifyAll():一旦执行此方法,所有的线程都会被唤醒

说明上述三个方法都必须使用在同步代码块或同步方法中的同步监视器中,否则会出现异常

上述三个方法都是定义在Object类中的。

sleep和wait的异同

相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。

不同点:

1)俩个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()

2)调用的要求不同:sleep可以在任何场景下调用,而wait只能在同步代码块或者同步方法的同步监视器中

3)关于是否释放同步监视器:如果俩个方法都使用在同步代码块或同步方法中,sleep()不释放,而wait()释放。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 高价值Java多线程面试题分析

    问题一 A线程正在执行一个对象中的同步方法,B线程是否可以同时执行同一个对象中的非同步方法? 可以,两个线程运行所需资源不同,不需要抢占. 案例一. package duoxiancheng2; /** * @author yeqv * @program A2 * @Classname Ms1 * @Date 2022/2/7 19:08 * @Email w16638771062@163.com */ public class Ms1 { //A线程正在执行一个对象中的同步方法,B线程是否可以

  • Java十分钟入门多线程中篇

    目录 1.线程的调度: 1.设置优先级(Priority): 2.休眠(sleep) 3.强制运行(join) 4.礼让(yield) 2.定时器线程: 3.线程的同步: 举例说明: 我们知道飞机在天上飞行是有固定的航线(可以理解成线程),每个机场都有最大的运行负载能力,当运行情况超过了负载能力的时候,这就需要塔台调度参与,会根据每架飞机的优先级排序.当在航线的时候,如果出现紧急情况,会让其他飞机避让,让这架飞机优先级提高,先降落.这就是调度,计算机程序线程运行也是这样的. 1.线程的调度: 在

  • Java十分钟入门多线程上篇

    什么是多线程? 在学习前,我们先对程序.进程.线程.并行.并发有个基础的概念了解: 程序: 为完成指定任务,用编程语言编写的一组指令的集合,即指一段静态的代码,静态对象. 进程: 是程序的一次执行过程,是一个动态的过程,进程自身有产生.使用和消亡的过程.(也称为生命周期,在后面会介绍) 线程: 进程可进一步细化为线程,是一个程序内部的一条执行路径,也就是进程内有线程 并行: 指两个或者多个事件在同一时刻发生,(同时发生) 并发: 指两个或者多个事件在同一个时段内发生,(并不是同时发生) 更好的理

  • Java十分钟入门多线程下篇

    目录 1.线程池: 2.创建线程池: 1.newCacheThreadPool: 2.newSingleThreadExecutor: 3.newFixedThreadPool(inta): 4.newScheduledTreadPool: 3.线程池创建自定义线程: 4.Runnable和Callable的区别: 5.线程池总结: 1.线程池: 什么是线程池? 咱们也不看长篇大论,通俗的来讲,线程池就是装线程的容器,当需要用的时候去池里面取出来,不用的时候放回去或者销毁.这样一个线程就可以反复

  • Java多线程之线程安全问题详解

    目录 1.什么是线程安全和线程不安全? 2.自增运算为什么不是线程安全的? 3.临界区资源和竞态条件 总结: 面试题: 什么是线程安全和线程不安全? 自增运算是不是线程安全的?如何保证多线程下 i++ 结果正确? 1. 什么是线程安全和线程不安全? 什么是线程安全呢?当多个线程并发访问某个Java对象时,无论系统如何调度这些线程,也无论这些线程将如何交替操作,这个对象都能表现出一致的.正确的行为,那么对这个对象的操作是线程安全的. 如果这个对象表现出不一致的.错误的行为,那么对这个对象的操作不是

  • 一篇文章带你Java多线程入门

    目录 多线程的四种创建方式 1.继承Thread类 2.实现Runnable接口 3.实现Callable接口 4.使用线程池 线程的优先级 测试Thread中常用的方法 线程的生命周期 多线程的同步控制 1.同步代码块 2.同步方法 3.同步锁 线程通信 wait/notify模式 sleep和wait的异同 总结 多线程的四种创建方式 1.继承Thread类 /* * 多线程的创建,方式一:继承Thread类 * 1.创建一个继承于Thread类的子类 * 2,重写Thread类的run()

  • 一篇文章带你Java Spring开发入门

    目录 Spring概述 Spring简单应用 Spring框架地基本使用 项目创建 添加依赖包 创建Spring配置文件 修改配置文件 修改测试类 运行结果 总结 Spring概述 Spring就是为解决企业应用开发的复杂性而创建的,做为开源中间件,它使用基本的JavaBean来完成以前只可能有EJB(Java企业bean)完成的事情.Spring独立于各种应用服务器,甚至无须应用服务器的支持也能提供应用服务器的功能,同时为JavaEE应用程序开发提供继承的框架,是企业应用开发的"一站式&quo

  • 一篇文章带你搞懂Java线程池实现原理

    目录 1. 为什么要使用线程池 2. 线程池的使用 3. 线程池核心参数 4. 线程池工作原理 5. 线程池源码剖析 5.1 线程池的属性 5.2 线程池状态 5.3 execute源码 5.4 worker源码 5.5 runWorker源码 1. 为什么要使用线程池 使用线程池通常由以下两个原因: 频繁创建销毁线程需要消耗系统资源,使用线程池可以复用线程. 使用线程池可以更容易管理线程,线程池可以动态管理线程个数.具有阻塞队列.定时周期执行任务.环境隔离等. 2. 线程池的使用 /** *

  • 一篇文章带你了解Java中ThreadPool线程池

    目录 ThreadPool 线程池的优势 线程池的特点 1 线程池的方法 (1) newFixedThreadPool (2) newSingleThreadExecutor (3) newScheduledThreadPool (4) newCachedThreadPool 2 线程池底层原理 3 线程池策略及分析 拒绝策略 如何设置maximumPoolSize大小 ThreadPool 线程池的优势 线程池做的工作主要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些

  • 一篇文章带你顺利通过Python OpenCV入门阶段

    目录 1. OpenCV 初识与安装 2. OpenCV 模块简介 3. OpenCV 图像读取,显示,保存 4. 摄像头和视频读取,保存 5. OpenCV 常用数据结构和颜色空间 6. OpenCV 常用绘图函数 7. OpenCV 界面事件操作之鼠标与滑动条 8. 图像像素.通道分离与合并 9. 图像逻辑运算 10. 图像 ROI 与 mask 掩膜 11. 图像几何变换 12. 图像滤波 13. 图像固定阈值与自适应阈值 14. 图像膨胀腐蚀 15. 边缘检测 16. 霍夫变换 17.

  • 一篇文章带你了解Java Spring基础与IOC

    目录 About Spring About IOC Hello Spring Hello.java Beans.xml Test.java IOC创建对象的几种方式 Spring import settings Dependency Injection 1.构造器注入 2.set注入 3.拓展注入 P-namespcae&C-namespace Bean scopes singleton prototype Bean的自动装配 byName autowire byType autowire 小结

  • 一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis)

    一.前期配置 1. 加入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> &

  • 一篇文章带你搞懂Python类的相关知识

    一.什么是类 类(class),作为代码的父亲,可以说它包裹了很多有趣的函数和方法以及变量,下面我们试着简单创建一个吧. 这样就算创建了我们的第一个类了.大家可以看到这里面有一个self,其实它指的就是类aa的实例.每个类中的函数只要你不是类函数或者静态函数你都得加上这个self,当然你也可以用其他的代替这个self,只不过这是python中的写法,就好比Java 中的this. 二.类的方法 1.静态方法,类方法,普通方法 类一般常用有三种方法,即为static method(静态方法),cl

  • 一篇文章带你搞定Python多进程

    目录 1.Python多进程模块 2.Python多进程实现方法一 3.Python多进程实现方法二 4.Python多线程的通信 5.进程池 1.Python多进程模块 Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Process对象来创建一个进程对象.这个进程对象的方法和线程对象的方法差不多也有start(), run(), join()等方法,其中有一个方法不同Thread线

  • 一篇文章带你了解清楚Mysql 锁

    一丶为什么数据库需要锁 数据库锁设计的初衷是处理并发问题.作为多用户共享 的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则.而锁就是用来实 现这些访问规则的重要数据结构. 根据加锁的范围,MySQL 里面的锁大致可以分成全局锁.表级锁和行锁三类 二丶全局锁&全库逻辑备份 全局锁就是对整个数据库实例加锁.全局锁的典型使用场景是,做全库逻辑备份,全库逻辑备份有以下几种方式: 1.Flush tables with read lock (FTWRL) Flush tables with

随机推荐