进程、线程

​ 进程(Process) 是程序的运行实例。例如,一个运行的 Eclipse 就是一个进程。进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位。线程(Thread)是进程中可独立执行的最小单位。一个进程可以包含多个线程。进程和线程的关系,好比一个营业中的饭店与其正在工作的员工之间的关系。

 

1. 线程的创建、启动与运行

在 Java 中实现多线程主要用两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口。(当然还有Callable和线程池)。下面我们就分别来介绍这两种方式的使用,其他请关注此博客下文。

 

1.1 .继承Thread的类

public class PrimeThread extends Thread{
//线程执行体
@Override
public void run(){ for(int i = 0; i < 100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + "=" + i);
}
} } }

 

public class TestThread {
public static void main(String[] args) {
//新建一个线程
PrimeThread p1 = new PrimeThread();
//启动一个线程
p1.start(); PrimeThread p2 = new PrimeThread();
p2.start(); for(int i = 0; i < 100; i++ ){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + "=" + i);
}
} }
}

 

1.2 .实现 Runnable接口

public class Ticket implements Runnable{

	private int ticket = 100;

	@Override
public void run() {
while(ticket > 0){
System.out.println(Thread.currentThread().getName() + "=" + --ticket);
} }
}

 

public class TestThread2 {
public static void main(String[] args) { Ticket ticket = new Ticket(); //虽然是实现了Runnable接口 本质上只是实现了线程执行体 启动工作还是需要Thread类来进行
Thread t1 = new Thread(ticket,"售票窗口一");
t1.start(); Thread t2 = new Thread(ticket,"售票窗口二");
t2.start(); Thread t3 = new Thread(ticket,"售票窗口三");
t3.start();
}
}

 

两种实现方式的对比:

1.从面向对象编程角度看:第一种创建方式(继承Thread类) 是一种基础继承的技术,第二种创建方式(以Runnable接口实例为构造器参数直接通过new创建Thread实例)是一种基础组合的技术。方式二不仅会避免单继承的尴尬,也会降低类与类之间的耦合性。

2.从对象共享角度看:第二种创建方式意味着多个线程实例可以共享同一个Runnable实例。而第一种方式则需要依赖static关键字来完成操作。

 

2. 线程的控制

Java的调度方法

同优先级线程组成先进先出队列(先到先服务),使用时间片策略

对高优先级,使用优先调度的抢占式策略

 

Thread类的相关方法

  • sleep(long millis) : 是 Thread 类中的静态方法,使当前线程进入睡眠状态
  • join() / join(long millis) : 是一个实例方法,使当前线程进入阻塞状态
  • interrupt() : 中断阻塞状态的线程
  • isAlive() : 判断当前线程是否处于存活状态
  • yield() : 线程让步
public class TestThread3 {
public static void main(String[] args) throws Exception { Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1; i < 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + "=" + i);
}
} }
},"线程1");
t1.start();
//线程1在 sleep之前就执行完了
t1.sleep(10000);
//join方法 迫使t2 必须等线程1 执行完 才能执行 然而 t1输出完自己的 睡着了 t2被迫等了10秒
t1.join(); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1; i < 100;i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + "=" + i);
}
}
}
},"线程2");
t2.start(); }
}

 

 

3. 线程的同步

线程同步:模拟售票程序,实现三个窗口同时售票 100 张 (1.1案例)

问题:当三个窗口同时访问共享数据时,产生了无序、重复、超额售票等多线程安全问题

解决:将需要访问的共享数据“包起来”,视为一个整体,确保一次只能有一个线程执行流访问该“共享数据”

Java给上述问题提供了几种相应的解决方法

 

3.1.同步代码块

synchronized(同步监视器) {

​ //需要访问的共享数据

}

同步监视器 : 俗称“锁”,可以使用任意对象的引用充当,注意确保多个线程持有同一把锁(同一个对象)

 

public class SafeTicket implements Runnable{

	private int ticket = 100;

	@Override
public void run() {
while(true){
//使用同步代码块
synchronized (this) {
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + " 完成售票,余票:" + --ticket);
}
} }
} }

 

3.2 同步方法

同步方法 : 在方法声明处加 synchronized. 注意:非静态同步方法隐式的锁 ---- this

例如:

public synchronized void show(){}

 

public class SafeTicket implements Runnable{

	private int ticket = 100;

