其实很早就想写一些java多线程方面的文章,只是奈何这东西看着简单,但要真要理解和正确使用可能还需要花费一定的精力,虽然平时的工作中会用到这方面的知识,可是更多的只是为了完成工作,至于详细的东西,也没有深入思考过。当然了网上关于这方面的知识很多,可是大部分都是东拼西凑,到处复制黏贴,虽然也有不少好的文章,但是还是那句老话,“纸上得来终觉浅,绝知此事要躬行”,因此就有了这个系列的文章。

说起多线程,首先就要说说线程, 这里小编不会在解释一些理论性的东西,我觉得程序员用代码说话最简单,下面是java中两种构造线程的方法,具体的一些解释会在代码注释中详细列出,首先我们会列举出两种实现线程的方法,具体直接看代码:

 package com.liuyang.junit.test.thread;

 /**
* demo1主要是用继承Thread类实现构造线程,在这里我们需要重写run方法
* 来实现我们具体的逻辑
* @author 刘洋
*/
public class ThreadDemo1 extends Thread { @Override
public void run() {
super.run();
System.out.println("当前正在运行的线程是:" + Thread.currentThread().getName());
} public static void main(String[] args) { //构造线程对象
ThreadDemo1 t1 = new ThreadDemo1();
//启动线程
t1.start();
//主线程输出
System.out.println("我是的demo1主线程");
}
} /**
* 这种方式是通过实现Runnable接口来实现多线程
*
*/
class ThreadDemo2 implements Runnable{ @Override
public void run() {
System.out.println("当前正在运行的线程是:" + Thread.currentThread().getName());
} public static void main(String[] args) {
//构造接口对象,后面会用到
ThreadDemo2 demo2 = new ThreadDemo2();
/**
* 构造线程对象,变化点主要在这里,我们可以通过一个实现了runnable接口的对象
* 来构造线程,而且这种方式还可以直接给线程命名
*/
Thread t1 = new Thread(demo2,"线程1");
//启动线程
t1.start();
//同样给主线程一个输出
System.out.println("我是的demo2主线程"); }
}

总结来说,有两种方式来实现线程对象的构造,当然一般采用Runnable接口线程有一定的好处,除了代码中我们看到的可以给线程命名外,采用接口编程的好处也是我们应该首选的原因。

其次我们知道线程的调度是由CPU决定的(在没有优先级的情况下),所以上面的代码在执行多次情况下会出现不同的结果,为了让大家拿到代码就能用,我自己也试了下(但是如果只是上述的代码,这中效果不明显),为了让效果显示的比较明显,可以再主线程中增加一个简答的循环打印(如果了解线程sleep的话也不失为一种很好的解决方法),这样效果会很明显,直接上代码:

 /**
* 这种方式是通过实现Runnable接口来实现多线程
*
*/
class ThreadDemo2 implements Runnable{ @Override
public void run() {
System.out.println("当前正在运行的线程是:" + Thread.currentThread().getName());
} public static void main(String[] args) {
//构造接口对象,后面会用到
ThreadDemo2 demo2 = new ThreadDemo2();
/**
* 构造线程对象,变化点主要在这里,我们可以通过一个实现了runnable接口的对象
* 来构造线程,而且这种方式还可以直接给线程命名
*/
Thread t1 = new Thread(demo2,"线程1");
//启动线程
t1.start();
//主线程增加一个打印输出,这样在执行多次情况下会有不同的结果显示,充分证明了CPU的调度能力
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
//同样给主线程一个输出
System.out.println("我是的demo2主线程");
}
}

当然对于解决刚才CPU调度效果的展示问题,我们也可以使用线程的休眠方法,也就是sleep方法,下面我们一起来看看sleep的用法,接着上面的代码,我们俩看看sleep的用法:

 /**
* 这种方式是通过实现Runnable接口来实现多线程
*
*/
class ThreadDemo2 implements Runnable{ @Override
public void run() {
//当前实现该runnable接口的线程休眠200ms
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前正在运行的线程是:" + Thread.currentThread().getName());
} public static void main(String[] args) {
//构造接口对象,后面会用到
ThreadDemo2 demo2 = new ThreadDemo2();
/**
* 构造线程对象,变化点主要在这里,我们可以通过一个实现了runnable接口的对象
* 来构造线程,而且这种方式还可以直接给线程命名
*/
Thread t1 = new Thread(demo2,"线程1");
//启动线程
t1.start();
//主线程休眠200ms
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程增加一个打印输出,这样在执行多次情况下会有不同的结果显示,充分证明了CPU的调度能力
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
//同样给主线程一个输出
System.out.println("我是的demo2主线程");
}
}

