阳光男孩

Never give up!

Entries Tagged ‘线程’

Java线程初级总结

1顶一下1.多线程概念 程序:计算机指令的集合,是一段静态的代码,还有指令和数据的文件。 进程:进程是程序一次动态执行的所有过程,进程包括运行中的程序和程序所使用到的内存和系统资源。 线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器,但内存单元是共享的,即不同的线程可以执行同样的函数。 并发编程:指由若干个可同时执行的程序模块组成程序的程序设计方法。这种可同时...[阅读全文]

1
顶一下

1.多线程概念

程序:计算机指令的集合,是一段静态的代码,还有指令和数据的文件。
进程:进程是程序一次动态执行的所有过程,进程包括运行中的程序和程序所使用到的内存和系统资源。
线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器,但内存单元是共享的,即不同的线程可以执行同样的函数。
并发编程:指由若干个可同时执行的程序模块组成程序的程序设计方法。这种可同时执行的程序模块就是进程。
2.为什么使用多线程

在java多线程模型中,多个线程共存于同一块内存中,且共享资源,线程之间的通信非常容易。
java程序可以并行处理,很大提高了程序的效率以及功能。

3.java线程的模型
抢占式调度模型
线程调度程序挑选线程时,将选择处于就绪状态且优先级最高的线程。
如果多个线程具有相同的优先级,它们将被轮流调度。
4.java线程优先级
      优先级是从0到10的整数,并且它仅表示线程之间的相对关系;
      当多个线程并行执行时,具有较高优先级的线程将获得较多的CPU时间片;
Thread类包含的常量有:
1. public static final int MAX_PRIORITY: 最大优先级,值是10。
2. public static final int MIN_PRIORITY:  最小优先级,值是1。
3. public static final int NORM_PRIORITY:缺省优先级,值是5。
5.主线程
      java中建立了一个类,当运行到该类的时候,java虚拟机自动创建一个一个线程调用main函数,这就是它的主线程。
6.建立线程
     当一个Thread类或其子类的对象被声明冰箱被创建时。此时它已经有了相应的内存空间和其他资源,但是并没有开始执行它内部的代码。
     void run():用该方法来执行线程。
void start():开始执行run部分的代码。
static void sleep(long millis)throws InterruptedException:将可运行对象置为休眠状态,休眠时间为指定的毫秒。

7.如何知道线程是否已经结束
     可以调用isAlive()这个方法来判断:isAlive方法可以返回线程的状态,若true则线程处在可运行或不可运行状态; 若false则线程处在新创建或死亡状态。
还有join()方法,它会等待线程结束。相当于下面的语句:
while(thread.isAlive())   {
try   {
thread.sleep(10);
}   catch   (InterruptedException   e)   {
}
}
8.动画实现

创建多个Thread类或其子类的对象,循环开始start(),然后在run()函数中实现。

Comments (4)

简述进程、线程与项目的关系

0顶一下首先在Visual Studio2010新建了一个WinForm项目WinFormApp1,里面会默认有个叫Form1的窗口。假设,项目WinFormApp1的功能是为了查询远端Ftp服务器的文档目录,然后把查到的结果显示到Form1的界面上。通常的做法是在Form1.cs类中添加一个QueryFtpFiles()方法用于对远端的ftp服务器进行读操作,然后把读到的内容显示到界面上就算完成了。 在这个项目完成后,一般会先在本地或局域网内的某...[阅读全文]

0
顶一下

