介绍:Java提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程。本章主要内容为:多线程的创建、启动、控制以及同步操作,并介绍JDK 5新增的线程创建方式。

一、线程的创建与使用

  1. 继承Thread类创建线程

  • 建立一个继承Thread的子类
  • 重写Thread类的run()--把所执行的操作写在该方法中
  • 创建一个子类对象
  • 通过该对象调用start()方法
 //1.1. 建立一个继承Thread的子类
class MyThread1 extends Thread{
@Override
//1.2. 重写Thread类的run()
public void run() {
for(int i=;i<;i++){
if(i%==){
System.out.println(getName() + ":" + i);//Thread.currentThread().getName()
}
}
}
} //1.3. 创建一个子类对象
MyThread1 t1 = new MyThread1();
t1.setName("线程1"); //1.4. 通过该对象调用start()方法
t1.start();//注意,一个线程对象不能同时两次start,需要重新创建一个对象来start

  2. 实现Runnable接口创建线程

  • 建立一个Runnable接口的实现类
  • 实现Runnable中的抽象方法run()
  • 创建实现类对象
  • 将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
  • 通过Thread类的对象调用start()方法
 //2.1 建立一个Runnable接口的实现类
class MyThread2 implements Runnable{
//2.2. 实现Runnable中的抽象方法run()
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} //2.3 2.4 创建实现类对象,将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
Thread t2 = new Thread(new MyThread2());
t2.setName("线程2"); //2.5. 通过Thread类的对象调用start()方法
t2.start();

  开发中优先选择后者:既没有类的单继承性的限制,而且实现的方式更适合来处理多个线程有共享数据的情况。

  3. 实现Callable接口创建线程

  • 创建一个Callable接口的实现类
  • 实现call方法,将此线程需要执行的操作写在方法体中
  • 创建Callable接口实现类的对象
  • 将上面的实现类对象作为参数创建FutureTask的对象
  • 将上面的FutureTask的对象作为参数创建Thread的对象
  • 利用Thread的对象调用start方法
  • 通过 FutureTask的对象.get() 方法获取call方法的返回值
 //3.1 创建一个Callable接口的实现类