说了这么多线程知识了,应该进入我们今天的主题多线程,其实上述两个例子,本身是一个多线程例子(一个是main主线程和一个子线程 线程1),下面我们通过代码来看看多线程到底能干啥,有哪些实际的作用,以及多线程带来的一些问题。

首先说说多线程能干啥,说白了就是想最大限度的利用CPU资源,把以前单一串行的事情,通过多个线程实现并发(看清楚不是并行,当然现在计算机核心数已不再是传统的单核处理器,实现并行也是没有问题),就比如餐厅服务员这件事,以前呢老板给每一桌客人安排一个服务员,来负责这桌客人的所有服务,但是一个月下来老板发现者虽然客人多了,但是服务员的工资也不少,关键是这么多服务员在服务过程中还经常会出现无事可做的时候,这让作为一个资本家的老板很是头疼,老板经过仔细的思考后,决定裁员,由以前的一个人服务一桌,到现在一个人服务五桌,这样的安排之后老板发现即没有因为服务员人数的减少,而给顾客带来不便,因为每一个顾客也不一定都是同时来的(就算同时来了,稍微等几分钟也是可以的),所以当第一位客人点完餐后,第二位客人来了,这时候服务员刚好直接又去服务第二位客人,这样服务员的工作很饱和,而且成本也降低了。

下面就写一个简单的例子,主要是看看多线程的使用,顺便引出我们经常性关注的问题,线程安全问题。

 package com.liuyang.junit.test.thread;

 /**
* 使用多线程模拟抢票问题
*
* @author 刘洋
* @since 2018-08-19
*/
public class Web12306 implements Runnable { /**
* 模拟50张票
*/
private static int COUNT = 50; @Override
public void run() {
while(COUNT > 0){
System.out.println(Thread.currentThread().getName() + "抢到第" + COUNT-- + "张票");
}
} public static void main(String[] args) {
Web12306 web = new Web12306();
//启动三个线程来抢票
Thread t1 = new Thread(web,"甲");
Thread t2 = new Thread(web,"已");
Thread t3 = new Thread(web,"丙");
t1.start();
t2.start();
t3.start();
}
}

我们看看来执行代码情况,

甲抢到第50张票
已抢到第48张票
丙抢到第49张票
丙抢到第45张票
已抢到第46张票
甲抢到第47张票
已抢到第43张票
丙抢到第44张票
已抢到第41张票
已抢到第39张票
已抢到第38张票
已抢到第37张票
已抢到第36张票
已抢到第35张票
已抢到第34张票
已抢到第33张票
已抢到第32张票
已抢到第31张票
已抢到第30张票
甲抢到第42张票
已抢到第29张票
丙抢到第40张票
已抢到第27张票
甲抢到第28张票
已抢到第25张票
丙抢到第26张票
丙抢到第22张票
丙抢到第21张票
丙抢到第20张票
丙抢到第19张票
丙抢到第18张票
丙抢到第17张票
丙抢到第16张票
丙抢到第15张票
丙抢到第14张票
丙抢到第13张票
丙抢到第12张票
丙抢到第11张票
丙抢到第10张票
丙抢到第9张票
丙抢到第8张票
丙抢到第7张票
丙抢到第6张票
丙抢到第5张票
丙抢到第4张票
丙抢到第3张票
丙抢到第2张票
丙抢到第1张票
已抢到第23张票
甲抢到第24张票

从上面的代码可以看出,共享的静态变量COUNT确实在3个线程中随机被瓜分完了,但是我们也注意到,这种结果并不是我们想要的,这就引出了我们下节要说的线程安全问题。

好了天道酬勤、编码路上需要一颗持之以恒的心!!!!!

