1.        传统线程技术回顾

继承线程与实现Runnable的差异?为什么那么多人都采取第二种方式?

因为第二种方式更符合面向对象的思维方式。创建一个线程,线程要运行代码,而运行的代码都封装到一个独立的对象中去。一个叫线程,一个叫线程运行的代码,这是两个东西。两个东西一组合,就表现出了面向对象的思维。如果两个线程实现数据共享,必须用Runnable的方式。

查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。

问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么,线程运行时的执行代码是子类的run方法的代码?还是Runnable对象的run方法的代码?子类的run方法。

示例代码

     new Thread(new Runnable() {
public void run() {
while (true) {
System.out.println("run:runnable");
}
}
}) {
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
System.out.println("run:thread");
}
}
}.start();

该线程会运行重写的Thread中的run方法,而不是Runnable中的run方法,因为在传统的Thread的run方法是:

     public void run() {
if (target != null) {
target.run();
}
}

如果想要运行Runnable中的run方法,必须在Thread中调用,但是此时我重写了Thread中的run方法,导致if (target != null) {  target.run();     }不存在,所以调用不了Runnable中的run方法。

注意:多线程的执行,会提高程序的运行效率吗?为什么会有多线程下载?

不会,有时候还会降低程序的运行效率。因为CPU只有一个,在CPU上下文切换的时候,可能还会耽误时间。

多线程下载:其实你的机器没有变快,而是你抢了服务器的带宽。如果你一个人下载,服务器给你提供的是20K的话,那么10个人的话,服务器提供的就是200K。这个时候你抢走200k,所以感觉变快了。

2.        传统定时器技术回顾(jdk1.5以前)Timer、TimerTask

Timer:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。

scheduleAtFixedRate(TimerTask task, Date firstTime, long period):

安排指定的任务在指定的时间开始进行重复的固定速率执行。

schedule(TimerTask task, long delay, long period): 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。

作业调度框架 Quartz(专门用来处理时间时间的工具)。你能够用它来为执行一个作业而创建简单的或复杂的调度。{ http://www.oschina.net/p/quartz}

问题:每天早晨3点来送报纸。

问题:每个星期周一到周五上班,周六道周日不上班。

示例代码:(间隔不同时间,执行不同事件)

 package com.chunjiangchao.thread;

 import java.util.Timer;
import java.util.TimerTask;
/**
* 重复执行某项任务,但是时间间隔性不同是2,4这种状态
* @author chunjiangchao
*
*/
public class TimerDemo02 { private static long count = 1;
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new Task(), 1000);
/*
测试打印结果如下:
执行任务,当前时间为:1460613231
执行任务,当前时间为:1460613235
执行任务,当前时间为:1460613237
执行任务,当前时间为:1460613241
*/ new Thread(new Runnable() {
public void run() {
while (true) {
System.out.println("run:runnable");
}
}
}) {
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
System.out.println("run:thread");
}
}
}.start(); } static class Task extends TimerTask{ @Override
public void run() {
System.out.println("执行任务,当前时间为:"+System.currentTimeMillis()/1000);
new Timer().schedule(new Task(), 2000*(1+count%2));
count++;
} } }

3.        传统线程互斥技术

本道例题:关键在于说明:要想实现线程间的互斥,线程数量必须达到两个或者两个以上,同时,访问资源的时候要用同一把锁(这个是必须的)。如果两个线程都访问不同的同步代码块,而且它们的锁对象都不相同,那么这些线程就没有达到同步的目的。

示例代码:(访问同一个资源对象,但是锁对象不同,同样没有达到同步的目的)

 package com.chunjiangchao.thread;