	@Override
public void run() {
while(true){
//使用同步代码块
sale(); }
} public synchronized void sale(){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + " 完成售票,余票:" +
--ticket);
}
} }

 

3.3 同步锁

同步锁 : Lock 接口

 

public class SafeTicket implements Runnable{

	private int ticket = 100;

	private Lock l = new ReentrantLock();

	@Override
public void run() {
while(true){
l.lock();
try {
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + " 完成售票,余票:" +
--ticket);
}
} finally {
l.unlock();//释放锁
} }
} }

 

3.4 死锁

死锁 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于 死锁 状态或系统产生了 死锁 ,这些永远在互相等待的进程称为 死锁 进程

 

public class TestDeadLock {
public static void main(String[] args) {
final StringBuffer s1 = new StringBuffer();
final StringBuffer s2 = new StringBuffer(); new Thread() {
public void run() {
synchronized (s1) {
s2.append("A"); try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} synchronized (s2) {
s2.append("B");
System.out.print(s1);
System.out.print(s2);
}
}
}
}.start(); new Thread() {
public void run() {
synchronized (s2) {
s2.append("C");
synchronized (s1) {
s1.append("D");
System.out.print(s2);
System.out.print(s1);
}
}
}
}.start();
}
}

 

1.4 线程的通信

在 java.lang.Object 类中:

wait() : 使当前“同步监视器”上的线程进入等待状态。同时释放锁

notify() / notifyAll() : 唤醒当前“同步监视器”上的(一个/所有)等待状态的线程

注意:上述方法必须使用在同步中

场景1:使用两个线程打印 1-100 线程1和线程2交替打印

public class MyThread implements Runnable{

	int i = 0;

	@Override
public void run() { while(true){
synchronized (this) {
this.notify(); if(i <= 100){
System.out.println(Thread.currentThread().getName() + "=" + i++);
} try {
this.wait();
} catch (InterruptedException e) {
}
} } } }
public class TestThread4 {
public static void main(String[] args) {
MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread,"线程1");
Thread t2 = new Thread(myThread,"线程2"); t1.start();
t2.start();
}
}

 

经典例题:生产者/消费者问题

  • 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
  • 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,
  • 如果店中有空位放产品了再通知生产者继续生产;
  • 如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
public class TestProduct {

	public static void main(String[] args) {
Clerk clerk = new Clerk(); Productor pro = new Productor(clerk);
Customer cus = new Customer(clerk); new Thread(pro).start();
new Thread(cus).start();
} } // 店员
class Clerk { private int product; // 进货
public synchronized void getProduct() {
if (product >= 20) {
System.out.println("产品已满!"); try {
wait();
} catch (InterruptedException e) {
} } else {
System.out.println("生产者生产了第" + ++product + " 个产品"); notifyAll();
}
} // 卖货
public synchronized void saleProduct() {
if (product <= 0) {
System.out.println("缺货!"); try {
wait();
} catch (InterruptedException e) {
} } else {
System.out.println("消费者消费了第" + --product + " 个产品"); notifyAll();
}
} } // 生产者
class Productor implements Runnable { private Clerk clerk; public Productor() {
} public Productor(Clerk clerk) {
this.clerk = clerk;
} public Clerk getClerk() {
return clerk;
} public void setClerk(Clerk clerk) {
this.clerk = clerk;
} @Override
public void run() {
while (true) {
clerk.getProduct();
}
} } // 消费者
class Customer implements Runnable { private Clerk clerk; public Customer() {
} public Customer(Clerk clerk) {
this.clerk = clerk;
} public Clerk getClerk() {
return clerk;
} public void setClerk(Clerk clerk) {
this.clerk = clerk;
} @Override
public String toString() {
return "Customer [clerk=" + clerk + "]";
} @Override
public void run() {
while(true){
clerk.saleProduct();
}
} }

