简述Java多线程(一)
JAVA多线程
程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
进程:是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位。
线程是CPU调度和执行的单位。
创建一个新的执行线程有两种方法:一是将一个类声明为Thread子类。这个子类应该重写run方法,编写线程执行体,创建线程对象,调用start()方法启动线程。二是实现Runnable接口;实现run()方法,编写线程执行体;创建线程,调用start()启动线程。
线程开启不一定立即执行,看CPU的调度
创建方法一举例:
public class Threaddemo1 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for(int i = 0 ; i < 100 ; i++){
            System.out.println("thread"+i);
        }
    }
    public static void main(String[] args) {
        Threaddemo1 thread = new Threaddemo1();
        thread.start();
        for(int i = 0 ; i < 100 ; i++){
            System.out.println("hello"+i);
        }
    }
}
输出结果是相互交替的,因为是CPU负责调度,每次结果都可能不一样,若把start()换成run()则顺序确定,性质就不同了。
创建方法二举例:
public class TestThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0 ; i < 100 ; i++){
            System.out.println("thread"+i);
        }
    }
    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread testThread = new TestThread();
        //创建线程对象
        Thread thread = new Thread(testThread);
        thread.start();
        for(int i = 0 ; i < 100 ; i++){
            System.out.println("hello"+i);
        }
    }
}
两种方法稍微有差异,但是以上两段代码输出等效
总结
继承Thread类:子类继承Thread类具备多线程能力;启动线程只需要子类对象.start();不建议使用(避免OOP单继承局限性)
实现Runnable接口:启动线程需要传入目标对象+Thread对象.Strat();推荐使用(避免单继承局限性,灵活方便,方便同一个对象被多个线程使用)
模拟抢票
@Override
public void run() {
    while(true){
        if(Nums <= 0)
            break;
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--> get the No."+Nums--);
    }
}
重写run(),该段实现每次间隔200毫秒,环境模拟抢票,main()如下
TestThread1 testThread1 = new TestThread1();
new Thread(testThread1,"xiaoming").start();
new Thread(testThread1,"xiaohong").start();
new Thread(testThread1,"huangniu").start();
该demo出现了不安全情况,可能同时出现两个线程抢占一个资源,考虑信号量方面,没有实现互斥。

模拟龟兔赛跑
public class race implements Runnable{
    private static String winner;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            boolean flag = gameOver(i);
            if (flag) {
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
        }
    }
    //判断是否完成比赛
    private boolean gameOver(int steps){
        if(winner != null){
            return true;
        }
        if(steps == 100){
            winner = Thread.currentThread().getName();
            System.out.println("winner is "+winner);
            return true;
        }
        return false;
    }
    public static void main(String[] args) {
        race race1 = new race();
        new Thread(race1,"兔子").start();
        new Thread(race1,"乌龟").start();
    }
}
在这个demo中发现,若不建立公共变量,是不会出现抢占资源的情况的。
实现Callable接口(简单了解)
可以定义返回值,可以抛出异常
- 需要定义返回值类型
- 重写call方法,若获取结果需抛出异常
- 创建目标对象
- 创建执行服务
- 提交执行
- 获取结果
- 关闭服务
举例一个demo,首先介绍定义的内部类
class WebDownloader{
    public void downloader(String url, String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO Exception,downloader has a problem");
        }
    }
}
该类实现的是将一个url文件下载下来
public class TestCallable implements Callable <Boolean>{
    private String url;
    private String name;
    public TestCallable(String url,String name){
        this.url = url;
        this.name = name;
    }
    @Override
    public Boolean call() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("download the file named "+name);
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable s = new TestCallable("URL.jpg","wallpaper1.jpg");
        TestCallable s1 = new TestCallable("URL.jpg","wallpaper2.jpg");
        TestCallable s2 = new TestCallable("URL.jpg","wallpaper3.jpg");
        //创建执行服务,线程池
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> rs1 = ser.submit(s);
        Future<Boolean> rs2 = ser.submit(s1);
        Future<Boolean> rs3 = ser.submit(s2);
        //获取结果
        Boolean rt1 = rs1.get();
        Boolean rt2 = rs2.get();
        Boolean rt3 = rs3.get();
        //关闭服务
        ser.shutdown();
    }
}
以上只是简单的应用,将在下面的文章中介绍具体用法。
静态代理
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处:代理对象可以做很多真实对象做不了的事情;真实对象专注于做自己的事情
interface Marry{
    void HappyMarry();
}
//真实角色
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("its very happy.");
    }
}
//代理角色
class WeddingCompany implements Marry{
    private Marry target;
    public WeddingCompany(Marry target){
        this.target = target;
    }
    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();//真实对象结婚
        after();
    }
    private void before(){
        System.out.println("布置");
    }
    private void after(){
        System.out.println("收款");
    }
}
以结婚的过程举例静态代理,其实Thread就是一个代理角色,真实角色则为Runnable
Lambda表达式
为了避免内部类定义过多
(params)-> expression(表达式); (params) -> statement(语句); (params) -> {statements}
函数式接口的定义:
- 任何接口,如果只包含唯一一个抽象方法,那么他就是函数式接口
- 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
like = ()->{
    System.out.println("I like lambda2");
};
like.lambda();
like为接口定义的一个对象,此接口中有一个lambda方法。
interface Ilike{
    void lambda(int a);
}
三种简化写法:
like = (a)->{
    System.out.println("I like lambda"+a);
};
like = a -> {
    System.out.println("I like lambda"+a);
};
like = a -> System.out.println("I like lambda"+a);
like.lambda(520);
Lambda表达式前提是接口为函数式接口;多个参数可以同时去掉参数类型,需要保持同步,需要加小括号;
简化过程其实可以理解为:静态内部类(类中定义static class,重写方法)->局部内部类(类中定义class,重写方法)->匿名内部类(直接new 接口,重写方法,不写内部类名)->Lambda表达式
线程状态
- 创建状态
- 就绪状态
start()之后线程进入就绪状态,但不意味着立即调度执行
- 阻塞状态
- 运行状态
- 死亡状态
线程中断或者结束,一旦进入死亡状态,就不能再次启动