/**
* 线程间同步与互斥
* @author chunjiangchao
* 因为print1方法与print3方法锁对象相同,所以在调用的时候,会产生互斥的现象,而print2的锁是当前正在执行对象print2方法的对象,
* 与print1和print3同时执行,打印结果就不是期望的结果
*
*/
public class TraditionalThreadSynchronizedDemo { public static void main(String[] args) {
final MyPrint myPrint = new MyPrint();
//A
new Thread(new Runnable() { @Override
public void run() {
myPrint.print1("chunjiangchao");
}
}).start();
//B
// new Thread(new Runnable() {
//
// @Override
// public void run() {
// myPrint.print2("fengbianyun");
// }
// }).start();
//C
new Thread(new Runnable() { @Override
public void run() {
myPrint.print3("liushaoyue");
}
}).start();
}
static class MyPrint{
public void print1(String str){
synchronized (MyPrint.class) {
for(char c :str.toCharArray()){
System.out.print(c);
pSleep(200);
}
System.out.println("print1当前已经打印完毕");
} }
public synchronized void print2(String str){
for(char c :str.toCharArray()){
System.out.print(c);
pSleep(200);
}
System.out.println("print2当前已经打印完毕");
}
public synchronized static void print3(String str){
for(char c :str.toCharArray()){
System.out.print(c);
pSleep(200);
}
System.out.println("print3当前已经打印完毕");
}
private static void pSleep(long time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
} } }

4.        传统线程同步通信技术

在设计的时候,最好将相关的代码封装到一个类中,不仅可以方便处理,还可以实现内部的高耦合。

问题示例图

经验总结:要用到共同数据(包括同步锁)或共同算法的若干个方法应该归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性。

同步通信,互斥的问题不是写在线程上面的,而是直接写在资源里面的,线程是直接拿过来使用就可以了。好处就是,以后我的类,交给任何一个线程去访问,它天然就同步了,不需要考虑线程同步的问题。如果是在线程上面写,明天又有第三个线程来调用我,还得在第三个线程上面写互斥写同步。(全部在资源类的内部写,而不是在线程的代码上面去写)

示例代码:(子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100, 如此循环50次,请写出程序。)

 package com.chunjiangchao.thread;

 public class TraditionalThreadCommunication {
/**
* 经验:涉及到线程互斥共享,应该想到将同步方法写在资源里面,而不是写在线程代码块中 在资源中判断标记的时候,最好用while语句
*/
public static void main(String[] args) {
final Output output = new Output();
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50; i++) {
output.sub(10);
}
}
}).start();
for (int i = 0; i < 50; i++) {
output.main(i);
}
}
} class Output {
private boolean flag = true; public synchronized void sub(int i) {
while (!flag) {// 用while比用if更加健壮,原因是即使线程被唤醒了,也判断一下是不是真的该它执行了
// 防止伪唤醒的事件发生。
try {
this.wait();
} catch (InterruptedException e) {
}
}
for (int j = 0; j < 10; j++) {
System.out.println(i + "子线程运行" + j);
}
flag = false;// 记得要改变一下标记的状态
this.notify();// 最后要唤醒其他要使用该锁的线程
} public synchronized void main(int i) {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
for (int j = 0; j < 100; j++) {
System.out.println(i + "主线程运行" + j);
}
flag = true;
this.notify();
}
}

5.        线程范围内共享变量的概念与作用

线程范围内的数据共享:不管是A模块,还B模块,如果他们在同一个线程上运行,那么他们操作的数据应该是相同。而不应该是不管A模块和B模块在哪个线程上面运行他们的数据在每个线程中的数据是一样的。(应该是各自线程上的数据是独立的)

线程间的事务处理:

如图:

不能出现这样的情况:thread1转入的data,还没有来得及操作。CPU时间片转入到thread2,该thread2来执行,转入、转出,最后直接提交事务。导致thread1的数据出现错误。

线程范围内的变量有什么用?

我这件事务在线程范围内搞定,不要去影响别的线程的事务。但是我这个线程内,几个模块之间是独立的,这几个模块又要共享同一个对象。它们既要共享又要独立,在线程内共享,在线程外独立。【对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。】

示例代码(不同线程之间共享同一个Map对象,但是Map中的每个元素表示的是不同线程的数据)

 package com.chunjiangchao.thread;

 import java.util.HashMap;