java核心技术-多线程基础的更多相关文章

  1. JAVA核心技术I---JAVA基础知识(工具类Arrays和Collections类)

    一:工具类 –不存储数据,而是在数据容器上,实现高效操作 • 排序 • 搜索 –Arrays类 –Collection类 二:Arrays类(处理数组) (一)基本方法 –排序:对数组排序, sort ...

  2. JAVA核心技术I---JAVA基础知识(static关键字)

    一:static特殊关键字用处 –变量 –方法 –类 –匿名方法 二:静态变量:类共有成员 –static变量只依赖于类存在(通过类即可访问),不依赖于对象实例存在. –所有的对象实例,对于静态变量都 ...

  3. java核心技术-多线程之引导概念

    前两篇文章分别讲了,Java线程相关基础概念以及线程的内存模型,本节作为后续章节的引导,个人认为对于学习后面的只是还是比较重要.好了,既然说了多线程,那么首先要弄清以下几个问题: 1. 什么是多线程? ...

  4. Java核心技术卷一基础知识-第14章-多线程-读书笔记

    第 14 章 多线程 本章内容: * 什么是线程 * 中断线程 * 线程状态 * 线程属性 * 同步 * 阻塞队列 * 线程安全的集合 * Collable与Future * 执行器 * 同步器 * ...

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

    说起线程,无法免俗首先要弄清楚的三个概念就是:进程.线程.协程.OK,那什么是进程,什么是线程,哪协程又是啥东西.进程:进程可以简单的理解为运行在操作系统中的程序,程序时静态代码,进程是动态运行着的代 ...

  6. Java核心技术卷一基础技术-第13章-集合-读书笔记

    第13章 集合 本章内容: * 集合接口 * 具体的集合 * 集合框架 * 算法 * 遗留的集合 13.1 集合接口 Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制. 13. ...

  7. JAVA核心技术I---JAVA基础知识(映射Map)

    一:映射Map分类 二:Hashtable(同步,慢,数据量小) –K-V对,K和V都不允许为null –同步,多线程安全 –无序的 –适合小数据量 –主要方法:clear, contains/con ...

  8. JAVA核心技术I---JAVA基础知识(时间类)

    一:时间类库了解 java.util.Date(基本废弃,Deprecated) –getTime(),返回自1970..1以来的毫秒数 java.sql.Date(和数据库对应的时间类) //与数据 ...

  9. Java 并发——多线程基础

    Thead类与Runnable接口 Java的线程,即一个Thread实例. Java的线程执行过程有两种实现方式: 子类继承Thread类,并且重写void run()方法. 自定义类实现Runna ...

随机推荐

  1. 工作中常用Linux命令

    建立软链接  ln -s      例:ln -s b a 解释:把文件夹a和文件夹b关联起来,访问文件夹a,实际访问的是问价夹b 删除软连接  rm -rf a  直接删掉a文件夹跟a和b的软连接. ...

  2. junit启动tomcat来进行单元测试

    1.pom.xml配置下载需要的jar.   <dependency>            <groupId>junit</groupId>            ...

  3. aspx代码审计-2

    今天和大家分享一下aspx网站的代码审计,漏洞类型为:未授权访问和任意文件下载. 本文作者:i春秋签约作家——非主流 今天看的源码文件就不共享给大家了,本文只作学习只用. 还是先看我们的文件夹目录和d ...

  4. HTML5语义化标签总结

    1.语义化标签总结 基础布局标签 <header></header> <nav></nav> <main></main> < ...

  5. 【FAQ】tomcat启动jdk版本不一致

    一.tomcat7.exe与startup.bat的区别: 1.这两个都可以启动tomcat,但tomcat7.exe必须安装了服务才能启动,而startup.bat不需要 2.另外一个区别是它们启动 ...

  6. Java 设计模式之单利模式

    一.首先介绍一下单例模式:     单例模式(Singleton),也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在.许多时候整个系统只需要拥有一个的全局 ...

  7. SP9098 LCS3

    题目链接 题意分析 \(olinr\) : 序列自动机+一系列的鬼畜操作 相信我 你们没人能切 \(lzxkj\) : \(2^m+vector+\)暴力二分 跑得比你正解还快 首先一看\(m≤5\) ...

  8. Oracle数据库学习(二):Oracle Linux下oracle、ogg的挂载与参数配置

    准备工作:打开虚拟机端的Oracle Linux Server 6.9的系统,然后使用root用户登录.打开终端界面,输入ifconfig -a查看IP地址. 然后在本地打开XShell软件使用以下命 ...

  9. golang (4) golang 操作mongdb

    1. 数据按照时间聚合操作 1.1 正常的数据结构 { "_id" : ObjectId("5cac8d7b1202708adf5d4b64"), " ...

  10. [iOS]使用Windows Azure來做iOS的推播通知 (转帖)

    這一篇我們用Windows Azure 的Mobile Service 來實作iOS的推播通知,底下我們分成三個階段來探討如何實作推播通知的服務: 第一階段: 開啓你的Windows Aure服務   ...