多线程

1、线程的概念
? 多线程,就类似与操作系统中的多进程。简单的讲,就是可 以同时并发执行多个任务,处理多件事情。这与我们经常所 谓的边唱边跳,边说边做事一个道理。
? 线程是一个轻量级的进程,一个进程中可以分为多个线程。 比起进程,线程所耗费的系统资源更少,切换更加容易

/*
 * 进程是操作系统中的一个任务,一个程序启动运行,就会创建
 * 一个(或多个)进程。
 * 线程是轻量级的进程。进程会有自己独立的内存空间与资源。一个进程
 * 下会存在一个(或多个)线程。线程为进程的执行单元。线程本身不含有
 * 独立的资源,而是共享进程中的资源。
 *
 * 多线程的优势:
 * 1 使用多线程可以提高CPU的利用率。
 * 2 使用多线程可以提供更好的动态性与交互性。
 */
package day18;

public class HelloWorld {

	public static void main(String[] args) {
		// 无限循环,用来查看java程序的进程。
		// Java程序在运行时,就会存在一个进程。
		// 一个进程至少要包含一个线程,Java线程就是
		// 执行main方法的main线程(线程的名字叫做main)。
		while (true)
			;
	}
}

  2、Thread类
? Thread是Java中的线程类,我们可以通过继承Thread类来实 现多线程操作。
? 继承Thread类,重写run方法, run方法中的代码即为我们需 要线程执行的任务,然后调用对象的start方法即可启动线程。
说明:
? 不是调用run方法,而是调用start方法,否则无法实现多线 程。
? 执行main方法就会创建一个线程。

/*
 * Thread类就表示线程。可以用来创建线程。
 * 通过继承Thread类实现多线程。
 */
package day18;

public class ThreadTest {
	public static void main(String[] args) {
		// 创建线程
		Mission1 m = new Mission1();
		Mission2 m2 = new Mission2();
		// 创建线程后,需要启动线程,线程才能够执行。
		// 调用start方法来启动一个线程,是线程处于就绪
		// 状态。线程处于就绪状态,不代表该线程会马上得到执行,
		// 具体何时执行,要取决于操作系统的调度。线程处于就绪
		// 状态,表示该线程有机会获得CPU的时间片,即有机会
		// 得到执行。
		m.start();
		m2.start();
	}
}

// run方法就是线程要执行的任务。
// 我们重写Thread类的run方法。
// 线程从run方法开始执行。
class Mission1 extends Thread {
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			System.out.println(i);
			// 令当前线程休眠参数指定的时间(毫秒)
			// 处于休眠的线程会放弃掉当前的时间片,
			// 并且在整个休眠期间,不会获得CPU的时间片。
			// 当线程苏醒时,线程会处于就绪状态。(不是运行状态)。
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 获取线程的名字。
			System.out.println(getName());
		}
	}
}

