多线程编程的资源争用问题

一个 基本概念

(1)server运行任务必须等待一个任务后运行,只要运行的下一个任务。所谓同步(Synchronized)。任务发布运行命令,此时server能够接受要运行的命令,那接到任务后,,server也能够干其它事称为异步。

同步和异步的差别

举个样例:普通B/S模式(同步)AJAX技术(异步)

同步:提交请求->等待server处理->处理完成返回 这个期间client浏览器不能干不论什么事

异步: 请求通过事件触发->server处理(这是浏览器仍然能够作其它事情)->处理完成

(2)实现实现多线程编程的两种方法:

A.继承类Thread(所属的jar包 java.lang.Thread)。

重写run方法。简单样例

Publicclass ThreadA extends Thread{

Publicvoid run(){//重写方法

Sleep();//直接调用由于继承自父类 父类中含有这种方法

}

Publicvoid main(String []ars)

{

ThreadA thread=new Thread();

Thread.start();//这里直接调用方法就可以

}

}

B.实现接口 Runnable,重写run方法。简单样例:

Public class ThreadB implements Runnable{

Publicvoid run(){//重写方法

Thread.sleep();// 假设须要调用sleep()方法的话,须要Thread静态方法。由于这里仅仅是实现了接口

}

Publicvoid main(String []ars)

{

ThreadB threadB=new ThreadB();

(newThreadB(threadB)).start();//这里使用了装饰者模式注意两种方法启动线程的差别由于仅仅是实现了接口,并没有实现 start方法。

}

}

(3) 线程概念 以及五个状态

一般操作系统都支持同一时候执行多个任务,每一个任务就是一个程序。系统级别的任务称之为进程,一个程序又能够同一时候执行多个任务。程序级别的任务称之为线程

线程与进程的详细差别:

http://www.cnblogs.com/lmule/archive/2010/08/18/1802774.html

进程是并发的,OS 将时间划分为若干个时间片。在每一个时间片段中运行不

同的任务。

OS 尽可能均匀地将时间片分给每一个任务,所以在微观上,全部的程序都是走走停停。在宏观上全部程序都在同一时候执行,这样的现象叫做并发。

线程的五个状态:new,runnable,running, block ,dead.

Thread.yeald() 方法会主动让出此CPU 时间,主动从running 状态回到runnable。但让出的时间不可控。

java中,每一个线程都需经历新生(new)、就绪(runnable)、执行(running)、堵塞(block)和死亡(dead)五种状态,线程从新生到死亡的状态变化称为word=%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F&fr=qb_search_exp&ie=utf8">生命周期。
用newword=%E8%BF%90%E7%AE%97%E7%AC%A6&fr=qb_search_exp&ie=utf8">运算符和Thread类或其子类建立一个线程对象后。该线程就处于新生状态。
   新生(new)--->就绪(runnable):通过调用start()方法
   就绪(runnable)--->执行(running):处于word=%E5%B0%B1%E7%BB%AA%E7%8A%B6%E6%80%81&fr=qb_search_exp&ie=utf8">就绪状态的线程一旦得到CPU,就进入执行状态并自己主动调用自己的run()方法
   执行--->堵塞:处于执行状态的线程,执行sleep()方法。或等待I/O设备资源。让出CPU并临时中止自己执行。进入word=%E9%98%BB%E5%A1%9E%E7%8A%B6%E6%80%81&fr=qb_search_exp&ie=utf8">堵塞状态
   堵塞--->就绪:睡眠时间已到,或等待的I/O设备空暇下来,线程便进入word=%E5%B0%B1%E7%BB%AA%E7%8A%B6%E6%80%81&fr=qb_search_exp&ie=utf8">就绪状态,又一次到就绪队列中等待CPU。

当再次获得CPU时,便从原来中止位置開始继续执行。
   执行--->死亡:(1)(正常情况下)线程任务完毕
               (2)(非正常状况)线程被强制性的中止,如通过执行stop()或destroy()方法来终止一个线程

(4)wait sleep notify方法以及synchronized()进程块

4.1 wait与sleep

Sleep 是线程类(Thread)的方法(调用sleep方法由运行状态进入堵塞状态),它使当前线程在指定时间内,将运行机会给其它线程,但不会释放对象锁。wait 是Object 类的方法。3,此对象调用wait(方调用后会马上释放对象锁。线程挂起也就是说wait以下的程序都不再运行,当其它线程对此对象调用notify后才会运行)
方法导致本线程释放对象锁。仅仅有针对此对象发出notify(或notifyAll)方法后本线程才再次试图获得对象锁并进入运行状态。须要注意notify后不是立即就释放对象锁的,而是在对应的synchronized(){}语句块运行结束。自己主动释放锁后。JVM会在wait()对象锁的线程中随机选取一线程。赋予其对象锁。唤醒线程,继续运行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都能够暂停当前线程,释放CPU控制权,基本的差别在于Object.wait()在释放CPU同一时候,释放了对象锁的控制。

