《Java多线程编程核心技术》读后感(一)

1、继承Thread
package First;
public class MyThread extends Thread {
    public void run() {
        super.run();
        System.out.println("mythread");
    }
}
package First;
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("main");
    }
}

在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。
package First;
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        myThread.start();
        System.out.println("main");
    }
}

多次执行start(),会出现java.lang.IllegalThreadStateException异常
package First;
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.run();
        System.out.println("main");
    }
}
start()通知“”线程规划器“”此线程已经准备就绪,等待调用线程对象的run(),具有异步效果。如果直接调用run(),则是同步,从上到下顺序依次执行

执行start()顺序不代表线程启动的顺序。
2、实现runnable接口
package First;
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("运行中");
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        System.out.println("运行结束");
    }
}

Thread构造函数:

构造函数Thread(Runnable target)意味着不光可以传入runnable接口的对象,还可以传入Thread类的对象,这样做完全可以将一个Thread对象的run()方法交给其他线程进行调用
实例变量与线程安全
(1)不共享数据
package First;
public class MyThread extends Thread {
    private int count = 5;
    public MyThread(String name) {
        super();
        //设置线程名称
        this.setName(name);
    }
    public void run() {
        super.run();
        while(count>0) {
            count--;
            System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
        }
    }
}
package First;
public class Test {
    public static void main(String[] args) {
        MyThread a = new MyThread("A");
        MyThread b = new MyThread("B");
        MyThread c = new MyThread("C");
        a.start();
        b.start();
        c.start();
        System.out.println("main");
    }
}

(2)共享数据
package First;
public class MyThread extends Thread {
    private int count = 5;
    public void run() {
        super.run();
          //此实例不要用for语句,因为使用同步后其他线程就得不到运行的机会了
            count--;
            System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
    }
}
package First;
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread a = new Thread(myThread,"A");
        Thread b = new Thread(myThread,"B");
        Thread c = new Thread(myThread,"C");
        Thread d = new Thread(myThread,"D");
        Thread e = new Thread(myThread,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
        System.out.println("main");
    }
}
非线程安全问题(随机):主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程

某些JVM中,i--的操作分成三步:
1)取得原有的i值
2)计算i-1
3)对i进行赋值
在这三个步骤中,如果有多个线程同时访问,那么一定会出现非线程安全问题
package First;
public class MyThread extends Thread {
    private int count = 5;
    synchronized public void run() {
        super.run();
            count--;
            System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
    }
}
程序改成上述,就不会出现问题了