class Mission2 extends Thread {
	@Override
	public void run() {
		for (int i = 11; i <= 20; i++) {
			System.out.println(i);
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

  3、Runnable接口
除了继承Thread类以外,还可以通过实现Runnable接口来实现 多线程。
? 首先,我们要创建一个类,该类实现Runnable接口, Runnable接口中存在一个抽象方法run,因此,我们需要实 现(重写) run方法。
? 然后创建该类的对象,将对象作为Thread的运行目标。 ( target)。

/*
 * 通过实现Runnable接口实现多线程
 */
package day18;

public class RunnableTest {
	public static void main(String[] args) {
		Mission m = new Mission();
		// 使用实现Runnable接口的对象,作为
		// 线程的目标执行体。
		Thread t1 = new Thread(m);
		Thread t2 = new Thread(m);
		t1.start();
		t2.start();
	}
}

class Mission implements Runnable {
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			System.out.println(i);
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 无法直接调用getName(),因为当前类没有继承Thread,
		// 不是Thread类的子类。
		// getName();
		// 获取当前的执行的线程。
		Thread current = Thread.currentThread();
		current.getName();
	}
}

  4、线程的生命周期
线程的生命周期可以分为以下环节:
? 新建-创建对象
? 就绪-调用start后
? 运行-获得CPU资源
? 阻塞(挂起) -失去CPU资源
? 死亡-线程执行结束或抛出未捕获的异常

5、常用方法

public static Thread currentThread(); 获得当前执行的线程。
? public String getName() 获得线程名称。
? public final native boolean isAlive(); 判断线程是否存活
(在start方法调用后,并且线程没有死亡)。
? public void join() 等待直到这个线程死亡。
? public static void sleep(long millis) 使当前线程睡眠(暂时
停止执行) millis毫秒。如果当前程序存在其他等待的线程,
则其他线程会获得执行机会。? public static void yield();当前运行的线程有意让出CPU资源,由线程调度器重新选择线程调度。不过,这仅仅是一个提示而已,线程调度器可能会忽略。? public void setDaemon(boolean on);设置线程是否为后台线程。? public void setPriority(int newPriority)设置线程的优先级。优先级为1-10。优先级高的线程仅意味着可能获得更多执行的机会,不表示一定会一直执行,优先级低的线程仍然有机会执行。

6、yield方法

package day18;

public class YieldTest {
	public static void main(String[] args) {
		Thread2 t = new Thread2();
		Thread3 t2 = new Thread3();
		t.start();
		t2.start();
	}
}

class Thread2 extends Thread {
	@Override
	public void run() {
		for (int i = 0; i <= 5; i++) {
			System.out.println(i);
		}
		// 当前线程有意让出CPU资源,让其他线程得到执行。
		// 但是,操作系统可能会忽略线程的这种请求。
		Thread.yield();
		for (int i = 6; i <= 9; i++) {
			System.out.println(i);
		}
	}
}

class Thread3 extends Thread {
	@Override
	public void run() {
		for (int i = 100; i < 110; i++) {
			System.out.println(i);
		}
	}
}

7、

package day18;

//执行main方法的线程名就叫main。
public class ThreadMethod {

	public static void main(String[] args) {
		// 获取当前执行的线程。
		Thread current = Thread.currentThread();
		// 获取线程的名字。
		// System.out.println(current.getName());
		// 判断当前线程是否处于存活状态(调用start方法之后,
		// 线程死亡之前),存活返回true,否则返回false。
		// System.out.println(current.isAlive());

		ThreadA ta = new ThreadA();
		ta.start();
		try {
			// 如果在A线程中调用B线程对象的join方法(无参),则A线程
			// 会一直等待B线程执行结束,A线程才会继续执行。
			ta.join();
			// 当前线程最多等待ta线程参数指定的时间。(毫秒)
			// ta.join(500);
			// 当前线程最多等待第一个参数的时间(毫秒)加上
			// 第二个参数的时间(纳秒)。(时间的和)
			// ta.join(500, 100);
			// 令当前线程休眠参数指定的时间。
			// Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("main方法输出内容");
		ThreadA ta2 = new ThreadA();
		// 设置当前线程是否为后台线程,true为后台线程,否则
		// 为前台线程。该方法需要在线程启动之前调用。
		ta2.setDaemon(true);
		// 设置线程的优先级。优先级高的线程不代表会一直执行,
		// 只是执行几率大于优先级低的线程。虽然Java提供了
		// 1-10这十个优先级,但是,操作系统未必会存在十个优先级
		// 有Java相对应。
		ta2.setPriority(Thread.NORM_PRIORITY);

	}
}

class ThreadA extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

8、中断线程

package day18;

public class InterruptTest {
	public static void main(String[] args) {
		Thread4 t = new Thread4();
		t.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 中断线程。
		t.interrupt();
	}
}

class Thread4 extends Thread {
	@Override
	public void run() {
		try {
			// 如果线程处于sleep,join等等待方法中,如果
			// 在等待期间,线程被其他线程所中断,则会产生
			// InterruptedException异常。如果没有处于
			// 以上等待方法中,则线程被其他线程所终端,不会
			// 产生InterruptedException异常。
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			System.out.println("产生了InterruptedException异常");
		}
	}
}

9、线程同步
当多线程并发运行时,多线程间很可能操作共享成员变量,此 时,就需要对共享成员变量的操作进行同步,避免出现多线程 的并发修改而引起的意外错误。

当多线程并发修改公共变量时,我们就需要对公共变量的修改 部分进行同步,避免出现并发修改错误。

线程同步可以使用:
? 同步块
? 同步方法

同步的代码在同一时刻,至多只会有一个线程执行。其使用线 程锁机制来保证。

/*
 * 当多线程对共享变量并发进行修改时,就可能会出现
 * 并发修改的不一致性。
 * 对共享变量进行并发修改的区域,我们称之为共享区域。
 * 对于共享区域,我们必须要实现上锁(线程的互斥访问)。
 *
 * 对于共享变量,要完全放在同步区域当中,否则就可能会出现
 * 问题。(存在修改共享变量)
 */
package day18;

public class BuyTicket {
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread t1 = new Thread(ticket);
		Thread t2 = new Thread(ticket);
		Thread t3 = new Thread(ticket);
		// 设置线程的名字
		t1.setName("张三");
		t2.setName("李四");
		t3.setName("王五");
		t1.start();
		t2.start();
		t3.start();
	}
}

class Ticket implements Runnable {
	private int num = 100;
	// 任意对象都可以充当共享区域的锁。
	private Object lock = new Object();
	// 因为任意对象都可以充当锁的角色,因此,我们没有必要
	// 单独创建一个对象锁,使用当前对象this来充当锁就
	// 可以了。

	@Override
	public void run() {
		// while (num > 0) {
		// synchronized (lock) {
		// String name = Thread.currentThread().getName();
		// System.out.println(name + "抢到第" + num + "张票");
		// num--;
		// }
		// }
		// 虽然可以这样实现,但是这样做,完全退化成单线程。
		// synchronized (lock) {
		// while (num > 0) {
		// String name = Thread.currentThread().getName();
		// System.out.println(name + "抢到第" + num + "张票");
		// num--;
		// }
		// }
		while (true) {
			synchronized (this) {
				if (num > 0) {
					String name = Thread.currentThread().getName();
					System.out.println(name + "抢到第" + num + "张票");
					num--;
				} else {
					break;
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	/*
	 * 声明同步方法,使用synchronized修饰。同步方法的整个 方法体都是共享区域,都是互斥的进行访问的。
	 * 对于实例方法,使用当前对象this充当锁。 对于静态方法,使用当前类型的Class对象充当锁。
	 */
	public synchronized void f() {

	}

	public synchronized void g() {

	}

	public void k() {
		synchronized (this) {

		}
	}

	public void n() {
		synchronized (lock) {

		}
	}
}

  

package day18;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BuyTicket2 {

	public static void main(String[] args) {

	}
}
/*
 * Lock与synchronized
 * Lock是JDK1.5新增的内容,更加贴近与面向对象化。
 * synchronized如果使用break,或者产生了一个未捕获的异常,
 * 同步区域的锁可以自动得到释放,但是Lock不能。为了确保Lock
 * 能够一定被解锁,将unlock方法写在finally语句块中。
 */

class Ticket2 implements Runnable {
	private int num = 100;
	private Lock lock = new ReentrantLock();

	@Override
	public void run() {
		while (true) {
			try {
				// 加锁
				lock.lock();
				if (num > 0) {
					// 输出一大堆
					num--;
				} else {
					break;
				}
			} finally {
				// 解锁,在finally中调用,确保锁能够
				// 被解开。
				lock.unlock();
			}
		}
	}
}

10、死锁
? 当两个或多个线程同时拥有自己的资源,而相互等待获得对 方资源,导致程序永远陷入僵持状态,这就是死锁。
? 当多线程并发访问共享数据时,使用同步操作可以避免多线 程并发修改带来的危害,但同时有可能会产生死锁。

/*
 * 死锁
 * 死锁就是两个线程分别拥有自己的资源,而想要获得对方的资源,
 * 从而处于无限的僵持与等待之中。
 */
package day18;

public class DeadLock {
	public static void main(String[] args) {
		Fighter f1 = new Fighter("张三");
		Fighter f2 = new Fighter("李四");
		Thread t1 = new Thread(() -> {
			f1.hold(f2);
		});
		Thread t2 = new Thread(() -> {
			f2.hold(f1);
		});
		t1.start();
		t2.start();
	}
}

class Fighter {
	private String name;

	public Fighter(String name) {
		this.name = name;
	}

	public synchronized void hold(Fighter f) {
		System.out.println(name + "抓住了" + f.name);
		System.out.println(name + "等待" + f.name + "的放手");
		// 等待着对方先放开自己
		f.loose(this);
		// 然后自己再放开对方
		this.loose(f);
	}

	public synchronized void loose(Fighter f) {
		System.out.println(name + "放开了" + f.name);
	}
}

11、等待与唤醒
? 在多线程通信时,在某些特定条件下,我们需要线程做出一 定的“让步”,否则就很容易造成双方(或多方)进行僵持 状态,进而形成死锁。

sleep方法虽然能使当前线程阻塞,但是sleep方法不会释放其  所占有的任何“锁”。而且,也不能保证线程苏醒后,条件就 一定会得到满足。

我们可以使用以下方法实现线程的阻塞,并且令线程暂时释放 “锁”资源。以下方法都是在Object类中声明的(这意味着什
么?)。

? wait 令当前线程等待,直到另一个线程调用为该对象调用 notify或notifyAll方法。当前线程必要拥有该对象的锁。当调 用wait方法后,线程会释放掉其占有的锁,并处于等待队列中。

? notify 唤醒等待该对象锁的一个线程,如果有多个线程处于 等待中,仅唤醒一个。具体哪一个,取决于底层的实现(这
又意味着什么?)。

? notifyAll唤醒等待该对象锁的所有线程。
说明: wait, notify与notifyAll方法调用时,当前线程一定要拥 有对象的锁。否则将会引发IllegalMonitorStateException异常。

package day18;

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

public class Product {

	public static void main(String[] args) {
		Worker w = new Worker();
		Thread t1 = new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				w.product();
			}
		});
		Thread t2 = new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				w.consume();
			}
		});
		t1.start();
		t2.start();
	}
}

