juc07 创建线程
创建线程JAVA中线程被封装成Thread对象。JDK API中有对Thread的说明,连创建方式都有。自定义线程有两种方式:
继承Thread
实现Runable接口
从打印结果是否是不同线程运行来验证多线程执行。主线程代码在main方法中,自定义线程方法代码在run方法中。
两种创建方式的区别:
Thread 的代码是存在子类当中;
Runable方式的代码是的实现接口的子类当中,还避免了单继承的问题。
一、继承 Thread 方式这种方式最简单,需要三个步骤:
继承 Thread 类
重写 run 方法,run方法要看成一个入口点。
调用 start 方法来执行自定义线程
实现代码:
123456789101112131415161718192021public class TestThread extends Thread { //1.继承Thread @Override public void run() { //2.重写run方法 //super.run(); for (int i = 0; i < 100; i ...
juc06-BLOCKED状态
前言java线程模型中有5种线程状态。五种状态之间可以进行转换。
强调一下 BLOCKED 状态跟 I/O 的阻塞是不同的,它不是一般意义上的阻塞,而是特指被 synchronized 块阻塞,即是跟线程同步有关的一个状态。
BLOCKED(阻塞)简单定义为:
A thread that is blocked waiting for a monitor lock is in this state.一个正在阻塞等待一个监视器锁的线程处于这一状态。
两种阻塞的情况:
第一种:
A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method.一个处于 blocked 状态的线程正在等待一个监视器锁以进入一个同步的块或方法。
第二种:
A thread in the blocked state is waiting for a monitor lock to reenter a synchronized block/method after calling ...
并发线程和进程的区别
在开销方面每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
所处环境在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
内存分配方面系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
包含关系没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
进程示例12345678910111213141516import java.io.IOException;public class ProcessDemo { //在Java中如何开启一个进程:运行记事本程序 public static void mai ...
juc05--线程通信
目的:就是让线程间具有互相发送信号通信的能力。
概述核心:利用共享对象实现通信,这里的通信不是指传值,而是发送信号。目的:就是让线程间具有互相发送信号通信的能力。而且,线程通信可以实现,一个线程可以等待来自其他线程的信号。举个例子,一个线程B可能正在等待来自线程A的信号,这个信号告诉线程B数据已经处理好了。
线程通信开发中不免会遇到,需要所有子线程执行完毕通知主线程处理某些逻辑的场景。或者是 线程A 在执行到某个条件通知 线程B 执行某个操作。在java中,比较典型的就是:等待通知机制。
等待通知机制等待通知模式是 Java 中比较经典的线程通信方式。两个线程通过对同一对象调用等待 wait() 和通知 notify() 方法来进行通讯。这种方式,有三个参与者:
阻塞线程 wait()
唤醒线程 notify()
monitor锁
看个最简单的例子:
12345678910111213141516171819202122232425262728293031323334353637383940414243public class TestWaitNotify4 { ...
juc04-验证线程处于临时状态
临时状态当一个线程被启动时,并不代表线程就有了执行权。线程处于临就绪状态并没有执行权,这个时候 main 线程继续往下执行,有可能是别的线程先开始执行。
代码验证12345678910111213141516public static void main(String[] args) { Test test = new Test(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.start(); test.flag = true; t2.start();}public class Test implements Runnable { boolean flag = false; public void run() { System.out.println( Thread.currentThread().getName() + "--->" ...
JUC03-模拟线程不安全
前言很多时间,我们需要证明线程是不安全的,那就需要复现线程不安全的情况。怎么复现?通过代码构建不安全场景。
由于线程在执行的时候是异步的,当所有线程操作共享数据时,有可以能出现都已经进入判断的情况下,共享数据已被改变,但是其后线程不知道,当线程醒来的时候,直接开始运行,这样就会出现数据不全安的问题。
为什么能构建出来?多条语句操作一个共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导到共享数据的错误。
构建不安全场景通过多个线程,在不加锁的情况下,让多条线程竞争同一个资源。
构建多条线程12345678910DemoRunnable run = new DemoRunnable();Thread t1 = new Thread(run);Thread t2 = new Thread(run);Thread t3 = new Thread(run);Thread t4 = new Thread(run); t1.start();t2.start();t3.start();t4.start();
竞争资源下面的代码执行,预其结果,最后打印:0,而实际 ...
JUC多线程02--什么是上下文切换
上下文切换即,不同线程之间的切换。是存储和恢愎CPU 状态的过程,它使得线程执行能够从中断恢愎执行。上下文切换是需要开销的。
线程切换只在多核 CPU 中才有并不是,线程切换是CPU的功能,单核 CPU 也可以进行上下文切换。CPU 执行线程的粒度是通过给分个线程分配时间切片来实现的。在单核时代,一个系统也会开很多程序,每个程序都会等待CPU来执行并不会等会某一个线程执行完毕。比如单核时代玩 CS,可以边玩游戏边听千千静听。
CPU 通过切换时间分片来执行任务,切换前都会保存上一次任务的状态,这样下次再切回来的时候,可以继续执行当前这个状态。这种保存再切换回来的操作,就是一次上下文切换。
查看上下文切换在linux下,使用vmstat 进行查看:
执行:
1vmstat 10 10
查看一下结果:
123456789101112procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi ...
java-计算概率
简述有一个需求,就是计算一个请求的命中概率,这个命中的概率是作用于单次的请求,而非整体,也就是每一次请求过来都只有20%的命中率。
代码实现123456789101112131415161718192021import java.util.Random;public class ProbabilityDemo { public static void main(String[] args) { // 设置命中概率为20% double hitProbability = 0.2; // 创建随机数生成器 Random random = new Random(); // 生成一个0到1之间的随机数 double randomValue = random.nextDouble(); // 判断随机数是否小于等于命中概率 if (randomValue <= hitProbability) { System.out.pr ...
JUC 多线程01--线程、进程概念
进程正在进行中的程序。每一个进程至少有一个线程。当程序运行时在内存空间中开辟一片独立空间。每一个进程都有一个执行顺序。一个进程更象一个任务。进程的内存原理:
应用程序在执行时都会在内存中开辟一片内存空间并分配地址。进程用于标识这片空间,封装里面的控制单元。
而线程就是进程中的控制单元。线程在控制着进程的执行。
进程和内存每个进程都有自己的一套虚拟内存地址,用来给自己的进程空间编号。进程空间的数据同样以字节为单位,依次增加。从功能上说,虚拟内存地址和物理内存地址类似,都是为数据提供位置索引。进程的虚拟内存地址相互独立。因此,两个进程空间可以有相同的虚拟内存地址,如0x10001000。虚拟内存地址和物理内存地址又有一定的对应关系,对进程某个虚拟内存地址的操作,会被CPU翻译成对某个具体内存地址的操作。
创建进程进程和线程都是由系统来进行创建,JVM通过调用当前系进行开辟进行和线程的操作。
123456789101112131415import java.io.IOException;public class ProcessDemo { //在Java中如何开启一个进 ...
java final 的一些总结
特点final 的含义是最终的、不可改变的。总结了一下 final 的一些规则。
修饰"类"则类不能被继承,所以没有子类,final类中的方法默认是final的。可以提高效率。JDK中很多加final是这个原因的。编译期确认调哪个方法,所以更快。1.5以后这样做没有效果。
修饰"方法"则方法不能被重写,
修饰"成员变量"则变量不以被改变,即被修饰成了常量。只能被赋值一次。
不能修饰构造方法
父类中的private成员方法是不能被子类覆盖的,因为private类型的方法默认是隐式final类型的
final 的引用不能指向新的对象。
形参声明为 final,则方法内不能再改变其:
基础数据类型值不可被修改
传入对象不能再被 new
继承关系时,final 的方法将不会被子类重写。所以父类使用的仍是本类自己的方法。
final 类不可被继承。
C++ 也有final关键字,在C++11及以后的标准中引入了final,final关键字用于修饰类、成员函数和虚函数,表示它们是最终版本,不能被继承或重写。
代码示例
final修 ...