Java编程思想 第21章 并发

这是在2013年的笔记整理。现在重新拿出来,放在网上,重新总结下。

两种基本的线程实现方式 以及中断

package thread;

/**

*

*
@author
zjf

*
@create_time
2013-12-18

*
@use
测试基本的两种线程的实现方式

*         测试中断

*/

public
class BasicThreadTest {

public
static
void main(String[] args) {

Counter c1 = new Counter();

c1.start();

Thread c2 = new Thread(new Countable());

c2.start();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//中断

c1.interrupt();

c2.interrupt();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//此时c1线程已经终止
不能再次start 多次启动一个线程是非法的。java.lang.IllegalThreadStateException

c1.start();

}

/**

*

*
@author
zjf

*
@create_time
2013-12-18

*
@use
Runnable接口方式的实现

*/

static
class Countable implements Runnable{

public
void run() {

int i = 0;

while(!Thread.currentThread().isInterrupted())

{

System.out.println(this.toString() + "-------------" + i);

i ++;

}

}

}

/**

*

*
@author
zjf

*
@create_time
2013-12-18

*
@use
Thread继承方式的实现

*/

static
class Counter extends Thread{

public
void run() {

int i = 0;

while(!Thread.currentThread().isInterrupted())

{

System.out.println(this.toString() + "-------------" + i);

i ++;

}

}

}

}

中断
  • public void interrupt()

中断线程。

如果线程在调用 Object 类的 wait()wait(long)wait(long, int) 方法,或者该类的 join()join(long)join(long, int)sleep(long)sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException

  • public static boolean interrupted()

测试当前线程是否已经中断。线程的中断状态 由该方法清除。

  • public boolean isInterrupted()

测试线程是否已经中断。线程的中断状态 不受该方法的影响。

测试睡眠被中断

sleep是静态方法。

package thread;

/**

*

*
@author zjf

*
@create_time
2013-12-18

*
@use
测试Sleep方法被中断

*/

public
class SleepTest {

/**

*
@author
zjf

*
@create_time
2013-12-18

*
@use
测试目的:睡眠时是否可以被中断

*
@param
args

*/

public
static
void main(String[] args) {

Thread t = new Thread(new Sleepable());

t.start();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

t.interrupt();

}

static
class Sleepable implements Runnable{

public
void run() {

try {

//睡眠10秒
但是线程开始1秒后被中断
当前线程在睡眠时能够接收到中断
然后退出

Thread.sleep(10000);

} catch (InterruptedException e) {

//如果被中断
就退出

System.out.println("exit");//一秒后退出

}

}

}

}

测试使用yield让步

yield是静态方法。

package thread;

public
class YieldTest {

/**

*
@author
zjf

*
@create_time
2013-12-18

*
@use
测试yield方法

*
@param
args

*/

public
static
void main(String[] args) {

new Thread() {

@Override

public
void run() {

for(int i = 1; i < 100; i++)

{

System.out.println("yield-----" + i);

//测试结果显示
使用yield让步与不使用差别不大

Thread.yield();

}

}

}.start();

new Thread() {

@Override

public
void run() {

for(int i = 1; i < 100; i++)

{

System.out.println("notyield-----" + i);

}

}

}.start();

}

}

测试cached线程池

newCachedThreadPool:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

CachedThreadPool一般会创建所需数量的线程,并且会复用,这是选择的首选。

package thread;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public
class CachedThreadPoolTest {

/**

*
@author
zjf

*
@create_time
2013-12-18

*
@use
测试Cached线程池

*
@param
args

*/

public
static
void main(String[] args) {

/*

* cached线程池不能设置拥有线程的数量

*/

ExecutorService es = Executors.newCachedThreadPool();

for (int i = 0; i < 10; i++) {

es.execute(new Countable(i));

}

/*

* 因为要复用线程
所以线程执行完任务之后不会立刻关闭
而是等待一分钟(可配置)

* 的时间
如果在这一分钟期间没有新的任务要执行
会自动关闭

* shutdown标明不会再有新的任务加入
所以加入shutdown代码之后任务执行之后就会关闭线程

* 不会等待一分钟

*/

es.shutdown();

}

static
class Countable implements Runnable {

private
int
i;

public Countable(int i) {

this.i = i;

}

public
void run() {

System.out.println("第" + i + "启动的线程的ID是"

+ Thread.currentThread().getId());

/**

*
输出为

第0启动的线程的ID是7

第2启动的线程的ID是9

第1启动的线程的ID是8

第3启动的线程的ID是10

第4启动的线程的ID是11

第5启动的线程的ID是12

第6启动的线程的ID是13

第8启动的线程的ID是8

第7启动的线程的ID是9

第9启动的线程的ID是10

可见
在地8
9
10个线程的时候
复用了第1
2
3个线程。

这建立在第1
2
3个线程已经运行完的基础上。

*/

}

}

}