首先在Visual Studio2010新建了一个WinForm项目WinFormApp1,里面会默认有个叫Form1的窗口。假设,项目WinFormApp1的功能是为了查询远端Ftp服务器的文档目录,然后把查到的结果显示到Form1的界面上。通常的做法是在Form1.cs类中添加一个QueryFtpFiles()方法用于对远端的ftp服务器进行读操作,然后把读到的内容显示到界面上就算完成了。
在这个项目完成后,一般会先在本地或局域网内的某个Ftp服务器上先做个测试,这时你会发现程序很完美地运行着。当你兴致冲冲地把代码交给你领导的时候,你的上司拿了远在美国的ftp服务器来试运行,点击“查询”按钮执行QueryFtpFiles()查询操作的时候,悲剧发生了——页面死掉了(没有响应)。。。你是不是觉得很没面子?有木有?
这里有个问题,就是在执行QueryFtpFiles()方法时,由于是对远端的ftp服务器进行读操作,本身IO操作就比较耗时,再考虑到网络延迟等因素,你的界面就不得不停下了等待读操作的完成。如果在这个读操作完成前,你急不可耐地点了下界面,就出现了“没有响应”的后果。
其实,我在运行这个项目的时候,实际上是在运行由这个项目自动在Debug或Release目录下生成的WinFormApp1.exe应用程序,而WinFormApp1.exe对于我的Windows Server2003即操作系统来说,他就是一个进程。
操作系统书中讲,传统的进程有两个基本属性:拥有资源的独立单位和可独立调度和分配的基本单位。由于在进程的创建、撤销和切换中,系统必须耗费较大的时空开销。引入线程后,传统的进程的两个基本属性分开,线程作为调度和分配的基本单位,进程作为独立分配资源的单位。也就是说,在完成一个复杂的功能时,可以在一个进程中建立多个线程,每个线程分别完成某一项简单功能,进程通过调度和排列组合这些线程来实现这个复杂的功能。
线程,其实就是一段代码,一个方法或一连串方法,这段代码或方法可以去完成某个功能,也可以什么都不用做,比如上面提到的QueryFtpFiles(),它其实就满足作为线程的基本条件。
再回到原来的“没有响应”的问题上,在执行WinFormApp1.exe这个进程的时候,这个进程会调用UI线程,其实也就是Form1.cs内的代码以及它调用的其他类的代码,因为QueryFtpFiles()也在Form1.cs这个类中,所以它也算是UI线程的一部分。WinFormApp1.exe进程执行时会首先调用这个UI线程(若没有定义其他线程,这时UI线程应该也是这个进程的唯一线程),这个UI线程在执行QueryFtpFile()方法的过程中由于耗时很多以至于这个进程其他什么什么操作都要停下来等待,包括你去点击界面的操作。
问题的描述应该就是这样了,至于解决方法,可以在UI线程中再定义另外一个线程thread1用来执行QueryFtpFile()方法就可以了。

Comments (6)

Java线程同步锁解决共享数据安全

2顶一下我们在共享我们的数据的时候必须要考虑到安全的问题。Java线程同步锁就帮助我们解决了这个难缠的问题。下面我们就来学些有关着房门的问题,希望大 家有所后收获。 我们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程(Thread)。线程(Thread)是一份独立运 行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等...[阅读全文]

2
顶一下

我们在共享我们的数据的时候必须要考虑到安全的问题。Java线程同步锁就帮助我们解决了这个难缠的问题。下面我们就来学些有关着房门的问题,希望大 家有所后收获。

我们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程(Thread)。线程(Thread)是一份独立运 行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。

当多个Java线程同步锁同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后 到,不能一窝蜂挤上去抢作一团。

同步这个词是从英文synchronize(使同时发生)翻译过来的。我也不明白为什么要用这个很容易引起误解的词。既然大家都这么用,咱们也 就只好这么将就。 Java线程同步锁的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进 行操作。

因此,关于线程同步,需要牢牢记住的第一点是:线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程“同步”执行。这可真是个无聊 的绕口令。

关于线程同步,需要牢牢记住的第二点是 “共享”这两个字。只有共享资源的读写访问才需要同步。如果不是共享资源,那么就根本没有同步的必要。

关于线程同步,需要牢牢记住的第三点是,只有“变量”才需要同步访问。如果共享的资源是固定不变的,那么就相当于“常量”,线程同时读取常量也 不需要同步。至少一个Java线程同步锁修改共享资源,这样的情况下,线程之间就需要同步。

关于线程同步,需要牢牢记住的第四点是:多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要 这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。

为了加深理解,下面举几个例子。

有两个采购员,他们的工作内容是相同的,都是遵循如下的步骤:

(1)到市场上去,寻找并购买有潜力的样品。

(2)回到公司,写报告。

这两个人的工作内容虽然一样,他们都需要购买样品,他们可能买到同样种类的样品,但是他们绝对不会购买到同一件样品,他们之间没有任何共享资 源。所以,他们可以各自进行自己的工作,互不干扰。

这两个采购员就相当于两个Java线程同步锁;两个采购员遵循相同的工作步骤,相当于这两个线程执行同一段代码。

下面给这两个采购员增加一个工作步骤。采购员需要根据公司的“布告栏”上面公布的信息,安排自己的工作计划。

这两个采购员有可能同时走到布告栏的前面,同时观看布告栏上的信息。这一点问题都没有。因为布告栏是只读的,这两个采购员谁都不会去修改布告栏 上写的信息。

下面增加一个角色。一个办公室行政人员这个时候,也走到了布告栏前面,准备修改布告栏上的信息。

如果行政人员先到达布告栏,并且正在修改布告栏的内容。两个采购员这个时候,恰好也到了。这两个采购员就必须等待行政人员完成修改之后,才能观 看修改后的信息。

如果行政人员到达的时候,两个采购员已经在观看布告栏了。那么行政人员需要等待两个采购员把当前信息记录下来之后,才能够写上新的信息。

上述这两种情况,行政人员和采购员对布告栏的访问就需要进行同步。因为其中一个线程(行政人员)修改了共享资源(布告栏)。而且我们可以看到, 行政人员的工作流程和采购员的工作流程(执行代码)完全不同,但是由于他们访问了同一份可变共享资源(布告栏),所以他们之间需要同步。

