其实很早就想写一些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. Linux - 网络检测

    linux 利用bmon/nload/iftop/vnstat/iptraf实时查看网络带宽状况 .添加yum源方便安装bmon # rpm -Uhv http://apt.sw.be/redhat/ ...

  2. Git操作学习笔记

    根据廖雪峰老师git教程学习整理 这里需要辨析一下概念.Github是代码托管平台,是协作的工具;而Git是版本控制工具.Git不需要联网,在本机就可以使用 集中式版本控制系统与分布式版本控制系统 S ...

  3. G - 楼房重建 (线段树)

    题目链接:https://cn.vjudge.net/contest/281960#problem/G 题目大意:中文问题 具体思路:首先每一个点的值可以用当前这个点的斜率来表示,每一次输入一个值,我 ...

  4. Mybatis进阶学习笔记——关系查询——一对多查询

    一个客户拥有多个订单 <resultMap type="User" id="UserOrderResultMap"> <id column=& ...

  5. URLSession

    URLSession时ios7中的心得网络接口,与NSURLConnection是并列的. 当程序在前台时,URLSession与NSURLConnection大部分可以互相替代. URLSessio ...

  6. android fragment解析

    1.fragment加载到Activity (1).添加fragment到Activity的布局文件 (2).动态在activity中添加fragment 例子: // 步骤1:获取FragmentM ...

  7. Docker三要素

    一.镜像(Image) Docker镜像(Image)就是一个只读的模板,镜像可以用来创建Docker容器,一个镜像可以创建很多容器. Docker 面向对象 镜像 类(class) 容器 实例对象 ...

  8. Android OAuth认证

    OAuth认证 为了安全地访问在线服务,用户需要在服务上进行身份验证,即要提供他们的身份的证明.对于一个要访问第三方服务的程序来说,安全问题甚至更复杂.不仅仅是用户需要在访问服务前要进行身份验证,而且 ...

  9. S5PV210 NAND Flash

    NAND Flash 关于NAND FlashS5PV210的NAND Flash控制器有如下特点:1) 支持512byte,2k,4k,8k的页大小2) 通过各种软件模式来进行NAND Flash的 ...

  10. MySQL双版本共存解决方案

    案例是MySQL5.5(3306端口)和MySQL5.6(3307端口). 1. 修改C:\Program Files (x86)\MySQL\MySQL Server 5.6\下的my-xxx.in ...