shutdown和shutdownnow

shutdown:

  • 阻止加入新的任务。
  • 结束已经完成任务的空闲线程,直到所有任务执行完毕,关闭所有线程为止。

shutdownnow:

  1. 完成shutdown的功能。
  2. 向每个未完成的线程发布中断命令。
  3. 返回未执行的任务列表
shutdownnow

package thread;

import java.util.List;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

public
class ShutdownNowTest {

/**

*
@author
zjf

*
@create_time
2014-2-18

*
@use

*
@param
args

*
@throws
InterruptedException

*/

public
static
void main(String[] args) throws InterruptedException {

ExecutorService es = Executors.newFixedThreadPool(3);

for(int i = 0; i < 10; i ++)

{

es.execute(new Countable(i));

}

TimeUnit.SECONDS.sleep(1);

//返回等待的任务列表

List<Runnable> countList = es.shutdownNow();

for(Runnable r : countList)

{

System.out.println(r.toString() + " is not done...");

}

}

}

class Countable implements Runnable{

private
int
i;

public Countable(int i) {

this.i = i;

}

public
int getI() {

return
i;

}

@Override

public String toString() {

return
"thread, id : " + i;

}

public
void run() {

try {

System.out.println(this.toString() + " is start to run...");

TimeUnit.MILLISECONDS.sleep(500);

System.out.println(this.toString() + " is done...");

} catch (InterruptedException e) {

System.out.println(this.toString() + " is interrupted...");

}

}

}

/**
输出

thread,
id
:
0
is
start
to
run...

thread,
id
:
1
is
start
to
run...

thread,
id
:
2
is
start
to
run...

thread,
id
:
0
is
done...

thread,
id
:
1
is
done...

thread,
id
:
2
is
done...

thread,
id
:
3
is
start
to
run...

thread,
id
:
4
is
start
to
run...

thread,
id
:
5
is
start
to
run...

thread,
id
:
5
is
done...

thread,
id
:
3
is
done...

thread,
id
:
4
is
done...

thread,
id
:
6
is
start
to
run...

thread,
id
:
6
is
interrupted...

thread,
id
:
7
is
not
done...

thread,
id
:
8
is
not
done...

thread,
id
:
9
is
not
done...

*/

测试ThreadFactory

package thread;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.ThreadFactory;

public
class ThreadFactoryTest {

/**

*
@author
zjf

*
@create_time
2013-12-18

*
@use
测试Cached线程池

*
@param
args

*/

public
static
void main(String[] args) {

ThreadFactory threadFactory = new MyThreadFactory();

ExecutorService es = Executors.newCachedThreadPool(threadFactory);

for (int i = 0; i < 10; i++) {

es.execute(new Countable(i));

}

es.shutdown();

}

static
class Countable implements Runnable {

private
int
i;

public Countable(int i) {

this.i = i;

}

public
void run() {

System.out.println("第" + i + "个任务正在运行!");

}

}

static
class MyThreadFactory implements ThreadFactory {

private
static
int
count = 0;

public Thread newThread(Runnable r) {

return
new MyThread(r,count++);

}

};

static
class MyThread extends Thread

{

private Runnable target;

private
int
count;

public MyThread(Runnable target, int count) {

super();

this.target = target;

this.count = count;

}

@Override

public
void run() {

System.out.println("第" + count + "个线程启动!" );

if(target != null)

{

target.run();

}

System.out.println("第" + count + "个线程结束!" );

}

}

}

/*

* 输出结果

第0个线程启动!

第1个线程启动!

第2个线程启动!

第3个线程启动!

第0个任务正在运行!

第1个任务正在运行!

第2个任务正在运行!

第4个线程启动!

第3个任务正在运行!

第5个线程启动!

第4个任务正在运行!

第5个任务正在运行!

第8个任务正在运行!

第6个线程启动!

第7个任务正在运行!

第7个线程启动!

第6个任务正在运行!

第9个任务正在运行!

第7个线程结束!

第0个线程结束!

第3个线程结束!

第6个线程结束!

第5个线程结束!

第1个线程结束!

第4个线程结束!

第2个线程结束!

证明:    Countable中的run方法被执行了10次

MyThread中的run方法只被执行了9次

原因:CachedThreadPool在需要的时候会调用ThreadFactory的newThread方法
但是也会用到缓存

*/

测试FixedThreadPool

newFixedThreadPool:创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。(这与cacheThreadPool不一样)