一个非线程安全的例子:
package First;
public class LoginServlet {
    private static String usernameRef;
    private static String passwordRef;
    public static void doPost(String username,String password) {
        try {
            usernameRef = username;
            if(username.equals("a")) {
                Thread.sleep(5000);
            }
            passwordRef = password;
            System.out.println("username="+usernameRef+"       password="+password);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}
package First;
public class ALogin extends Thread{
    public void run() {
        LoginServlet.doPost("a", "aa");
    }
}
package First;
public class BLogin extends Thread{
    public void run() {
        LoginServlet.doPost("b", "bb");
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        ALogin a = new ALogin();
        a.start();
        BLogin b = new BLogin();
        b.start();
    }
}

解决非线程安全问题使用synchronized
package First;
public class LoginServlet {
    private static String usernameRef;
    private static String passwordRef;
    synchronized public static void doPost(String username,String password) {
        try {
            usernameRef = username;
            if(username.equals("a")) {
                Thread.sleep(5000);
            }
            passwordRef = password;
            System.out.println("username="+usernameRef+"       password="+password);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}

留意i--与System.out.println()
println()与i++联合使用时有可能出现另外一种异常情况
package First;
public class MyThread extends Thread {
    private int i = 5;
    public void run() {
        //代码i--由前面项目中单独一行运行改成在当前项目中在println()方法中直接进行打印
        System.out.println("i="+(i--)+"  threadName="+Thread.currentThread().getName());
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        MyThread run = new MyThread();
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        Thread t3 = new Thread(run);
        Thread t4 = new Thread(run);
        Thread t5 = new Thread(run);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}


虽然println()内部是同步的,但i--的操作却是在进入println()之前发生的。所以为了防止非线程安全问题,还是应该继续使用同步方法
currentTread()方法
package First;
public class MyThread extends Thread {
    public MyThread() {
        System.out.println("构造方法的打印: "+Thread.currentThread().getName());
    }
    public void run() {
        System.out.println("run方法打印:"+Thread.currentThread().getName());
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

package First;
public class Run {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //myThread.start();
        myThread.run();
    }
}

由第一个实验可以看出:Mythread的构造函数是被main函数调用的,而run()方法是被名称为Thread-0调用的
由第二个实验可以看出:直接调用run()的话,则调用者名称为main
package First;
public class CountOperate extends Thread {
    public CountOperate() {
        System.out.println("CountOperation---begin");
        System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName());
        System.out.println("this.getName()"+this.getName());
        System.out.println("CountOperation---end");
    }
    public void run() {
        System.out.println("CountOperation---begin");
        System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName());
        System.out.println("this.getName()"+this.getName());
        System.out.println("CountOperation---end");
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        CountOperate countOperate = new CountOperate();
        Thread thread = new Thread(countOperate);
        thread.setName("A");
        thread.start();
    }
}

isLive()
判断当前线程是否处于活跃状态
package First;
public class MyThread extends Thread {
    public void run() {
        System.out.println("run="+this.isAlive());
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        System.out.println("begin="+myThread.isAlive());
        myThread.start();
        System.out.println("end="+myThread.isAlive());
    }
}

注意最后一个println()的值是不确定的
package First;
public class CountOperate extends Thread {
    public CountOperate() {
        System.out.println("CountOperation---begin");
        System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="+Thread.currentThread().isAlive());
        System.out.println("this.getName()"+this.getName());
        System.out.println("this.isAlive()"+this.isAlive());
        System.out.println("CountOperation---end");
    }
    public void run() {
        System.out.println("run---begin");
        System.out.println("Thread.currentThread().getName()="+Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="+Thread.currentThread().isAlive());
        System.out.println("this.getName()"+this.getName());
        System.out.println("this.isAlive()"+this.isAlive());
        System.out.println("run---end");
    }
}
package First;
public class Run {
public static void main(String[] args) {
CountOperate countOperate = new CountOperate();
Thread thread = new Thread(countOperate);
System.out.println("main begin t1 isAlive="+thread.isAlive());
thread.setName("A");
thread.start();
System.out.println("main end t1 isAlive="+thread.isAlive()); }
}

如果将线程对象以构造参数的方式传递给Thread对象进行start()启动时,运行的结果和前面实例的结果有些差异。造成这样的差异的原因还是来自于Thread.currentThread()和this之间的差异(具体解释参考:http://blog.csdn.net/yezis/article/details/57513130)
sleep()方法
是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)这个“正在执行的线程”是指this.currentThread()返回的线程
package First;
public class MyThread extends Thread {
    public void run() {
        try {
            System.out.println("run threadName="+this.currentThread().getName()+" begin");
            Thread.sleep(2000);
            System.out.println("run threadName="+this.currentThread().getName()+" end");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
package First;
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
System.out.println("begin = " +System.currentTimeMillis());
myThread.run();
//myThread.start();
System.out.println("end = " +System.currentTimeMillis()); }
}

package First;
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
System.out.println("begin = " +System.currentTimeMillis());
//myThread.run();
myThread.start();
System.out.println("end = " +System.currentTimeMillis()); }
}

getId()方法
作用是获取线程的唯一标识
package First;
public class Run {
public static void main(String[] args) {
Thread runThrad = Thread.currentThread();
System.out.println(runThrad.getId()); }
}

停止线程
Thread.stop()是不安全的,已经被弃用
大多数使用Thread.interrupt(),但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止

停不了的线程
调用interrupt仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程
package First;
public class MyThread extends Thread {
    public void run() {
        super.run();
        for(int i = 0;i <50000;i++) {
            System.out.println("i"+(i+1));
        }
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        try {
            Thread myThread= new MyThread();
            myThread.start();
            Thread.sleep(2000);
            Thread.interrupted();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
    }
}

说明调用interrupt并没有停止线程
判断线程是否是停止状态
this.interrupted();是static方法,测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程
package First;
public class Run {
    public static void main(String[] args) {
        try {
            Thread myThread= new MyThread();
            myThread.start();
            Thread.sleep(2000);
            Thread.interrupted();
            System.out.println("是否停止1? =  "+Thread.interrupted());
            System.out.println("是否停止1? =  "+Thread.interrupted());
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}

线程并未停止,这也说明了interrupted()方法的解释:测试当前线程是否已经中断。这个“当前线程”是main,它从未中断过,所以打印的是两false
package First;
public class Run {
    public static void main(String[] args) {
        Thread.currentThread().interrupt();
        System.out.println("是否停止1? =  " + Thread.interrupted());
        System.out.println("是否停止1? =  " + Thread.interrupted());
        System.out.println("end");
    }
}

为什么第2个布尔值是false呢?:

this.isInterrupted():方法不是static的
package First;
public class Run {
    public static void main(String[] args) {
        try {
            Thread myThread= new MyThread();
            myThread.start();
            Thread.sleep(1000);
            Thread.interrupted();
            System.out.println("是否停止1? =  "+myThread.isInterrupted());
            System.out.println("是否停止1? =  "+myThread.isInterrupted());
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}
两者之间的区别:

能停止的线程-异常法(这两个实验我都没成功)
package First;
public class MyThread extends Thread {
    public void run() {
        super.run();
        for(int i = 0;i <50000;i++) {
            if(Thread.interrupted()) {
                System.out.println("已经是停止的状态,退出");
                break;
            }
            System.out.println("i"+(i+1));
        }
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        try {
            Thread myThread= new MyThread();
            myThread.start();
            Thread.sleep(2000);
            Thread.interrupted();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}

虽然停止了线程,但如果for语句下面还有语句,还是会继续执行的
package First;
public class MyThread extends Thread {
    public void run() {
        super.run();
        try {
            for(int i = 0;i <50000;i++) {
                if(Thread.interrupted()) {
                    System.out.println("已经是停止的状态,退出");
                    break;
                }
                System.out.println("i"+(i+1));
            }
            System.out.println("我在for下面");
        } catch (Exception e) {
            System.out.println("进入MyThread.java类run方法中的catch");
        }
    }
}
package First;
public class Run {
    public static void main(String[] args) {
        try {
            Thread myThread= new MyThread();
            myThread.start();
            Thread.sleep(2000);
            Thread.interrupted();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}

《Java多线程编程核心技术》读后感(一)的更多相关文章
- Springboot揭秘-快速构建微服务体系-王福强-2016年5月第一次印刷
		JavaConfig项目: spring IOC有一个非常核心的概念——Bean.由Spring容器来负责对Bean的实例化,装配和管理.XML是用来描述Bean最为流行的配置方式.Spring可以从 ... 
- 《SpringBoot揭秘 快速构建微服务体系》读后感(一)
		SpringIOC IOC有两种方式:一种是DI,另一种是DL,即Dependency Lookup(依赖查找).前者是当前软件实体被动接受其依赖的其他组件被IoC容器注入,而后者则是当前软件实体主动 ... 
- 《SpringBoot揭秘 快速构建微服务体系》读后感(五)
		应用日志和spring-boot-starter-logging 快速web应用开发与spring-boot-starter-web 1.项目结构层面的约定 
- 《SpringBoot揭秘 快速构建微服务体系》读后感(三)
		SpringApplication:SpringBoot程序启动的一站式解决方案 深入探索SpringApplication执行流程 因为书上的版本是1.2的,比较老,这里参考http://blog. ... 
- 《SpringBoot揭秘 快速构建微服务体系》读后感(二)
		最简单的springBoot应用 package com.louis.test; import org.springframework.boot.SpringApplication; import o ... 
- 《SpringBoot揭秘 快速构建微服务体系》读后感(四)
		再谈自动配置 基于条件的自动配置 调整自动配置的顺序 
- [高清] SpringBoot揭秘快速构建微服务体系
		------ 郑重声明 --------- 资源来自网络,纯粹共享交流, 如果喜欢,请您务必支持正版!! --------------------------------------------- 下 ... 
- SpringBoot 快速构建微服务体系 知识点总结
		可以通过http://start.spring.io/构建一个SpringBoot的脚手架项目 一.微服务 1.SpringBoot是一个可使用Java构建微服务的微框架. 2.微服务就是要倡导大家尽 ... 
- SpringBoot揭秘:快速构建微服务体系
		chapter 2: 饮水思源:回顾与探索Spring框架本质 IoC其实有两种方式,一种是DI(dependency Injection),一种是DL(dependency Lookup 依赖查找, ... 
- 通过GeneXus如何快速构建微服务架构
		概览 “微服务”是一个非常广泛的话题,在过去几年里,市面上存在着各种不同的定义. 虽然对这种架构方式没有一个非常精确的定义,但仍然有一些概念具有代表性. 微服务有着许多围绕业务能力.自动化部署.终端智 ... 
随机推荐
- 用字符串处理函数中的比较函数strcmp做的一个密码登录验证
			正确返回0 1大返回正数 2大返回负数 1,2表示输入字符串1和字符串2 根据ASCII码大小来判断 代码: #include<stdio.h> #include<string.h ... 
- caffe搭建以及初步学习--win7-vs2013-gtx650tiboost-cuda8.0-cifar10训练和测试-2-完整解决方案cifar10_full_solver.prototxt
			首先总结前一节的内容. 简单的讲,就是训练并测试了快速解决方案. 转换数据格式: convert_cifar_data.exe data/cifar10 examples/cifar10 lmdb 计 ... 
- windows下gVim 中文显示为乱码
			打开vimrc文件,在vim的安装目录下可以找到该文件: 或在windows下是在vim/gvim下输入:edit $vim/_vimrc. 在文件的末尾添加一句 "set fileenco ... 
- ubuntu 安装后的配置
			osx 下用 vmware 安装了一个 ubuntu 虚拟机,版本是 14.04 server.安装完之后要做一系列配置,记录如下. 配置 Android 编译环境 sudo apt-get inst ... 
- 根据百度地图API获取指定地点的经纬度
			做项目时,遇到对地点获取地图中对应的经纬度,作一下笔记,以备以后直接使用 package com.hpzx.data; import java.io.BufferedReader; import ja ... 
- java 多线程2(转载)
			http://www.cnblogs.com/DreamSea/archive/2012/01/11/JavaThread.html Ø线程的概述(Introduction) 线程是一个程序的多个执行 ... 
- xcode升级到6.0以后遇到的警告错误 原帖链接http://www.cocoachina.com/bbs/simple/?t112432.html
			Xcode 升级后,常常遇到的遇到的警告.错误,解决方法 从sdk3.2.5升级到sdk 7.1中间废弃了很多的方法,还有一些逻辑关系更加严谨了.1,警告:“xoxoxoxo” is depreca ... 
- 开源流媒体服务器--EasyDarwin
			欢迎加入我们的开源流媒体服务器项目:EasyDarwin, EasyDarwin是在Apple开源流媒体服务器Darwin Streaming Server(v6.0.3)基础上进行开发和维护的免费开 ... 
- ios上视频与音乐合成后出现播放兼容问题的解决方法
			近期EasyDarwin开源流媒体团队EasyVideoRecorder小组同学Carl在支持一款短视频应用上线时,遇到一个问题:我们在IOS上合成"图片+音乐"成为视频之后,在P ... 
- java基础知识查漏 四
			1.JAVA多线程实现方式 (1)继承Thread类,并重写run()方法 (2)实现Runnable接口,,实现run()方法 (3)使用ExecutorService.Callable.Futur ... 