class Worker {
	private List<String> list = new ArrayList<>();

	public synchronized void product() {
		if (list.size() == 3) {
			System.out.println("仓库已满,生产阻塞。");
			try {
				// 释放掉当前占用的锁资源,使自身处于阻塞队里当中。
				// 调用该方法的线程必须要具有锁资源,否则就会产生异常
				// (IllegalMonitorStateException)。
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			list.add("");
			// 通知(唤醒)之前处于阻塞队列的线程。
			notifyAll();
		}
	}

	public synchronized void consume() {
		if (list.size() == 0) {
			System.out.println("仓库已空,消费阻塞。");
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			list.remove(0);
			notifyAll();
		}
	}
}

  

package day18;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Product2 {

	public static void main(String[] args) {
		Worker2 w = new Worker2();
		Thread t1 = new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				w.product();
			}
		});
		Thread t2 = new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				w.consume();
			}
		});
		t1.start();
		t2.start();
	}
}

/*
 * Condition与Lock联合使用。
 */
class Worker2 {
	private List<String> list = new ArrayList<>();
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();

	public void product() {
		try {
			lock.lock();
			if (list.size() == 3) {
				System.out.println("仓库已满,生产阻塞。");
				try {
					condition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				list.add("");
				// condition.signal();
				condition.signalAll();
			}
		} finally {
			lock.unlock();
		}
	}

	public void consume() {
		try {
			lock.lock();
			if (list.size() == 0) {
				System.out.println("仓库已空,消费阻塞。");
				try {
					condition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				list.remove(0);
				condition.signalAll();
			}
		} finally {
			lock.unlock();
		}
	}
}

  

时间: 2024-10-06 03:36:16

多线程的相关文章

java核心技术-多线程基础

进程.线程 ? 进程(Process) 是程序的运行实例.例如,一个运行的 Eclipse 就是一个进程.进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位.线程(Thread)是进程中可独立执行的最小单位.一个进程可以包含多个线程.进程和线程的关系,好比一个营业中的饭店与其正在工作的员工之间的关系. 1.1 线程的创建.启动与运行 在 Java 中实现多线程主要用两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口.(当然还有Callable和线程池).下

java核心技术-多线程之线程基础

说起线程,无法免俗首先要弄清楚的三个概念就是:进程.线程.协程.OK,那什么是进程,什么是线程,哪协程又是啥东西.进程:进程可以简单的理解为运行在操作系统中的程序,程序时静态代码,进程是动态运行着的代码,程序的运行需要向操作系统申请资源比如内存,文件句柄等,特别强调的是进程申请的资源都是独立的,也就是进程与进程之间资源是独立的.它被操作系统调度,所以进程是相对于操作系统的:线程:线程是进程中程序执行任务的那个,它共享着进程申请的资源:协程:可以简单的说是线程制造的轻量线程.讲完了基本的概念看看三

《Java核心技术 卷1 基础知识 原书第9版》pdf

下载地址:网盘下载 内容简介 编辑 CayS.Horstmann等编著,公飞编译的<Java核心技术>(CoreJava)自第1版出版以来,一直备受广大Java程序设计人员的青睐,畅销不衰,是Java经典书籍.第8版针对JavaSE6平台进行了全面更新,囊括了Java平台标准版(JavaSE/J2SE)的全部基础知识,提供了大量完整且具有实际意义的应用实例,详细介绍了Java语言基础知识.面向对象程序设计.接口与内部类.事件监听器模型.swing图形用户界面程序设计.打包应用程序.异常处理.登

java核心技术学习笔记之一程序设计概述

Java 核心技术之一程序设计概述 一.   Java语言的特点 简单行 :取经于C++,排除了C++不常用的指针.结构等,增加垃圾回收. 面向对象:与C++不同是单继承,但是可以继承多接口.完全面向对象的语言: 网络技能:Socket,FTP,Http,URL编程简单: 健壮性:避免指针错误使用: 安全性:构建防病毒防篡改系统: 体系结构中立:字节码: 可移植性: 解释性:可以解释任何移植了的字节码: 高性能: 多线程 动态性: 二.   Internet 和 Java Applet 在网页中

[基础] Java目录(摘自Java核心技术·卷1 基础知识)

Java核心技术·卷1 基础知识(原书第9版) 第1章 Java程序设计概述 1.1 Java程序设计平台 1.2 Java"白皮书"的关键术语 1.2.1 简单性 1.2.2 面向对象 1.2.3 网络技能 1.2.4 健壮性 1.2.5 安全性 1.2.6 体系结构中立 1.2.7 可移植性 1.2.8 解释型 1.2.9 高性能 1.2.10 多线程 1.2.11 动态性 1.3 Java applet与Internet 1.4 Java发展简史 1.5 关于Java的常见误解

深入理解Java之多线程

一.为什么使用多线程 1. 并发与并行 我们知道,在单核机器上,“多进程”并不是真正的多个进程在同时执行,而是通过CPU时间分片,操作系统快速在进程间切换而模拟出来的多进程.我们通常把这种情况成为并发,也就是多个进程的运行行为是“一并发生”的,但不是同时执行的,因为CPU核数的限制(PC和通用寄存器只有一套,严格来说在同一时刻只能存在一个进程的上下文). 现在,我们使用的计算机基本上都搭载了多核CPU,这时,我们能真正的实现多个进程并行执行,这种情况叫做并行,因为多个进程是真正“一并执行”的(具

读《java核心技术卷一》有感

过去一个多月了吧.才囫囵吞枣地把这书过了一遍.话说这书也够长的,一共706页.我从来不是个喜欢记录的人,一直以来看什么书都是看完了就扔一边去,可能有时候有那么一点想记录下来的冲动,但算算时间太紧,很多也是有始无终,毕竟在之前研究研究程序也只是自己的一个爱好而已,但没有想到签了一个程序员的工作.唉,这老天也太捉弄人了吧,让一个学电气工程(强电方向)学生毕业之后去写代码,而且是与硬件完全无关的代码.真是白念几年大学了.行了,就行发这么多牢骚吧. <java核心技术>有两个卷,我只看了卷一,从我的感

java核心技术卷一

java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128-127       浮点类型 数据类型 字节数 取值范围 小数位数 float 4 10^-38~10^38和-10^-38~-10^38 小数位数6-7 double 4 10^-308~10^308和-10^-308~-10^308 15位小数         boolean 类型和char 类

【Java】多线程初探

 参考书籍:<Java核心技术 卷Ⅰ > Java的线程状态 从操作系统的角度看,线程有5种状态:创建, 就绪, 运行, 阻塞, 终止(结束).如下图所示 而Java定义的线程状态有: 创建(New), 可运行(Runnable), 阻塞(Blocked), 等待(Waiting), 计时等待(Time waiting) 被终止(Terminated). 那么相比起操作系统的线程状态, Java定义的线程状态该如何解读呢? 如下: 1. Java的阻塞. 等待. 计时等待都属于操作系统中定义的

《Java核心技术卷I》观赏指南

Tomxin7 如果你有想看书的计划,但是还在纠结哪些书值得看,可以简单看看"观赏指南"系列,本文会简单列出书中内容,给还没有买书的朋友提供一个参考. 前言 秋招过去很久了,虽然在学校的时候有恶补java基础,但是都是为了面试而准备的,有些技术并没有进行全面的了解,再加上java现在疯狂的更新版本,很多新东西没有了解过,所以就打算再过一遍.java的书籍很多,也不想重复的看,所以就买了两本写的比较系统的<java核心技术卷I II>,本篇文章主要是想分享一下卷I,如果你正打