Java线程同步锁

前面讲了为什么要Java线程同步锁同步,下面我们就来看如何才能线程同步。

线程同步的基本实现思路还是比较容易理解的。我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资 源。

生活中,我们也可能会遇到这样的例子。一些超市的外面提供了一些自动储物箱。每个储物箱都有一把锁,一把钥匙。人们可以使用那些带有钥匙的储物 箱,把东西放到储物箱里面,把储物箱锁上,然后把钥匙拿走。这样,该储物箱就被锁住了,其他人不能再访问这个储物箱。(当然,真实的储物箱钥匙是可以被人 拿走复制的,所以不要把贵重物品放在超市的储物箱里面。于是很多超市都采用了电子密码锁。)

Java线程同步锁这个模型看起来很直观。但是,还有一个严峻的问题没有解决,这个同步锁应该加在哪里?

当然是加在共享资源上了。反应快的读者一定会抢先回答。

Comments (62)

Java线程:深入ThreadLocal

3顶一下ThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量。这个局部变量与一般的成员变量不一 样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊。 准确的说,应该是ThreadLocal类型的变量内部的注册表(Map)发生了变化,但ThreadLocal类...[阅读全文]

3
顶一下

ThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量。这个局部变量与一般的成员变量不一 样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊。

准确的说,应该是ThreadLocal类型的变量内部的注册表(Map)发生了变化,但ThreadLocal类型的变 量本身的确是一个,这才是本质!

下面就做个例子:

一、标准例子

定义了MyThreadLocal类,创建它的一个对象tlt,分别给四个线程使用,结果四个线程tlt变量并没有出现共用现象,二是各用各 的,这说明,四个线程使用的是tlt的副本(克隆品)。
/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/
public class MyThreadLocal {
//定义了一个ThreadLocal变量,用来保存 int或Integer数据
private ThreadLocal tl = new ThreadLocal() {
@Override
protected Integer initialValue() {
return 0;
}
};
public Integer getNextNum() {
//将tl的值获取后加1,并更新设置 t1的值
tl.set(tl.get() + 1);
return tl.get();
}
}
/**
* 测试线程
*
* @author leizhimin 2010-1-5 10:39:18
*/
public class TestThread extends Thread {
private MyThreadLocal tlt = new MyThreadLocal();
public TestThread(MyThreadLocal tlt) {
this.tlt = tlt;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ”\t” + tlt.getNextNum());
}
}
}
/**
* ThreadLocal测试
*
* @author leizhimin 2010-1-5 10:43:48
*/
public class Test {
public static void main(String[] args) {
MyThreadLocal tlt = new MyThreadLocal();
Thread t1 = new TestThread(tlt);
Thread t2 = new TestThread(tlt);
Thread t3 = new TestThread(tlt);
Thread t4 = new TestThread(tlt);
t1.start();
t2.start();
t3.start();
t4.start();
}
}

可以看出,三个线程各自独立编号,互不影响:

Thread-0 1
Thread-1 1
Thread-0 2
Thread-1 2
Thread-0 3
Thread-1 3
Thread-2 1
Thread-3 1
Thread-2 2
Thread-3 2
Thread-2 3
Thread-3 3
Process finished with exit code 0

tlt对象是一个,废话tl对象也是一个,因为组合关系是一对一的。但是tl对象内部的Map随着线程的增多,会创建很多Integer对象。 只是Integer和int已经通用了。所以感觉不到Integer的对象属性。

二、不用 ThreadLocal

假如不用ThreadLocal,只需要将MyThreadLocal类重新定义为:
/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/
public class MyThreadLocal {
private Integer t1 = 0;
public Integer getNextNum(){
return t1=t1+1;
}
// //定 义了一个ThreadLocal变量,用来保存int或Integer数据
// private ThreadLocal tl = new ThreadLocal() {
// @Override
// protected Integer initialValue() {
// return 0;
// }
// };
//
// public Integer getNextNum() {
// //将tl的值获 取后加1,并更新设置t1的值
// tl.set(tl.get() + 1);
// return tl.get();
// }
}

然后运行测试:

Thread-2 1
Thread-2 2
Thread-1 4
Thread-1 6
Thread-3 3
Thread-3 9
Thread-3 10
Thread-1 8
Thread-0 7
Thread-0 11
Thread-0 12
Thread-2 5
Process finished with exit code 0

从这里可以看出,四个线程共享了tlt变量,结果每个线程都直接修改tlt的属性。