import java.util.Map;
import java.util.Random; /**
* 线程范围内共享数据
* @author chunjiangchao
*
*/
public class ThreadScopeShareDataDemo {
//所有线程共享的数据是datas,但是datas中的每个元素key是Thread,每个元素针对每个线程来说是独立的,value代表不同线程处理的数据
private static Map<Thread,Integer> datas = new HashMap<Thread,Integer>();
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable() { @Override
public void run() {
int nextInt = new Random().nextInt();
datas.put(Thread.currentThread(), nextInt);
///A模块与B模块是独立的,但是A与B共享当前当前线程中的数据
new ModuleA().getThreadData();
new ModuleB().getThreadData();
}
}).start();
}
/*
打印的结果为
Thread-1的ModuleA获取的变量为:-918049793
Thread-0的ModuleA获取的变量为:-1424853148
Thread-0的ModuleB获取的变量为:-1424853148
Thread-1的ModuleB获取的变量为:-918049793 */
}
static class ModuleA{
public void getThreadData(){
System.out.println(Thread.currentThread().getName()+"的ModuleA获取的变量为:"+datas.get(Thread.currentThread()));
}
}
static class ModuleB{
public void getThreadData(){
System.out.println(Thread.currentThread().getName()+"的ModuleB获取的变量为:"+datas.get(Thread.currentThread()));
}
} }

6.        ThreadLocal类及应用技巧

ThreadLocal就相当于一个Map。

一个ThreadLocal代表一个变量,故其中只能放一个数据,你有两个变量都要线程范围内共享,则要定义两个ThreadLocal对象,如果有一个一百个变量要线程共享?那么就应该定义一个对象来装着一百个变量,然后在ThreadLocal中存储这一个对象。

问题:怎么在线程结束的时候得到通知?提示:监听虚拟机结束(只是一种思路)。就我目前所学的知识来说,还没找到这个方法。

最重要的一点,就是里面涉及到的设计方法。

示例代码(演示ThreadLocal的使用,不同线程中使用相同类型对象的设计)

 package com.chunjiangchao.thread;

 import java.util.Random;

 /**
* 使用ThreadLocal
* @author chunjiangchao
*
*/
public class ThreadLocalDemo {
private static ThreadLocal<Integer > localInteger = new ThreadLocal<Integer>();
public static void main(String[] args) {
//创建两个线程
for(int i=0;i<2;i++){
new Thread(new Runnable() { @Override
public void run() {
int randomInt = new Random().nextInt();
localInteger.set(randomInt);
new ModuleA().getDataShareData();
new ModuleB().getDataShareData(); //给线程中的变量进行赋值
ThreadScopeData threadInstance = ThreadScopeData.getThreadInstance();
threadInstance.setName("每个线程里面都有不同的ThreadScopeData对象---"+randomInt);
threadInstance.setHabbies("每个线程里面都有不同的ThreadScopeData对象---设置不同对象的成员变量"+randomInt);
//取出数据
new ModuleC().getDataShareData();
new ModuleD().getDataShareData(); }
}).start();
}
//测试的结果为:
/*
Thread-0在ModuleA中访问的数据为:-1711153118
Thread-1在ModuleA中访问的数据为:532928477
Thread-1在ModuleB中访问的数据为:532928477
Thread-0在ModuleB中访问的数据为:-1711153118
Thread-1在ModuleC中访问的数据为:
每个线程里面都有不同的ThreadScopeData对象---532928477
每个线程里面都有不同的ThreadScopeData对象---设置不同对象的成员变量532928477
Thread-0在ModuleC中访问的数据为:
每个线程里面都有不同的ThreadScopeData对象----1711153118
每个线程里面都有不同的ThreadScopeData对象---设置不同对象的成员变量-1711153118
Thread-1在ModuleD中访问的数据为:
每个线程里面都有不同的ThreadScopeData对象---532928477
每个线程里面都有不同的ThreadScopeData对象---设置不同对象的成员变量532928477
Thread-0在ModuleD中访问的数据为:
每个线程里面都有不同的ThreadScopeData对象----1711153118
每个线程里面都有不同的ThreadScopeData对象---设置不同对象的成员变量-1711153118
* */
}
static class ModuleA{
public void getDataShareData(){
int data = localInteger.get();
System.out.println(Thread.currentThread().getName()+"在ModuleA中访问的数据为:"+data);
}
}
static class ModuleB{
public void getDataShareData(){
int data = localInteger.get();
System.out.println(Thread.currentThread().getName()+"在ModuleB中访问的数据为:"+data);
}
}
static class ModuleC{
public void getDataShareData(){
ThreadScopeData data = ThreadScopeData.getThreadInstance();
System.out.println(Thread.currentThread().getName()+"在ModuleC中访问的数据为:");
System.out.println(data.getName());
System.out.println(data.getHabbies());
}
}
static class ModuleD{
public void getDataShareData(){
ThreadScopeData data = ThreadScopeData.getThreadInstance();
System.out.println(Thread.currentThread().getName()+"在ModuleD中访问的数据为:");
System.out.println(data.getName());
System.out.println(data.getHabbies());
}
} }