Thread常用方法:
| 方法 | 作用 | 
|---|---|
| setPriority(int newPriority) | 更改此线程的优先级。 | 
| static void sleep(long millis) | 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 | 
| void join() | 等待这个线程死亡。 | 
| static void yield() | 对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。 | 
| void interrupt() | 中断这个线程。 | 
| boolean isAlive() | 测试这个线程是否活着。 | 
- 一般来讲线程最好正常停止(利用循环次数,不建议死循环) 
- 建议设置一个标志位 

State枚举类型(具有约束,例如定义四季枚举类型:春夏秋冬)
Thread.State state = thread.getState();
线程休眠_sleep
- sleep指定当前线程阻塞的毫秒数
- sleep存在异常- InterruptedException
- sleep时间达到后,线程进入就绪状态
- 可以模拟网络延时,倒计时等
- 每个对象都有一个锁,sleep不会释放锁
- sleep()方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指- this.currentThread()返回的线程。
try {
    Thread.sleep(200);
} catch (InterruptedException e) {
    e.printStackTrace();
}
需要抛出异常
模拟网络延时:可以放大问题的发生性(例如上方“模拟抢票”中,设置一定休眠时间,会发现多个线程抢占同一公共资源)
模拟倒计时:
public static void main(String[] args) throws InterruptedException {
    //Date startTime = new Date(System.currentTimeMillis()); //获取系统当前时间
    while(true){
        Thread.sleep(1000);
        Date startTime = new Date(System.currentTimeMillis());
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
    }
}
每隔1s输出一下当前时间(Date类的使用)。
线程礼让_yield
- 让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功
@Override
public void run() {
    System.out.println(Thread.currentThread().getName()+"线程开始执行");
    Thread.yield();
    System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
执行两个线程,结果如下:

0执行,礼让后给1。该结果可能不成功。yield执行后,会让CPU重新调度,重新调度的结果可能是新的也可能是旧的。
线程强制执行_join
合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞(本质插队)
public class testJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("special Thread comes."+i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        testJoin t = new testJoin();
        Thread t1 = new Thread(t);
        t1.start();
        for (int i = 0; i < 500; i++) {
            if(i==200){
                t1.join();
            }
            System.out.println("main"+i);
        }
    }
}
结果为,在循环的500中,前200遍历主线程和t1在并行,而在200时,只剩下t1执行,执行完毕后才把资源归还主线程。
后续将在下一节中介绍线程优先级,锁,同步,生产者消费者,线程池,管程等相关问题。
简述Java多线程(一)的更多相关文章
- 简述Java多线程(二)
		Java多线程(二) 线程优先级 Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行. 优先级高的不一定先执行,大多数情况是这样的. 优 ... 