三、自己实现个 ThreadLocal
package com.lavasoft.test2;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/
public class MyThreadLocal {
//定义了一个ThreadLocal变量,用来保存 int或Integer数据
private com.lavasoft.test2.ThreadLocal tl = new com.lavasoft.test2.ThreadLocal() {
@Override
protected Integer initialValue() {
return 0;
}
};
public Integer getNextNum() {
//将 tl的值获取后加1,并更新设置t1的值
tl.set(tl.get() + 1);
return tl.get();
}
}
class ThreadLocal {
private Map map = Collections.synchronizedMap(new HashMap());
public ThreadLocal() {
}
protected T initialValue() {
return null;
}
public T get() {
Thread t = Thread.currentThread();
T obj = map.get(t);
if (obj == null && !map.containsKey(t)) {
obj = initialValue();
map.put(t, obj);
}
return obj;
}
public void set(T value) {
map.put(Thread.currentThread(), value);
}
public void remove() {
map.remove(Thread.currentThread());
}
}

运行测试:

Thread-0 1
Thread-0 2
Thread-0 3
Thread-2 1
Thread-2 2
Thread-3 1
Thread-2 3
Thread-3 2
Thread-1 1
Thread-3 3
Thread-1 2
Thread-1 3
Process finished with exit code 0

很意外,这个山寨版的ThreadLocal也同样运行很好,实现了JavaAPI中ThreadLocal的功能。

四、透过现象看本 质

其实从程序角度看,tlt变量的确是一个,毫无疑问的。但是为什么打印出来的数字就互不影响呢?

是因为使用了Integer吗?—–不是。

原因是:protected T initialValue()和get(),因为每个线程在调用get()时候,发现Map中不存在就创建。调用它的时候,就创建了一个新变量,类型为 T。每次都新建,当然各用个的互不影响了。

为了看清本质,将Integer换掉,重写部分类:
package com.lavasoft.test2;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/
public class MyThreadLocal {
//定义了一个ThreadLocal变量,用来保存 int或Integer数据
// private ThreadLocal tl = new ThreadLocal() {
private com.lavasoft.test2.ThreadLocal tl = new com.lavasoft.test2.ThreadLocal() {
@Override
protected Bean initialValue() {
return new Bean();
}
};
@Override
public String toString() {
return ”MyThreadLocal{“ +
“tl=” + tl +
‘}’;
}
public Bean getBean() {
return tl.get();
}
}
class ThreadLocal {
private Map map = Collections.synchronizedMap(new HashMap());
public ThreadLocal() {
}
protected T initialValue() {
return null;
}
public T get() {
Thread t = Thread.currentThread();
T obj = map.get(t);
if (obj == null && !map.containsKey(t)) {
obj = initialValue();
map.put(t, obj);
}
return obj;
}
public void set(T value) {
map.put(Thread.currentThread(), value);
}
public void remove() {
map.remove(Thread.currentThread());
}
}
package com.lavasoft.test2;
/**
* 测试Bean
*
* @author leizhimin 2010-1-5 14:18:26
*/
public class Bean {
private String id = ”0″;
private String name = ”none”;
public Bean() {
}
public Bean(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String showinfo() {
return ”Bean{“ +
“id=’” + id + ’\” +
“, name=’” + name + ’\” +
‘}’;
}
}
package com.lavasoft.test2;
/**
* 测试线程
*
* @author leizhimin 2010-1-5 10:39:18
*/
public class TestThread extends Thread {
private MyThreadLocal tlt = new MyThreadLocal();
public TestThread(MyThreadLocal tlt) {
this.tlt = tlt;
}
@Override
public void run() {
System.out.println(“>>>>>:” + tlt);
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ”\t” +tlt.getBean()+”\t”+tlt.getBean().showinfo());
}
}
}

然后运行测试:

>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
Thread-1 com.lavasoft.test2.Bean@291aff Bean{id=’0′, name=’none’}
Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id=’0′, name=’none’}
Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id=’0′, name=’none’}
Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id=’0′, name=’none’}
Thread-2 com.lavasoft.test2.Bean@fe64b9 Bean{id=’0′, name=’none’}
Thread-0 com.lavasoft.test2.Bean@291aff Bean{id=’0′, name=’none’}
Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id=’0′, name=’none’}
Thread-3 com.lavasoft.test2.Bean@186db54 Bean{id=’0′, name=’none’}
Thread-1 com.lavasoft.test2.Bean@291aff Bean{id=’0′, name=’none’}
Thread-0 com.lavasoft.test2.Bean@291aff Bean{id=’0′, name=’none’}
Thread-0 com.lavasoft.test2.Bean@291aff Bean{id=’0′, name=’none’}
Thread-1 com.lavasoft.test2.Bean@291aff Bean{id=’0′, name=’none’}
Process finished with exit code 0

从打印结果很清楚的看到,MyThreadLocal的tlt对象的确是一个,tlt对象里的ThreadLocal的tl对象也是一个,但 是,将t1t给每个线程用的时候,线程会重新创建Bean对象加入到ThreadLocal的Map中去使用。

Comments (25)