class NumThread implements Callable{
@Override
//3.2 实现call方法,将此线程需要执行的操作写在方法体中
public Object call() throws Exception {
int sum = 0;
for(int i=1;i<=100;i++){
if(i%2==0){
System.out.println(i);
sum += i;
}
}
return sum;
}
} //3.3 创建Callable接口实现类的对象
NumThread num = new NumThread();
//3.4 将上面的实现类对象作为参数创建FutureTask的对象
FutureTask futureTask = new FutureTask(num);
//3.5 将上面的FutureTask的对象作为参数创建Thread的对象
Thread t = new Thread(futureTask);
t.setName("线程3");
//3.6 利用Thread的对象调用start方法
t.start(); try {
//3.7 通过 FutureTask的对象.get() 方法获取call方法的返回值
Object sum = futureTask.get();//get方法的返回值即为NumThread.call的返回值
System.out.println("共计:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

  相较于Runnable接口更强大、可以抛出异常、支持泛型,但是需要借助FutureTask类

  4. 使用线程池创建线程

  • 创建指定线程数量的线程池
  • 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
  • 关闭连接池
 class MyThread2 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} //4.1 创建指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);//创建确定个数的线程池
// 4.2 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
service.execute(new MyThread2());//参数只能是Runnable
service.execute(new MyThread3());//参数只能是Runnable
//4.3 关闭连接池
service.shutdown();

   5. Thread类中常见的方法

  • void start(): 启动线程,并执行对象的run()方法
  • run(): 线程在被调度时执行的操作
  • String getName(): 返回线程的名称
  • void setName(String name):设置该线程名称
  • static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
  • static void yield(): 线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法
  • join() : 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止,使得低优先级的线程也可以获得执行
  • static void sleep(long millis):(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。(抛出InterruptedException异常)
  • stop(): 强制线程生命期结束,不推荐使用
  • boolean isAlive(): 返回boolean,判断线程是否还活着
//给线程命名--方式一:通过setName()
t1.setName("线程1"); //设置该线程名称 //给线程命名--方式二:通过构造器
ThreadMethodTest2 t2 = new ThreadMethodTest2("*线程2");

  6. 线程优先级

  • MAX_PRIORITY:10
  • MIN _PRIORITY:1
  • NORM_PRIORITY:5

  涉及的方法

  • getPriority() :返回线程优先值
  • setPriority(int newPriority) :改变线程的优先级

  说明

  • 线程创建时继承父线程的优先级
  • 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

二、线程的同步

  1. 同步代码块

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

  同步监视器:俗称锁。任何一个类的对象都可以充当锁,要求多个线程必须共用同一个锁

  • Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
  • 继承Thread类的方式不能用this,可以使用 类.class 来充当

  2. 同步方法:synchronized放在方法声明中,表示整个方法为同步方法。同步方法仍然涉及同步监视器,但无需显示说明,例如:

public synchronized void show (String name){
....
}

  3. lock锁

//1. 实例化ReentrantLock
try{
//2. 调用lock
//需要被同步的代码
}finally{
//3. 调用unlock
}

  4. 案例

 /**
* 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
* 存在线程的安全问题,采用 同步代码块方式 解决
* synchronized(同步监视器){
* //需要被同步的代码
* }
* > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
* 要求多个线程必须共用同一个锁。
* > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
* > 继承Thread类的方式不能用this,可以使用 类.class 来充当
* @author shkstart
* @create 2019-02-13 下午 4:47
*/
class Window1 implements Runnable{ private int ticket = 100; //共享数据
private Object obj = new Object(); @Override
public void run() {
while(true){
synchronized (obj){ //synchronized(this)
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
} public class TicketSafeBR {
public static void main(String[] args) {
Window1 w = new Window1(); Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start();
} }

Runnable-同步代码块方式

 /**
* 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
* 存在线程的安全问题,采用 同步方法方式 解决
*
* > 同步方法仍然涉及同步监视器,但无需显示说明
* > 非静态同步方法的同步监视器是:this
* > 静态同步方法的同步监视器是:类.class
*
*
* @author shkstart
* @create 2019-02-13 下午 4:47
*/
class Window2 implements Runnable{ private int ticket = 100; @Override
public void run() {
while(true){
show();
}
} private synchronized void show(){ //同步监视器为this
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
} public class TicketSafeMR {
public static void main(String[] args) {
Window2 w = new Window2(); Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start();
} }

Runnable-同步方法方式

 /**
*
* 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
*
* 存在线程的安全问题,采用 同步代码块方式 解决
* synchronized(同步监视器){
* //需要被同步的代码
* }
* > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
* 要求多个线程必须共用同一个锁。
* > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
* > 继承Thread类的方式不能用this,可以使用 类.class 来充当(类.class只会加载一次)(反射)
* @author shkstart
* @create 2019-02-13 下午 4:20
*/
class Window extends Thread{
private static int ticket = 100;
//要求多个线程必须共用同一个锁,因此使用了static,
private static Object obj = new Object();//一定要注意,这里与Runnable接口的方式有所不同
@Override
public void run() { while(true){
synchronized (obj){ //synchronized(Window.class)
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
} public class TicketSafeBT {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window(); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); }
}

Thread-同步代码块方式

 /**
*
* 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
*
* 存在线程的安全问题,使用 同步方法方式 来解决
*
* > 同步方法仍然涉及同步监视器,但无需显示说明
* > 非静态同步方法的同步监视器是:this
* > 静态同步方法的同步监视器是:类.class
*
*
* @author shkstart
* @create 2019-02-13 下午 4:20
*/
class Window3 extends Thread{ private static int ticket = 100;
@Override
public void run() {
while(true){
show();
}
} private static synchronized void show(){ //同步监视器:Window3.class
//private static synchronized void show(){ //此种方法错误,同步监视器:t1、t2、t3(不唯一)
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
} public class TicketSafeMT {
public static void main(String[] args) {
Window3 t1 = new Window3();
Window3 t2 = new Window3();
Window3 t3 = new Window3(); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); }
}

Thread-同步方法方式

 public class LockTest {
public static void main(String[] args) {
Window4 w = new Window4(); Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); } } class Window4 implements Runnable{ private int ticket = 100; //1. 实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock(); @Override
public void run() {
while (true){
try{
//2. 调用lock
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
ticket--;
}else
break;
}finally {
//3. 调用unlock
lock.unlock();
}
}
}
}

Lock锁方式

三、线程的通信

  • wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
  • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
  • notifyAll ():唤醒正在排队等待资源的所有线程结束等待.
 /**
* 线程的通信:
* 两个线程交替打印1-100:
* > wait()
* > notify()/notifyAll()
* > 注意:wait和notify的调用者必须是同一个对象
* > wait和notify必须使用在同步代码块或者同步方法中
*
* @author marc
* @creat 2020-02-07 上午9:22
*/
public class MsgTest {
public static void main(String[] args) {
Number n1 = new Number(); Thread t1 = new Thread(n1);
Thread t2 = new Thread(n1); t1.setName("线程1");
t2.setName("线程2"); t1.start();
t2.start(); }
} class Number implements Runnable{
private int i = 1;
@Override
public void run() {
while (true){ synchronized (this) { notify(); //this.notify() if(i<=100){
System.out.println(Thread.currentThread().getName() + ":" + i);
i++; try {
wait(); //this.wait()
} catch (InterruptedException e) {
e.printStackTrace();
} }else
break;
} }
}
}

Java入门笔记 05-多线程的更多相关文章

  1. [Java入门笔记] 面向对象编程基础(二):方法详解

    什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...

  2. JAVA自学笔记05

    JAVA自学笔记05 1.方法 1)方法就是完成特定功能的代码块,类似C语言中的函数. 2)格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,-){ 函数体; return ...

  3. Java入门笔记 00-前言&目录

    前言:这本笔记记录的是Java基础部分的学习内容,大部分内容总结性的,包括: ---01 Java基础语法 ---02 数组 ---03 面向对象 ---04 异常处理 ---05 多线程 ---06 ...

  4. [Java入门笔记] Java语言简介

    前言 学习Java有一段时间了,但是一直缺少一个系统的思想,现在重新通过书籍中.网上的文章,视频等资料,整理与回顾Java的知识点. 言归正传,让我们先从了解Java语言开始. Java语言的由来 J ...

  5. 【原】Java学习笔记032 - 多线程

    package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...

  6. Java学习笔记之——多线程

    多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...

  7. Java学习笔记:多线程(一)

    Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...

  8. JAVA复习笔记之多线程并发

    前言:多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,还是值得深入研究一下 概念: 1 线程:进程中负责程序执行的执行单元线程本身依靠程序进行运行线程是程序中的顺序控制流,只能使用 ...

  9. Java精选笔记_多线程(创建、生命周期及状态转换、调度、同步、通信)

    线程概述 在应用程序中,不同的程序块是可以同时运行的,这种多个程序块同时运行的现象被称作并发执行. 多线程可以使程序在同一时间内完成很多操作. 多线程就是指一个应用程序中有多条并发执行的线索,每条线索 ...

  10. 萌新java入门笔记

    首先声明以下内容只是散乱笔记,如果有误还望大侠指出!不胜感激! 基本数据类型: 大体和C语言类似: boolean truth = true;//逻辑型 //文字型 char c; String st ...

随机推荐

  1. jdk之java.lang.Integer源码理解

    基本数据类型的包装类java.lang.Integer是我们频繁使用的一个系统类,那么通过一个示例反应出的几个问题来深入理解一下此类的源码. 需求:实现Integer类型的两个数值交换. packag ...

  2. Java中的Collections类

    转载:https://blog.csdn.net/yangxingpa/article/details/80515963 从[Java]Java中的Collections类——Java中升级版的数据结 ...

  3. 不可将布尔值直接与true或者1进行比较

    不可将布尔值直接与TRUR.FALSE或者"0"."1"进行比较. 根据布尔值的定义,零值为"假"(记为FALSE),任何非零值都是&quo ...

  4. 1059 Prime Factors (25分)

    1059 Prime Factors (25分) 1. 题目 2. 思路 先求解出int范围内的所有素数,把输入x分别对素数表中素数取余,判断是否为0,如果为0继续除该素数知道余数不是0,遍历到sqr ...

  5. Tomcat配置过程

    Tomcat的配置其实还是挺简单的,下面是在Myeclipse中配置 1.首先要在Tomcat官网下载,网址:http://tomcat.apache.org/,然后左侧会有Download,选择你要 ...

  6. Dockerfile书写介绍及构建ssh镜像、tomcat镜像、nginx镜像

    =================================================================================================== ...

  7. Codeforces 131C . The World is a Theatre(打表组合数)

    题目链接:http://codeforces.com/contest/131/problem/C 大意就是有n个男孩,m个女孩,从男孩中选不少于4个男孩,女孩中选不少于1个女孩,组成人数为t的队伍,问 ...

  8. Git-配置SSH公钥

    前言:Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置. 以下操作都在git-bash命令行中进行. 查看所有配置项: git config --l ...

  9. dw选择器

    选择器并没有一个固定的定义,在某种程度上说,jQuery的选择器和样式表中的选择器十分相似.选择器具有如下特点:1.简化代码的编写2.隐式迭代3.无须判断对象是否存在jQuery 的选择器可谓之强大无 ...

  10. Maven安装与学习

    一.安装 1.下载http://maven.apache.org/download.cgi 2.选择zip格式 安装完后解压到某一位置(E:\xitong\major\apache-maven-3.6 ...