- Java 多线程并发编程一览笔录
		Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ... 
- Java多线程原理+基础知识(超级超级详细)+(并发与并行)+(进程与线程)1
		Java多线程 我们先来了解两个概念!!!! 1.什么是并发与并行 2.什么是进程与线程 1.什么是并发与并行 1.1并行:两个事情在同一时刻发生 1.2并发:两个事情在同一时间段内发生 并发与并行的 ... 
- Java多线程:Linux多路复用,Java NIO与Netty简述
		JVM的多路复用器实现原理 Linux 2.5以前:select/poll Linux 2.6以后: epoll Windows: IOCP Free BSD, OS X: kqueue 下面仅讲解L ... 
- Java多线程小结
		简述 Java是支持多线程编程的语言,线程相比于进程更加轻量级,线程共享相同的内存空间,但是拥有独立的栈.减少了进程建立.销毁的资源消耗.jdk1.5后对java的多线程编程提供了更完善的支持,使得j ... 
- Java多线程详解
		Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ... 
- JAVA多线程学习笔记(1)
		JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ... 
- Java多线程面试题整理
		部分一:多线程部分: 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速. ... 
- Java多线程:AQS
		在Java多线程:线程间通信之Lock中我们提到了ReentrantLock是API级别的实现,但是没有说明其具体实现原理.实际上,ReentrantLock的底层实现使用了AQS(AbstractQ ... 
随机推荐
- 加州金融专访NGK,就NGK DeFi+展开讨论
			近日,加利福尼亚金融日报联合数家知名媒体就DeFi+行业专访了NGK团队代表特德惠斯基. 首先,加利福尼亚金融日报专栏记者迈尔斯表示,目前区块链领域,去中心化金融(DeFi+)的概念是目前市场上面最火 ... 
- [转]ROS中使用message_filters进行多传感器消息同步
			转:http://www.rosclub.cn/post-1030.html 最近实验室老师在做一个多传感器数据采集实验,涉及到了消息同步.所以就学习了ROS官网下的消息同步工具message_fil ... 
- 配置伪分布模式下的hadoop以及采用fuse-dfs来访问HDFS
			实验目标 配置环境的主要目的是得到HDFS的客户端fuse-dfs的IO性能.本来的服务器上没有任何环境,因此安装均是从无到有的.系统是Ubuntu server 14.04 amd64.整个过程参考 ... 
- 处理XML数据应用实践
			摘要:GaussDB(DWS)支持XML数据类型及丰富的XML解析函数,可实现关系数据和XML数据的映射管理功能. XML概述 XML是可扩展的标识语言(eXtensible Markup Langu ... 
- vue子组件的样式没有加scoped属性会影响父组件的样式
			scoped是一个vue的指令,用来控制组件的样式生效区域,加上scoped,样式只在当前组件内生效,不加scoped,这个节点下的样式会全局生效. 需要注意的是:一个组件的样式肯定是用来美化自己组件 ... 
- C#实现JWT无状态验证的实战应用
			前言 本文主要介绍JWT的实战运用. 准备工作 首先我们创建一个Asp.Net的,包含MVC和WebApi的Web项目. 然后使用Nuget搜索JWT,安装JWT类库,如下图. 设计思路 这里我们简单 ... 
- (三)MySQL锁机制 + 事务
			转: (三)MySQL锁机制 + 事务 表锁(偏读) 偏向MyISAM存储引擎.开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低. 查看当前数据库中表的上锁情况,0表示未上锁. sh ... 
- 最新版大数据平台安装部署指南,HDP-2.6.5.0,ambari-2.6.2.0
			一.服务器环境配置 1 系统要求 名称 地址 操作系统 root密码 Master1 10.1.0.30 Centos 7.7 Root@bidsum1 Master2 10.1.0.105 Cent ... 
- 剑指 Offer 58 - I. 翻转单词顺序 + 双指针
			剑指 Offer 58 - I. 翻转单词顺序 Offer_58_1 题目描述 方法一:使用Split函数 package com.walegarrett.offer; /** * @Author W ... 
- LeetCode-133克隆图(图的遍历+深拷贝概念)
			克隆图 LeetCode-133 使用一个map来存储已经遍历的结点,这个存起来的结点必须是新new的才符合题意 /* // Definition for a Node. class Node { p ... 