package com.liuyang.junit.test.thread;
/** * demo1主要是用继承Thread类实现构造线程,在这里我们需要重写run方法 * 来实现我们具体的逻辑 * @author 刘洋 */public class ThreadDemo1 extends Thread {
@Overridepublic void run() {super.run();System.out.println("当前正在运行的线程是:" + Thread.currentThread().getName());}public static void main(String[] args) {//构造线程对象ThreadDemo1 t1 = new ThreadDemo1();//启动线程t1.start();//主线程输出System.out.println("我是主线程");}}

谈谈java多线程(一)的更多相关文章

  1. Java多线程系列--“基础篇”09之 interrupt()和线程终止方式

    概要 本章,会对线程的interrupt()中断和终止方式进行介绍.涉及到的内容包括:1. interrupt()说明2. 终止线程的方式2.1 终止处于“阻塞状态”的线程2.2 终止处于“运行状态” ...

  2. 谈谈Java程序员进阶的那些知识和方向

    谈谈Java程序员进阶的那些知识和方向 记得前段时间看过一篇文章谈到一种程序员叫野生程序员,战斗力极强,可以搞定一切问题,但是通常看问题抓不到本质,或者说是google/baidu/stackover ...

  3. Java多线程同步的方法

    一 synchronized关键字 1.synchronized实现原理: ---基于对象监视器(锁) java中所有对象都自动含有单一的锁,JVM负责跟踪对象被加锁的次数.如果一个对象被解锁,其计数 ...

  4. Java多线程模型

    谈到Java多线程就涉及到多线程的模型及Java线程与底层操作系统之间的关系.正如我们熟知,现代机器可以分为硬件和软件两大块,如图2-5-1-1,硬件是基础,软件提供实现不同功能的手段.而且软件可以分 ...

  5. 谈谈JAVA中的安全发布

    谈谈JAVA中的安全发布 昨天看到一篇文章阐述技术类资料的"等级",看完之后很有共鸣.再加上最近在工作中越发觉得线程安全性的重要性和难以捉摸,又掏出了<Java并发编程实战& ...

  6. Java多线程面试题整理

    部分一:多线程部分: 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速. ...

  7. java多线程-----volatile

    谈谈Java中的volatile   内存可见性 留意复合类操作 解决num++操作的原子性问题 禁止指令重排序 总结 内存可见性 volatile是Java提供的一种轻量级的同步机制,在并发编程中, ...

  8. Java多线程之线程协作

    Java多线程之线程协作 一.前言 上一节提到,如果有一个线程正在运行synchronized 方法,那么其他线程就无法再运行这个方法了.这就是简单的互斥处理. 假如我们现在想执行更加精确的控制,而不 ...

  9. Java多线程专题5: JUC, 锁

    合集目录 Java多线程专题5: JUC, 锁 什么是可重入锁.公平锁.非公平锁.独占锁.共享锁 可重入锁 ReentrantLock A ReentrantLock is owned by the ...

随机推荐

  1. utf8_bin跟utf8_general_ci的区别

    ci是 case insensitive, 即 "大小写不敏感", a 和 A 会在字符判断中会被当做一样的; bin 是二进制, a 和 A 会别区别对待. 例如你运行: SEL ...

  2. Varish 缓存

    varish 缓存 2013年06月17日,Varnish Cache 3.0.4 发布,为目前最新版本. varish是以内存作为共享容器的:内存的大小决定了它的缓存容量.相对于主要以硬盘为存储的s ...

  3. MSSQL-SELECT&UPDATE动作要申请的锁

    最近在学习[MySQL事务&锁]这块知识,一不留神和MSSQL乱窜了~.~ 文章最初是想查看MySQL vs MSSQL在下面环境产生的阻塞现象会话1开启事务更新数据尚未提交->会话2读 ...

  4. strong、weak、copy、assign 在命名属性时候怎么用

    一直都在疑惑属性定义中在什么情况下用strong.在什么情况下用weak? 总结大致如下: 1.weak 是用来修饰代理(delegate)和UI控件. 2.strong 是用来修饰除了代理(dele ...

  5. Java 文本I/O 处理

    File类包含获得一个文件/目录的属性,以及对文件/目录进行改名和删除的方法. File类包含许多获取文件属性的方法,以及重命名和删除文件和目录的方法,但是,File类不包含读写文件内容的方法 Fil ...

  6. SAX解析XML文档——(二)

    SAX从上向下解析,一行一行解析.节省内存,不适合CRUD. XML文档: <?xml version="1.0" encoding="UTF-8"?&g ...

  7. Git入门——远程仓库及分支管理

    关于本地版本库的操作,请见:Git入门--本地版本库操作 本篇提到的所有命令: 小结 前面提到,Git相对于传统的SVN有着很大的优势,其中之一就在于集中式系统中,版本库只能存在于中央服务器上:而在G ...

  8. AT91RM9200---定时器简介

    1.前言 系统定时器模块集成了3个不同的定时器 一个周期性间隔的定时器,用来为操作系统设置时基 一个看门狗定时器,可用于软件死锁时进行系统复位 一个实时时钟计数器用来记录流逝的时间 系统定时器时钟 这 ...

  9. GPIO接口解析【转】

    本文提供了一个linux下访问GPIO的约定的概述. 这些调用使用gpio_* 命名前缀.没有别的调用会使用这个前缀或是相关的__gpio_*前缀. 转自:http://blog.163.com/w5 ...

  10. keepalived实现haproxy负载均衡器的高可用

    一.keepalived简介 keepalived是集群管理中保证集群高可用的一个服务软件,其功能类似于,用来防止单点故障. 二.vrrp协议2.1 vrrp协议简介 在现实的网络环境中,两台需要通信 ...