package thread;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class FixedThreadPoolTest {

/**

*
@author
zjf

*
@create_time
2013-12-18

*
@use
测试Fixed线程池

*
@param
args

*/

public static void main(String[] args) {

ExecutorService es = Executors.newFixedThreadPool(3);

for (int i = 0; i < 5; i++) {

es.execute(new Countable(i));

}

es.shutdown();

}

static class Countable implements Runnable {

private int i;

public Countable(int i) {

this.i = i;

}

public void run() {

System.out.println("第" + i + "启动的线程的ID是"

+ Thread.currentThread().getId());

}

}

}

/*

第0启动的线程的ID是7

第2启动的线程的ID是9

第1启动的线程的ID是8

第3启动的线程的ID是7

第4启动的线程的ID是9

*/

SingleThreadExecutor

newSingleThreadExecutor():

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程(备注:应该是内部实现的差异 外部的使用没什么差异)。

因为一个任务执行完毕之后,线程才会空闲下来去执行另外一个任务,所以可以保证顺序执行任务。

测试ScheduledExecutorService

scheduled

adj. 预定的;已排程的

v. 把…列表;把…列入计划;安排(schedule的过去分词)

上面演示的线程执行器或者线程池都是ExecutorService,下面看看ScheduledExecutorService。ScheduledExecutorService集成并且扩展了ExecutorService,可安排在给定的延迟后运行或定期执行的命令。

package thread;

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

public
class SingleThreadScheduledTest {

/**

*
@author
zjf

*
@create_time
2013-12-23

*
@use
测试SingleThreadScheduled线程池

*
@param
args

*
@throws
InterruptedException

*/

public
static
void main(String[] args) throws InterruptedException {

ScheduledExecutorService es = Executors

.newSingleThreadScheduledExecutor();

//ScheduledThreadPool需要传参控制池中所保存的线程数(即使线程是空闲的也包括在内)

//ScheduledExecutorService es = Executors.newScheduledThreadPool(1);

// 给定时间延迟后执行

// es.schedule(new Countable(), 1, TimeUnit.SECONDS);

// 传入一个任务然后按照给定频率循环执行
在每次任务开始执行的时间点之间存在固定间隔

//es.scheduleAtFixedRate(new Countable(), 2, 1, TimeUnit.SECONDS);

// 传入一个任务然后按照给定频率循环执行
每一次执行终止和下一次执行开始之间都存在给定的间隔

es.scheduleWithFixedDelay(new Countable(), 2, 1, TimeUnit.SECONDS);

// 如果没有这句代码
将没有任何反应,因为----|

// 下面的shutdown代码将会阻止执行新加入任务
包含延迟执行而未执行的任务

TimeUnit.SECONDS.sleep(10);

es.shutdown();

}

static
class Countable implements Runnable {

public
void run() {

System.out.println("一个任务运行开始!");

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

}

System.out.println("一个任务运行结束!");

}

}

}

package thread;

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

public
class ScheduledThreadPoolTest {

/**

*
@author
zjf

*
@create_time
2013-12-23

*
@use
测试SingleThreadScheduled线程池

*
@param
args

*
@throws
InterruptedException

*/

public
static
void main(String[] args) throws InterruptedException {

//ScheduledThreadPool需要传参控制池中所保存的线程数(即使线程是空闲的也包括在内)

ScheduledExecutorService es = Executors.newScheduledThreadPool(1);

es.scheduleAtFixedRate(new Countable(), 0, 1, TimeUnit.SECONDS);

TimeUnit.SECONDS.sleep(10);

es.shutdown();

}

static
class Countable implements Runnable {

public
void run() {

System.out.println("一个任务运行开始!");

try {

TimeUnit.SECONDS.sleep(3);

} catch (InterruptedException e) {

}

System.out.println("一个任务运行结束!");

}

}

}

/*

* 线程池中只有一个线程 + 每隔1秒要执行一个任务 + 一个任务要运行3秒才结束

* 结果是每隔3秒才能执行一次

*/

时间: 2024-08-07 00:15:29

Java编程思想 第21章 并发的相关文章

Java编程思想——第21章 并发(二)

三.共享受限资源 对于并发任务,你需要某种方式来防止两个任务访问相同的资源,至少在关键阶段不能出现这种情况. 1.解决共享资源竞争 防止这种冲突的方法就是当资源被一个任务使用时,在其上加锁.基本上所有的并发模式在解决线程冲突问题的时候,都是采用序列化访问共享资源的方案.通常这是通过在代码前面加上以挑锁语句来实现的,这使得在一段时间内只有一个任务可以运行这段代码.因为锁语句产生了一种互相排斥的效果,所以这种机制撑场成为互斥量(mutex). synchronized,当代码要执行被synchron