不同线程中使用相同类型的对象设计:

 package com.chunjiangchao.thread;
/**
* 设计线程范围内共享的对象:我这个对象的实例是与每个线程都相关的,那么这个设计就交个我这个类去办,其他的用户在
* 在任意的线程中调用我的方法,自然的就是与这个线程有关的实例。我们的类设计出来就是与线程绑定的,在线程的任意地
* 方调用我的方法,我就会返回与这个线程有关的实例对象,这个实例对象不需要用户去创建。
* struts2就是这个思想:每一个请求来了,就激活一个线程(一个请求就是一个容器),这个线程就会有一个容器对象,
* 这个容器对象就会将这个请求相关的所有东西都装载容器里面,这个容器里面装的东西只对这个请求有效。即使来了另外一
* 个请求,那也会有另外一个容器,与当前的容器没有关系。
* 隐藏了ThreadLocal这个变量,不用对外面暴露。外面可以直接调用我的方法就可获取与当前线程有关的数据
* 这个模式与单例设计模式差不多,单例设计模式是无论在程序中的什么地方调用,返回的都是同一个对象。
* 而这种设计是:无论在一个线程范围内的什么地方调用,返回的都是同一个对象。在不同范围内调用返回的是不同对象。
*
*/
class ThreadScopeData{
//当前ThreadLocal成员变量隐藏起来
private static ThreadLocal<ThreadScopeData> threadLocal = new ThreadLocal<ThreadScopeData>();
private ThreadScopeData(){}
public static ThreadScopeData getThreadInstance(){
ThreadScopeData data = threadLocal.get();
if(data==null){
data = new ThreadScopeData();
threadLocal.set(data);
}
return data;
}
//设置成员变量
private String name;
private String habbies;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHabbies() {
return habbies;
}
public void setHabbies(String habbies) {
this.habbies = habbies;
} }

 未完待续……