二 实际案例分析

2.1案例源代码

package KFC;

public
class
Ham {

static Objectbox=new Object();//监控对象

static
int
totalmaterial=10;//可用的材料

static
int
sales=0;//销售的个数

static
int
production=5;//总共生产了多少个

}

package KFC;

public
class
Hassistant implements Runnable {

public
void
sell() {

/*注意 这里必须是先加锁 然后再推断。假设是先推断,后加锁,则可能出现异步(和Hmaker同一时候运行的情况),会导致这里的推断结果不准确。以后在敲代码时也应当注意这点。都是先加锁再进行推断。

下例是错误的,现进行说明:

1.  先推断了Ham.production ==Ham.sales 是否成立,如果成立

2.  这时假设Hmaker也运行,production++,改变了production的数量,注意这时还有汉堡。

可是此时Hassistant方法仍然运行了synchronized (Ham.box)操作,告诉顾客已经没汉堡了。导致与实际情况相矛盾。

if (Ham.production ==Ham.sales) {

synchronized (Ham.box) {

System.out.println("营业员-" +this.getClass().getName()

+ ":顾客朋友。汉堡没了。请稍等。

。");

try {

Ham.box.wait();//此时整个线程会挂起 if语句外面的也不在运行

System.out.println("-----线程挂起-------");

} catch (Exception e) {

e.printStackTrace();

}

}

}

*/

synchronized (Ham.box) {

if (Ham.production ==Ham.sales) {

System.out.println("营业员-" +this.getClass().getName()

+ ":顾客朋友,汉堡没了,请稍等。。

");

try {

System.out.println("-----线程挂起-------");

Ham.box.wait();//此时整个线程会马上被挂起  Ham.box.notify()之前。wait()下的语句都不会再运行

System.out.println("营业员又一次获取对对象锁");

} catch (Exception e) {

e.printStackTrace();

}

}

}

Ham.sales++;

System.out.println("营业员-:顾客朋友汉堡来了,总共卖了:"
+ (Ham.sales) +
"个,还剩"

+ (Ham.production - Ham.sales) +"个,总生产了"
+ Ham.production

+ "个");

}

@Override

public
void
run() {

while (Ham.production <= Ham.totalmaterial) {

try {

sell();

Thread.sleep(1000);// 1s卖一个

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

package KFC;

import java.lang.*;

public
class
Hmaker implements Runnable {//厨师

public
void
make() {

synchronized (Ham.box)//对资源进行加锁

{

(Ham.production)++;//汉堡数量添加

System.out.println("厨师-" +this.getClass().getName()

+" 总共卖了:" + (Ham.sales)

+ "个,还剩"+(Ham.production-Ham.sales)+"个,总生产了"+Ham.production+"个");

Ham.box.notify();//资源解锁 这里的解锁是指在同步块完毕后才真正的解锁 所以notify后 会继续运行

System.out.println("--资源解锁了---");

}

}

@Override

public
void
run() {

while (Ham.production <= Ham.totalmaterial)//还有材料

{

try {

Thread.sleep(3000);// 3s做一个汉堡

} catch (Exception e) {

e.printStackTrace();

}

this.make();

}

}

}

package KFC;

public
class
ThreadTest {

public
static void
main(String []args)

{

Hmaker maker=new Hmaker();

Hassistant assistant=new  Hassistant();

new Thread(assistant).start();

new Thread(maker).start();

}

}

2.2源代码分析

代码运行步骤例如以下:


时间


Hmaker


Hassistant


production


sales


left


1s


销售1个


5


1


4


2s


销售1个


5


2


3


3s


销售1个


5


3


2


4s


生产1个,此时尽管运行了notify方法,没有线程处在wait()对象锁的线程


销售1个


6


4


2


5s


销售1个


6


5


1


6s


销售1个


6


6


0


7s


此时,因为盒子里已经没有汉堡,因此运行wait()方法,释放资源锁,线程挂起进入block状态


8s


生产了一个,运行notify方法,此时,处在wait()对象锁状态的仅仅有Hassistant线程一个。


由block状态先进入runnable状态,获取cpu时间,进入running状态,销售一个


7


7


0


9s


此时,因为盒子里已经没有汉堡,因此运行wait()方法。释放资源锁,线程挂起进入block状态


10s




….




11s


12s


13s


14s


15s


16s


17s


18s


19s


20s


21s


22s


23s

版权声明:本文博客原创文章。博客,未经同意,不得转载。

时间: 2024-08-27 11:02:14

多线程编程的资源争用问题的相关文章

c# winform编程之多线程ui界面资源修改总结篇

单线程的winfom程序中,设置一个控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一个新线程中这么做,比如: private void btnSet_Click(object sender, EventArgs e) {        Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));     //当然也可以用匿名委托

Windows多线程编程总结

Windows 多线程编程总结 keyword:多线程 线程同步 线程池 内核对象 1 内核对象 1 .1 内核对象的概念 内核对象是内核分配的一个内存块,这样的内存块是一个数据结构,表示内核对象的各种特征.而且仅仅能由内核来訪问.应用程序若须要訪问内核对象,须要通过操作系统提供的函数来进行,不能直接訪问内核对象( Windows 从安全性方面来考虑的). 内核对象通过 Create* 来创建,返回一个用于标识内核对象的句柄,这些句柄 (而不是内核对象)可在创建进程范围内使用,不可以被传递到其它

linux下多线程编程

最近研究mysql源码,各种锁,各种互斥,好在我去年认真学了<unix环境高级编程>, 虽然已经忘得差不多了,但是学过始终是学过,拿起来也快.写这篇文章的目的就是总结linux 下多线程编程,作为日后的参考资料. 本文将介绍linux系统下多线程编程中,线程同步的各种方法.包括: 互斥量(mutex) 读写锁 条件变量 信号量 文件互斥 在介绍不同的线程同步的方法之前,先简单的介绍一下进程和线程的概念, 它们的优缺点,线程相关的API,读者——写者问题和哲学家就餐问题. 基础知识 1. 进程和

Java多线程编程详解

线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问. 由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synch

iOS多线程编程的几种方式

一.线程概述 有些程序是一条直线,起点到终点——如简单的hello world,运行打印完,它的生命周期便结束了,像昙花一现. 有些程序是一个圆,知道循环将它切断——像操作系统,一直运行,直到你关机. 一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流. Mac和IOS中的程序启动,创建好一个进程的同时,一个线程便开始运作,这个线程叫做主线程.主线程在程序中的位置和其他线程不同,它是其他线程最终的父线程,且所有的界面的显示操作即AppKit或UIKit的

多线程编程核心技术总结(读周志明书籍的总结)

多线程编程核心技术总结 1.Java多线程基本技能 1.1进程和线程的概念: 进程是独立的程序,线程是在进程中独立运行的子任务. 1.2使用多线程 1.2.1实现方法:继承Thread类,重写Runnable接口. 1.2.2线程安全问题:并发修改公共的实例变量,i++,i-- 1.3线程Thread类的一些方法: currentThread() 放回代码段正在被那个线程调用 isAlive() 判断线程是否处于活动状态 sleep() 使得当前线程退出CPU片段,等待获取锁 1.4停止线程 1

Java基础知识—多线程编程(五)

概述 Java 给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径.使用多线程也是为了充分的利用服务器资源,提高工作效率. 线程生命周期 线程是一个动态执行的过程,它也有一个从产生到死亡的过程. 新建状态: 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态.它保持这个状态直到程序 start() 这个线程. 就绪状态: 当线程对象调用了start()方法之后,该

第73课 Qt中的多线程编程

1. QThread类 (1)QThread是一个跨平台的多线程解决方案 (2)QThread以简洁易用的方式实现多线程编程 2. QThread中的关键成员函数 (1)virtual void run() :线程函数,用于定义线程功能(执行流). (2)void start():启动函数,将线程入口地址设置为run函数.启动线程,新线程开始执行run函数. (3)int exec():进入事件循环,直至调用exit().返回线程退出事件循环的返回码. (4)void terminate():强

多线程编程(进程和线程)

多线程编程(进程和线程) 1.进程:指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程可以启动多个线程. 2.线程:指程序中一个执行流程,一个进程中可以运行多个线程. 一.创建线程(两种方式) 二.线程的5种状态( New,Runnable,Running,Block,Dead ): 三.线程的优先级 四.守护线程 /精灵线程/后台线程 五.方法 六.同步代码锁(synchronized) 一.创建线程(两种方式): 方式1:采用继承Thread的方法 第一,继承 Thre