Java编程思想——第17章 容器深入研究(two)

六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add 在尾部增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementExce

java编程思想笔记(第一章)

Alan Kay 第一个定义了面向对象的语言 1.万物皆对象 2.程序是对象的集合,他们彼此通过发送消息来调用对方. 3.每个对象都拥有由其他对象所构成的存储 4.每个对象都拥有其类型(TYpe) 5.某一特定类型的所有对象都可以接收同样的消息. Booch提出一种更简洁的描述: 对象拥有状态(state) 行为(behavior) 和标识(identity) 每个对象都有一个接口 每个对象都属于定义了特性和行为的某个类(特性可以理解为属性的状态,行为可以理解为method) 在面向对象的程序设

Java编程思想笔记(第二章)

第二章  一切都是对象 尽管Java是基于C++的,但相比之下,Java是一种更纯粹的面向对象程序设计语言. c++和Java都是杂合型语言(hybird language) 用引用(reference)操作对象 类似遥控器(引用)来操作电视(对象) 在Java中你可以创建一个引用,但是没有与任何对象关联,比如: String s; 这个时候如果用则会报错.安全的做法是: 创建一个引用的同时并进行初始化 String s="1111"; 必须由你创建所有对象 New关键字的意思是给我一

Java 编程思想 第五章 ----初始化与清理(1)

从今天开始每天一小时的java 编程思想的阅读和编码,其实就是把书上的代码抄下来. 5.5 清理:终结处理和垃圾回收 初始化和清理工作同等重要,但是清理工作却被常常忘记,但是在使用对象之后,对对象弃之不顾的做法并不是很安全.Java有自己的垃圾回收器负责回收无用的对象占据的内存资源.但也有特殊情况:假定你的内存区域不是用new获得的,这是无法用垃圾回收器释放所以java中允许在类中定义一个名为 finalize()的方法.       工作原理: 一旦垃圾回收器准备好释放对象占用的存储空间,将首

java 编程思想 一 第二章(对象)

上班之余发现有很多空闲时间,享受生活 又觉得有点空虚,而且对自己的基础知识总觉得掌握的不是很牢固,有点似懂非懂的感觉,近来刚好有时间,所以就考虑继续学习,然后再经过考虑和各大博主推荐选择了<java编程思想>这本书,在此分享学习心得跟大家共勉,也算是对自己的监督吧.(本内容需要有一定的基础才能看,类似于基础回顾,强化理解,新手可能有些地方不太能听懂) 一.什么是对象? 这并不是我们男女朋友那种对象哈哈. 简言之:万事万物皆对象. 个人理解:我们所要处理的事务或者建立的某种模型的抽象总结.具体就

Java编程思想之二十 并发

20.1 并发得多面性 并发编程令人困惑的一个主要原因:使用并发时需要解决的问题有多个,而实现并发的方法也有多种,并且在这两者之间没有明显的映射关系. 20.1.1 更快的执行 速度问题初听起来很简单:如果你需要一个程序运行得更快,那么可以将起断开为多个片段,在单个处理器上运行每个片段. 并发通常是提高运行在单个处理器上的程序的性能,但在单个处理器上运行的并发程序开销确实应该比该程序所有部分都顺序执行开销大,因为其中增加了所谓的上下文切换的代价. 如果没有任务会阻塞,那么在单处理器上使用并发就没

Java编程思想学习(十六) 并发编程

线程是进程中一个任务控制流序列,由于进程的创建和销毁需要销毁大量的资源,而多个线程之间可以共享进程数据,因此多线程是并发编程的基础. 多核心CPU可以真正实现多个任务并行执行,单核心CPU程序其实不是真正的并行运行,而是通过时间片切换来执行,由于时间片切换频繁,使用者感觉程序是在并行运行.单核心CPU中通过时间片切换执行多线程任务时,虽然需要保存线程上下文,但是由于不会被阻塞的线程所阻塞,因此相比单任务还是大大提高了程序运行效率. 1.线程的状态和切换: 线程的7种状态及其切换图如下: 2.多线

Java编程思想(第十一章持有对象)

1. 第11章 持有对象 java通过使用容器类来储存对象,与普通数组不同的是,普通数组的长度不可变. 1.1. 泛型与类型安全的容器 使用预定义的泛型,它指定了这个容器实例可以保存的类型,通过使用泛型,就可以在编译器防止将错误类型的对象放置到容器中. public class gerbil { private int gerbilNumber; public gerbil(int gerbilNumber){ this.gerbilNumber = gerbilNumber; } public