java多线程系列(一)
java多线程技能
前言:本系列将从零开始讲解java多线程相关的技术,内容参考于《java多线程核心技术》与《java并发编程实战》等相关资料,希望站在巨人的肩膀上,再通过我的理解能让知识更加简单易懂。
目录
- 认识cpu、核心与线程
- java多线程系列(一)之java多线程技能
- java多线程系列(二)之对象变量的并发访问
- java多线程系列(三)之等待通知机制
- java多线程系列(四)之ReentrantLock的使用
- java多线程系列(五)之synchronized ReentrantLock volatile Atomic 原理分析
- java多线程系列(六)之线程池原理及其使用
并发历史
- 在没有操作系统的时候,一台计算机只执行一个程序,在那个时候,对珍贵的计算机资源来说是一种浪费
- 为了提高资源利用率(比如在等待输入的时候,可以执行其他程序),为了提高公平性(不同用户和程序对计算机上的资源有平等的使用权),为了提高便利性(实现多个任务的时候,可以通过多个程序,而不用一个程序实现多个任务)计算机加入了操作系统
- 同样,相同的原因,线程诞生了。线程可以共享进程的资源。
线程优势
发挥多处理器的强大功能
- 随着技术的发展,多处理器系统越来越普及。在一个双处理器系统上,如果只用一个线程,那么无疑浪费了资源。
线程状态
- 新建(New):创建后尚未启动的线程
- 运行(Runanle):包括了操作系统线程中的Running和Ready,处于此状态的线程可能正在执行或者等待cpu分配时间片
- 无限期等待(Waiting):等待被其他线程显式唤醒,执行wait或者join方法或者LockSupport的park方法
- 限期等待(Timed Waiting):一定时间后会由系统自动唤醒
- 阻塞(Blocked):等待获取到一个排它锁
- 结束(Terminated):线程执行结束
多线程编程的两种方式
- 继承Thread
- 实现Runnable接口
通过继承Thread
代码的执行顺序与代码的顺序无关
public class T1 {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
System.out.println("代码的执行结果与代码的顺序无关");
}
}
class MyThread extends Thread
{
public void run()
{
System.out.println("创建的线程");
}
}
如果直接执行run方法是同步(主线程调用),start方法是让系统来找一个时间来调用run方法(子线程调用),
public class T1 {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.run();
System.out.println("如果是直接执行run方法,肯定是按代码顺序执行的,因为是通过主线程调用的");
}
}
class MyThread extends Thread
{
public void run()
{
System.out.println("创建的线程");
}
}
通过实现Runnable接口
比继承Thread的方式更有优势
- java不能多继承,所以如果线程类已经有一个父类,那么无法再继承Thread类
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("运行中!");
}
}
public class Run {
public static void main(String[] args) {
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
System.out.println("运行结束!");
}
}
线程数据非共享
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();
}
class MyThread extends Thread {
private int count = 5;
public MyThread(String name) {
super();
this.setName(name);
}
@Override
public void run() {
super.run();
while (count > 0) {
count--;
System.out.println("由 " + this.currentThread().getName()
+ " 计算,count=" + count);
}
}
}
- 这里的i并不共享,每一个线程维护自己的i变量
线程数据共享
public static void main(String[] args) {
MyThread mythread=new MyThread();
//线程a b c启动的时候,执行的是myThread的方法,此时数据共享
Thread a=new Thread(mythread,"A");
Thread b=new Thread(mythread,"B");
Thread c=new Thread(mythread,"C");
a.start();
b.start();
c.start();
}
- 由于i++不是原子操作(先获取i的值,让后再加一,再把结果赋给i),所以输出的值会有重复的情况,比如4 4 2
synchronized关键字让i++同步执行
public synchronized void run() {
super.run();
count--;
System.out.println("由 "+this.currentThread().getName()+" 计算,count="+count);//输出的一定是4 3 2
}
- synchronized 关键字,给方法加上锁,多个线程尝试拿到锁,拿到锁的线程执行方法,拿不到的不断的尝试拿到锁
Thread方法
- currentThread(),获得当前线程
- isLive() 线程是否活跃状态(启动还未运行或者启动了正在运行即为活跃状态)
- sleep()方法,让线程休眠
- getId()方法 获得该线程的唯一标识
- suspeend()方法,让线程暂停(已报废)
- ressume()方法,让线程恢复(已报废)
- stop()方法,让线程终止(已报废)
停止线程的方法
- 线程自己执行完后自动终止
- stop强制终止,不安全
- 使用interrupt方法
interrupt方法
- 线程对象有一个boolean变量代表是否有中断请求,interrupt方法将线程的中断状态设置会true,但是并没有立刻终止线程,就像告诉你儿子要好好学习一样,但是你儿子怎么样关键看的是你儿子。
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
- 上面的代码虽然执行了interrupt方法,但是并没有中断run方法里面的程序,并且run方法全部执行完,也就是一直执行到500000
判断线程是否中断
- interrupted方法判断当前线程是否中断,清除中断标志
- isInterrupt 方法判断线程是否中断,不清除中断标志
interrupted方法
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.interrupt();
//Thread.currentThread().interrupt();
System.out.println("是否停止1?="+thread.interrupted());//false
System.out.println("是否停止2?="+thread.interrupted());//false main线程没有被中断!!!
//......
- 这里thread线程执行了interrupt方法,按到里thread的中断状态应该为true,但是因为interrupted方法判断的是当前线程的中断状态,也就是main线程(main线程执行thread.interrupted方法),所以他的中断状态是false
public class Run {
public static void main(String[] args) {
try {
Thread.currentThread().interrupt();
System.out.println("是否停止1?="+Thread.interrupted());//true
System.out.println("是否停止2?="+Thread.interrupted());//false
//......
- 主线程执行interrupt方法,第一次执行interrupted方法的时候,中断状态为true,但是interrupted方法有清除中断标志的作用,所以再执行的时候输出的是false
isInterrupt方法
public static void main(String[] args) {
MyThread thread=new MyThread();
thread.start();
thread.interrupt();
System.out.println(thread.isInterrupted());//true
System.out.println(thread.isInterrupted());//true
}
- 这里也有判断线程中断的作用,而判断的是他的调用者的中断状态,而且没有清除中断标志的作用,所以两次都是true
停止线程
- 在上面的代码中,我们虽然执行了interrupt方法,但是并没有中断进程,那么我们如果来中断呢?我们可以在run方法中进行判断,判断中断状态,状态为true,那么就停止run方法。
import exthread.MyThread;
import exthread.MyThread;
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("666");
}
}
- 还有一个问题,我们要中断进程,通过上面的的操作我们终止了for循环,但是后面的输出666仍然执行,这并不是我们想要的中断。于是我们可以顺便抛出异常,然后直接捕获,这样的话后面的代码就不执行了。
异常法停止线程
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("我在for下面");
} catch (InterruptedException e) {
System.out.println("进MyThread.java类run方法中的catch了!");
e.printStackTrace();
}
}
}
- 当然我们也可以直接return,但是抛出异常比较好,因为后面可以继续将异常抛出,让线程中断事件得到传播
return 停止线程
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
return;
}
System.out.println("i=" + (i + 1));
}
sleep与interrupt
- 中断状态,进入sleep抛出异常
- 睡眠进入中断状态,抛出异常
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止!进入catch!"+this.isInterrupted());
e.printStackTrace();
}
}
}*/
暂停线程
- suspend (作废)会让同步方法直接锁住
public static void main(String[] args) {
try {
final SynchronizedObject object = new SynchronizedObject();
Thread thread1 = new Thread() {
@Override
public void run() {
object.printString();
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread() {
@Override
public void run() {
System.out
.println("thread2启动了,但进入不了printString()方法!只打印1个begin");
System.out
.println("因为printString()方法被a线程锁定并且永远的suspend暂停了!");
object.printString();
}
};
thread2.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class SynchronizedObject {
synchronized public void printString() {
System.out.println("begin");
if (Thread.currentThread().getName().equals("a")) {
System.out.println("a线程永远 suspend了!");
Thread.currentThread().suspend();
}
System.out.println("end");
}
}
- 当thread1执行了suspend方法后,printString方法直接就被锁住了,也就是thread1把这个锁占住了,但是却不工作
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
- 用System.out.println() 方法替换pringString方法同样也会锁住,因为这个方法也加锁了,当thread1暂停之后,同样占住了这个锁
yield方法
- 放弃当前cpu资源,让其他任务去占用,但是什么时候放弃不知道,因为放弃后,可能又开始获得时间片
线程优先级
- cpu先执行优先级高的线程的对象任务, setPriority方法可以设置线程的优先级
- 线程优先级具有继承性,也就是说A线程启动B线程,二者优先级一样,同样main主线程启动线程A,main和A的优先级也是一样的
public static void main(String[] args) {
System.out.println("main thread begin priority="
+ Thread.currentThread().getPriority());
Thread.currentThread().setPriority(6);
System.out.println("main thread end priority="
+ Thread.currentThread().getPriority());
MyThread1 thread1 = new MyThread1();
thread1.start();
}
class MyThread1 extends Thread {
@Override
public void run() {
System.out.println("MyThread1 run priority=" + this.getPriority());
MyThread2 thread2 = new MyThread2();
thread2.start();
}
}
优先级特性
- 规则性,cpu尽量将资源给优先级高的
- 随机性,优先级较高的不一定先执行完run方法
守护线程
- 线程有两种一种是用户线程,一种是守护线程
- 垃圾回收线程是典型的守护线程,当jvm中还有非守护线程,守护线程就一直还在,知道非守护线程不存在了,守护线程才销毁
总结
- 线程提高了资源利用率
- 线程的实现可以通过继承Thread类,也可以通过实Runnable接口
- 线程中断方式有3种,常用的是interrupt方法,该方法并没有立即中断线程,只是做了一个中断标志
- interrupted和isInterrupt两种方法都可以查看线程中断状态,第一种查看的是当前线程的中断状态,第二种查看的该方法调用者的中断状态
- interrupted方法会清除中断状态,isInterrupt不会清除中断状态
- interrupt方法没有真正中断线程,所以可以在run方法里面判断中断状态,然后通过抛出异常或者return来中断线程
- 当线中断状态为true,再进入sleep会抛出异常,反之一样,sleep状态执行interrupt方法,同样会抛出异常
- 线程暂停容易将锁占住
- 线程具有优先级,可以通过方法设置线程优先级,cpu会将资源尽量给优先级高的线程,但是当优先级差别不大的时候,优先级高的不一定先执行完run方法
- 线程有两种,一种用户线程,一种守护线程,直到用户线程都销毁,守护线程才销毁
我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)
作者:jiajun 出处: http://www.cnblogs.com/-new/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。
java多线程系列(一)的更多相关文章
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- Java多线程系列--“JUC锁”01之 框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...
- Java多线程系列目录(共43篇)
最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...
- Java多线程系列--“JUC锁”06之 Condition条件
概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...
- Java多线程系列--“JUC锁”05之 非公平锁
概要 前面两章分析了"公平锁的获取和释放机制",这一章开始对“非公平锁”的获取锁/释放锁的过程进行分析.内容包括:参考代码获取非公平锁(基于JDK1.7.0_40)释放非公平锁(基 ...
随机推荐
- 【shell脚本实例】shell脚本统计单词频率、出现次数最多的n个单词
1. 统计的对象words.txt,每个单词占一行(从简考虑了~) zjd@ubuntu:~/test$ cat word.txt used this count mysql count this u ...
- 小小白的python之路------python基础01
1. 不说python是啥了,百度一堆.,还是说说我学了啥 我说的是python3.5,其他的自己看着办 这个是下载链接啊,自己玩 https://www.python.org/ 我下载完成,使用py ...
- Java内存数据模型
本篇文章带来的是对Java内存数据模型的介绍,这对于我们深入理解Jvm虚拟机工作的原理和Java内存的划分大有裨益,好了,为了让我们理解的更为深刻,我们将会加入图片辅助的方法去理解. 本篇博文的目录: ...
- Hadoop化繁为简(二)—层层递进轻松入门hdfs
层层递进-解开hdfs的面纱 1.hdfs是什么?它与普通服务器的文件系统有什么区别?它的特性有什么? 2.hdfs的工作原理是怎样的? 3.每台机器都单独启动有什么弊端?假设有1000台机器需要启动 ...
- Java常用类之【八种基本数据类型】
一.装箱和拆箱 装箱:将基本数据类型包装为对应的包装类对象 拆箱:将包装类对象转换成对应的基本数据类型 JDK5.0中为基本数据类型提供了自动装箱(boxing).拆箱(unboxing)功能 二.八 ...
- JavaScript设计模式_03_代理模式
代理模式是非常常见的模式,比如我们使用的VPN工具,明星的经纪人,都是代理模式的例子.但是,有人会疑问,明明可以直接访问对象,为什么中间还要加一个壳呢?这也就说到了代理模式的好处.在我看来,代理模式最 ...
- 按钮开关demo
源码如下: <style> .container{ margin: 0px;padding: 100px 250px;} .switch-test{ -webkit-appearance: ...
- 浅谈Android studio中OKHttp安装及简单使用
Google貌似在6.0版本里面删除了HttpClient相关API,鉴于okhttp的口碑相当好,介绍一下OKHttp的安装及使用: 一.安装 对于Android Studio的用户,在Projec ...
- C语言和go语言之间的交互
一.go代码中使用C代码 go代码中使用C代码,在go语言的函数块中,以注释的方式写入C代码,然后紧跟import "C" 即可在go代码中使用C函数 代码示例: go代码:tes ...
- eclipse汉化教程,新手神器
网盘下载地址:http://pan.baidu.com/s/1i5ed6ZF 下载汉化包 将汉化包里的两个文件存放到eclipse安装目录中的dropins文件夹中 重启eclipse 汉化成功