前言

本文章部分笔记来自自高诗岩的《java多线程编程核心技术》书。

进程

进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动,是程序在一个数据及上运行的过程,它是系统进行资源分配和调度的一个独立单位。

线程

线程可以理解为在进程中独立运行的子任务,例如,QQ.exe运行时,很多的子任务也在同时运行,如好友视频线程、下载文件线程、传输数据线程、发送表情线程等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,传文件、听音乐、发送图片表情等这些功能都有对应的线程在后台默默地运行。

使用线程

继承Thread

public class MyTread extends Thread{
@Override
public void run(){
super.run();
System.out.println("MYThread");
}
}
public class Main {
public static void main(String[] args) {
MyTread mythreads = new MyTread();
mythreads.start();
System.out.println("结束");
}
}

线程随机性

线程的随机输出是因为CPU将时间片分给不同的线程,线程获得时间片后就执行任务,所以这些线程在交替地执行并输出导致输出结果呈现乱序的现象。

.start()的顺序不代表.run()的顺序

线程.start()的顺序并不代表.run()的顺序,和我们平常代码从上到下顺序执行不太一样。

实现Runnable

我们都知道java支持单继承,使用继承Thread类来开发多线程应用程序在设计上是有局限性的,所以在有些时候,我们可以继承一个类并用implement Runnable接口。

实例共享造成的非线程安全问题

当不共享时,多个线程都是访问各自的实例变量,对各自的变量进行操作, 不让多个线程访问同一个实例变量。
当共享数据时,即多个线程可以访问同一个实例变量。

public class Main {

    public static void main(String[] args) {
MyTread mythreads = new MyTread();
Thread a = new Thread(mythreads,"A");
Thread b = new Thread(mythreads,"B");
Thread c = new Thread(mythreads,"C");
Thread d = new Thread(mythreads,"D");
Thread e = new Thread(mythreads,"E");
a.start();
b.start();
c.start();
d.start();
e.start(); }
}
public class MyTread extends Thread{
private int count =5;
@Override
public void run(){
super.run();
count--;
System.out.println("由"+ currentThread().getName()+"计算,count"+count);
}
}


我们预期希望时可以递减的,结果不应该重复,但是很明显,这样已经出现了所谓的非线程安全问题。

典型场景:5个销售员卖产品,在售出一个产品后,应该在剩余物品上进行减1操作,这个时候就需要对多个线程进行同步的操作。

我们可以在run方法前面加上一个synchronized关键字,这样线程在对这个订单数量减少时就需要排队进行了

线程常用方法:

currentThread() 方法,返回代码段正在被哪个线程调用。

isAlive() 方法,判断当前的线程是否存活。

sleep() 方法,在指定的时间内让当前"正在执行的线程"休眠(暂停执行)

getId()获取线程的唯一标识

判断线程是否为停止状态

1)public static boolean interrupted():测试currentThread()是否已经中断。

2)public boolean this.isInterrupted():测试this关键字所在类的对象是否已经中断。

interrupted():测试当前线程是否已经中断,线程的中断状态由该方法清除。

两个方法的区别:

1)this.interrupted():测试当前线程是否已经是中断状态,执行后具有清除状态标志值为false的功能。

2)this.isInterrupted():测试线程Thread对象是否已经是中断状态,不清除状态标志。

用stop()方法停止线程,即暴力停止线程

利用

stop()方法已经是作废的方法,因为如果暴力性地强制让线程停止,一些请理性工作得不到完成,或者数据添加不完整。

利用stop()释放锁给数据造成不一致的结果

暂停线程suspend()和resume()

使用suspend()方法可以暂停线程,使用resume()方法来恢复线程的执行。

两者的缺点:独占

当使用方法不当,极易造成公共同步对象被独占,其他线程就无法访问公共同步对象的结果。

当一个线程对一个对象的操作一直都没有结束,其他线程就不能访问这个对象,假如这个线程操作这个对象永远进入suspend()状态,其他线程就不能再访问这个对象了。