02.Java多线程并发库API使用的更多相关文章

  1. 03.Java多线程并发库API使用2

    1.多个线程之间共享数据的方式探讨 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. 2.如果每个线程执行的代 ...

  2. 04.Java多线程并发库API使用3

    1.java5的Semaphere同步工具 Semaphore可以维护当前访问自身的线程个数,并提供了同步机制.使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数 ...

  3. java——多线程并发库

    JDK5中增加了Doug Lea的并发库,这一引进给Java线程的管理和使用提供了强大的便利性. java.util.current包中提供了对线程优化.管理的各项操作,使得线程的使用变得的心应手.该 ...

  4. Java多线程并发02——线程的生命周期与常用方法,你都掌握了吗

    在上一章,为大家介绍了线程的一些基础知识,线程的创建与终止.本期将为各位带来线程的生命周期与常用方法.关注我的公众号「Java面典」了解更多 Java 相关知识点. 线程生命周期 一个线程不是被创建了 ...

  5. Java高并发秒杀API之Service层

    Java高并发秒杀API之Service层 第1章 秒杀业务接口设计与实现 1.1service层开发之前的说明 开始Service层的编码之前,我们首先需要进行Dao层编码之后的思考:在Dao层我们 ...

  6. Java高并发秒杀API之业务分析与DAO层

    根据慕课网上关于java高并发秒杀API的课程讲解用maven+ssm+redis实现的一个秒杀系统 参考了codingXiaxw's blog,很详细:http://codingxiaxw.cn/2 ...

  7. 使用Java线程并发库实现两个线程交替打印的线程题

    背景:是这样的今天在地铁上浏览了以下网页,看到网上一朋友问了一个多线程的问题.晚上闲着没事就决定把它实现出来. 题目: 1.开启两个线程,一个线程打印A-Z,两一个线程打印1-52的数据. 2.实现交 ...

  8. Java 多线程并发编程一览笔录

    Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...

  9. Java多线程并发03——在Java中线程是如何调度的

    在前两篇文章中,我们已经了解了关于线程的创建与常用方法等相关知识.接下来就来了解下,当你运行线程时,线程是如何调度的.关注我的公众号「Java面典」了解更多 Java 相关知识点. 多任务系统往往需要 ...

随机推荐

  1. HDU 2516 取石子游戏(FIB博弈)

    取石子游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  2. 解决Windows时间同步失败问题!系统时间同步设置!

    使用NTP协议可以让你的计算机自动与服务器上的时间同步.从而保持最准确的时间. 中国国家授时中心的IP地址是:210.72.145.44 (至少我一直没ping通) 在Windows XP/2000/ ...

  3. TQImport3XLS.Map

    property Map: TStrings; 设置数据集字段和Excel单元格之间定义映射属性以下列方式: FieldName=CellRange 导入单独的单元格 Field1=A1 Field1 ...

  4. SQL嵌套查寻初识,以及SOME ANY EXISTS的基础常识

    定义: 1 .指在一个外层查询中包含有另一个内层查询.其中外层查询称为主查询,内层查询称为子查询. 2 .SQL允许多层嵌套,由内而外地进行分析,子查询的结果作为主查询的查询条件 3 .子查询中一般不 ...

  5. C++拓扑排序

    安利一篇比较写的比较好的的博客... 拓扑排序的原理及其实现 我本来以为我看懂了原理就会打了,没想到因为没有手动实践过...原理实际上也没记清楚.... 一题HDU的拓扑裸题HDU 3342 我的拓扑 ...

  6. CodeForces 702B Powers of Two (暴力,优化)

    题意:给定 n 个数,问你从有多少下标 i < j,并且 ai + aj 是2的倍数. 析:方法一: 从输入开始暴力,因为 i < j 和 i > j 是一样,所以可以从前面就开始查 ...

  7. Delphi中GUID相等检查中经典指针应用

    type PGUID = ^TGUID; TGUID = packed record D1: LongWord; D2: Word; D3: Word; D4: array[0..7] of Byte ...

  8. StructLayout特性

    StructLayout特性          公共语言运行库利用 StructLayoutAttribute控制类或结构的数据字段在托管内存中的物理布局,即类或结构需要按某种方式排列.如果要将类传递 ...

  9. C++成员变量、构造函数的初始化顺序 [转]

    C++成员变量.构造函数的初始化顺序 一.C++成员变量初始化 1.普通的变量:一般不考虑啥效率的情况下 可以在构造函数中进行赋值.考虑一下效率的可以再构造函数的初始化列表中进行 2.static 静 ...

  10. (4)html表格

    本节解说 :html的表格 表格: *<table></table> 标签定义 HTML 表格. *简单的 HTML 表格由 table 元素以及一个或多个 tr.th 或 t ...