缺点二:数据不完整

在使用两个方法时容易出现线程暂停,进而导致数据不完整的情况。

yield方法

这个方法是放弃当前的CPU资源,让其他任务去占用CPU执行时间,放弃时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

线程的优先级

线程可以划分优先级,优先级较高的线程得到CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务,其实就是让高优先级的线程获得更多的CPU时间片。

通过setPriority()方法设置线程的优先级

在java中,线程的优先级一般分为1~10共10个等级。

线程优先级的继承特性

在Java中,线程的优先级具有继承性

A启动B,A和B的优先级是一样的

优先级的规律性

setPriority()方法设置线程的优先级

一般情况下,高优先级的线程总是大部分先执行完,但是不代表高优先级的线程全部先执行完。

优先级的随机性

优先级较高的线程不一定每一次都先执行完。即:优先级高的线程并不一定每一次都先执行完run()中的任务,线程优先级与输出顺序无关。

守护线程

Java中有两种线程:一种是用户线程,也称非守护线程;另一种是守护线程。

当线程中不存在非守护线程,守护线程自动销毁。典型的守护线程就是垃圾回收机制。当最后的一个用户线程销毁了,守护线程退出,进程随即结束了。

public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.setDaemon(true);
thread.start();
Thread.sleep(10000);
System.out.println("我离开thread对象也不再打印了,也就是停止了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MyThread extends Thread {
private int i = 0; @Override
public void run() {
try {
while (true) {
i++;
System.out.println("i=" + (i));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} }

传入setDaemon(true)的线程即为守护线程,所以这里面我们可以看出main是属于用户线程,怎么样可以快速判断出来呢,比如我们将这一行代码注释掉,那么这里在main线程结束后,但是MyThread线程还没有结束,即用户线程还么有结束,这时守护线程就不会终止,所以注释掉后,运行起来这个线程就不会终止了。

对象及变量的并发访问

非线程安全问题会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是读取到的数据其实是被更改过的。而线程安全是指获得实例变量的值是经过同步处理的,不会出现脏读的现象。

方法内的变量为线程安全

非线程安全问题存在于实例变量中,对于方法内的私有变量,不存在非线程安全问题

解决方案:

当两个线程同时访问同一个对象的同步方法一定是线程安全的。当线程进入synchronized声明的方法时就会上锁,得等这个方法执行完成后,下一个线程才会进入synchronized声明的方法中。

synchronized方法

调用用关键字synchronized声明的方法一定是排队进行运行的。另外,需要牢牢记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么就没有同步的必要。

当一线程调用一个对象的synchronized类型方法时,其他线程可以调用该对象的的非synchronized方法。

当在一个方法添加synchronized并不是锁方法,而是锁当前的类的对象。在Java中,“锁”就是对象,“对象”可以映射成“锁”,哪个线程拿到这把锁,哪个线程就可以执行这个对象的synchronized同步方法。

synchronized拥有重入锁的功能,当一个线程得到一个对象的锁后,再次请求此对象锁时可

以得到该对象锁的,意思就是当我们在一个synchronized方法内调用其他方法块时,是可以得到锁的。

“可重入锁”是指自己还可以获取自己的内部锁。

synchronized方法和synchronized(this)代码块都是锁定当前对象

总结

简单的一个学习笔记,记录一下。

java线程的创建的更多相关文章

  1. Java线程:创建与启动

    Java线程:创建与启动 一.定义线程   1.扩展java.lang.Thread类.   此类中有个run()方法,应该注意其用法: public void run() 如果该线程是使用独立的 R ...

  2. 漫谈并发编程(二):java线程的创建与基本控制

    java线程的创建 定义任务           在java中使用任务这个名词来表示一个线程控制流的代码段,用Runnable接口来标记一个任务,该接口的run方法为线程运行的代码段. public ...

  3. JAVA - 线程从创建到死亡的几种状态都有哪些?

    JAVA - 线程从创建到死亡的几种状态都有哪些? 新建( new ):新创建了一个线程对象. 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 sta ...

  4. Java线程之创建线程

    翻译自:https://www.journaldev.com/1016/java-thread-example 进程 进程是一个自包含的执行环境,它可以被看成一个程序或应用程序.然而一个应用程序本身包 ...

  5. 【JAVA并发第二篇】Java线程的创建与运行,线程状态与常用方法

    1.线程的创建与运行 (1).继承或直接使用Thread类 继承Thread类创建线程: /** * 主类 */ public class ThreadTest { public static voi ...

  6. Java线程的创建及启动

    1.继承Thread类,重写该类的run()方法. package samTest; import java.util.Scanner; /** * Created by Sam on 2018-01 ...

  7. Java 线程的创建和启动

    Java 使用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例.每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码). Java 使用线程执 ...

  8. Java线程的创建方式三:Callable(四)

    一.Java实现多线程的三种方式 方式一:继承Thread类: public class Test extends Thread { public static void main(String[] ...

  9. java线程之创建线程类

    1.extends Thread方法 class Person extends Thread { int sum1 = 50; // 含参构造器 public Person(String name) ...

  10. ThreadPoolExecutor – Java Thread Pool Example(java线程池创建和使用)

    Java thread pool manages the pool of worker threads, it contains a queue that keeps tasks waiting to ...

随机推荐

  1. 在.NET中使用JWT

    1.配置文件添加 //jwt配置文件 "JWT": { "SigningKey": "14fa5f2rrwsg627fs256fdgff2r5rf52 ...

  2. PyTorch中的矩阵乘法

    1. 二维矩阵乘法   , 其中 , , 输出 的维度是.该函数一般只用来计算两个二维矩阵的矩阵乘法,而且不支持broadcast操作. 2. 三维带Batch矩阵乘法  由于神经网络训练一般采用mi ...

  3. js屏蔽开发者工具

    一.屏蔽浏览器右键菜单审查元素 document.oncontextmenu = function () { return false; }; 二.屏蔽F12以及ctrl+shift+i 打开调试工具 ...

  4. jvm垃圾收集器汇总

    1.吞吐量和延时 吞吐量:吞吐量指的是cpu的利用时间,计算公式是 运行用户代码时间  / (用户代码时间 + 垃圾收集时间),吞吐量越大说明cpu的利用率越大. 延时:延时指的是停顿时间,用户代码不 ...

  5. vs2010 Windows程序打包成安装包方法

    1.  在vs2010 选择"新建项目"--"其他项目类型"--"Visual Studio Installerà"安装项目": ...

  6. webgl 系列 —— 变换矩阵和动画

    其他章节请看: webgl 系列 变换矩阵和动画 动画就是不停地将某个东西变换(transform).例如将三角形不停地旋转就是一个动画 和 CSS transform 类似,变换有三种形式:平移.缩 ...

  7. 初认Spring

    官网地址:https://spring.io/ Spring Framework的系统架构 1.Core Contiainer:核心容器 2.AOP:面向切片编程 3.Aspects:AOP思想实现 ...

  8. 实践Pytorch中的模型剪枝方法

    摘要:所谓模型剪枝,其实是一种从神经网络中移除"不必要"权重或偏差的模型压缩技术. 本文分享自华为云社区<模型压缩-pytorch 中的模型剪枝方法实践>,作者:嵌入式 ...

  9. 如何使用Mutex确保并发程序的正确性

    1. 简介 本文的主要内容是介绍Go中Mutex并发原语.包含Mutex的基本使用,使用的注意事项以及一些实践建议. 2. 基本使用 2.1 基本定义 Mutex是Go语言中的一种同步原语,全称为Mu ...

  10. mixins使用混入引入组件,并可以使用公共函数。组件类同名函数可以替代公共函数。使用$ref获得子元素数据和元素dom节点。使用$parents获得父元素数据。slot插槽的使用

    父组件: <template> <div class="box"> <Header > <div slot="left" ...