Java线程总结
Posted You295
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java线程总结相关的知识,希望对你有一定的参考价值。
Java线程总结(一)
一.线程的生命周期
1)进程与线程的定义和特征
1.进程的定义:进程是程序运行的一个实体的运行过程,是系统进行资源分配和调配的一个独立单位。
2.进程的特征:2.1系统开销:创建撤销切换开销大,资源要重新分配和回收。
2.2拥有资产:资源拥有的基本单位。2.3调度:资源分配的基本单位。2.4安全性:进程间相互独立,互不影响。2.5地址空间:系统赋予的独立的内存地址空间。
3.线程的定义:线程是进程运行和执行的最小的调度单位。
4.线程的特征:4.1系统开销:仅保存少量寄存器的内容,开销小,在进程的地址空间执行代码。4.2:拥有资产:基本不占资源,仅有不可少的资源(程序计数器,一组寄存器和栈)。4.3:调度:独立调度分配的单位。4.4:安全性:线程共享一个进程下面的资源,可以互相通信和影响。4.5:地址空间:由相关堆栈寄存器和线程控制表TCB组成,寄存器可被用来存储线程内的局部变量。
2)线程的生命周期
1.线程的创建(三种方式):线程被new出来
继承Thread类,,实现Runnable接口,,实现Callable接口。
2.线程就绪:线程具有执行的资格,即线程调用了start(),没有执行的权利
Thread t = new Thread();
t.start();//线程就绪,准备启动
3线程运行:线程具有执行的资格和具备执行的权利
@Override
public void run() {
for (int i = 0; i <= 99; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.线程阻塞:没有执行的资格和执行的权利
sieep()方法:他使得线程在指定的时间内进入阻塞状态,不能得到CPU时间,指定的时间一过,线程重新进入可执行状态
/**
* 阻塞---Sleep()
*
* @param args
*/
public static void main(String[] args) {
Thread a1 = new SleepThread();
Thread a2 = new SleepThread();
Thread a3 = new SleepThread();
Thread a4 = new SleepThread();
a1.start();
a2.start();
a3.start();
a4.start();
}
public static class SleepThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 18; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(180); // 休息180ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
yield()方法:使得线程放弃当前分得的CPU时间,但是不使线程阻塞,即线程仍处于可执行状态
/**
* 阻塞---yield
*
* @author DELL
*
*/
public class ThreadYield {
public static void main(String[] args) {
Thread a1 = new YieldThread();
Thread a2 = new YieldThread();
Thread a3 = new YieldThread();
a1.start();
a2.start();
a3.start();
}
public static class YieldThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 18; i++) {
if (i == 9) {
Thread.yield();
}
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
join()方法:调用时当前线程则转入阻塞状态,直到另一个线程运行结束,当前线程再由阻塞状态转为就绪状态
public class ThreadJoin {
public static void main(String[] args) throws Exception {
Thread a1 = new JoinThread();
Thread a2 = new JoinThread();
Thread a3 = new JoinThread();
a1.start();
a1.join(); // 等待a1跑完,再跑其他的
a2.start();
a3.start();
}
public static class JoinThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 18; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
wait()方法:导致当前线程等待,直到其他线程调用此对象的notify()唤醒方法。
5.线程死亡:线程的对象变成垃圾,释放资源
二.创建线程的三种方式
1)继承Thread类
创建如下:
public class ThreadDemo01 extends Thread {
/**
* 线程创建方法1--继承Thread类
*/
@Override
public void run() {
for (int i = 0; i <= 99; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试如下:
public static void main(String[] args) {
Thread t1 = new ThreadDemo01();
Thread t2 = new ThreadDemo01();
Thread t3 = new ThreadDemo01();
Thread t4 = new ThreadDemo01();
Thread t5 = new ThreadDemo01();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
2)实现Runnable接口
创建如下:(也可用lambda表达式创建)
public class ThreadDemo02 implements Runnable {
/**
* Thread创建方法二 继承接口Runable
*/
@Override
public void run() {
for (int i = 0; i <= 99; i++) {
System.out.println(Thread.currentThread().getName() + ">>>" + i);
try {
Thread.sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试如下:
public class TestThreadDemo02 {
public static void main(String[] args) {
Thread t1 = new Thread(new ThreadDemo02());
Thread t2 = new Thread(new ThreadDemo02());
Thread t3 = new Thread(new ThreadDemo02());
Thread t4 = new Thread(new ThreadDemo02());
Thread t5 = new Thread(new ThreadDemo02());
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
3)实现Callable接口
创建如下:
public class ThreadDemo03 implements Callable<Integer> {
/**
* Thread 第三种创建方法--继承与Callable接口----此方法适用于值的计算
*/
@Override
public Integer call() throws Exception {
int t = 0;
for (int i = 0; i < 99; i++) {
t += i;
}
return t;
}
}
测试如下:
public class TestThreadDemo03 {
public static void main(String[] args) throws Exception {
FutureTask<Integer> ft = new FutureTask<Integer>(new ThreadDemo03());
Thread t = new Thread(ft);
t.start();
System.out.println(ft.get());
}
}
//输出:4851
4)Callable与其他两种方式的区别
1.call()方法可以有返回值
2.call()方法可以声明抛出异常
3.通常在做计算时使用
代码展示如下:
@Override
public Integer call() throws Exception { //可以直接抛出异常
int t = 0;
for (int i = 0; i < 99; i++) {
t += i;
}
return t; //返回值
}
三.CAS原理与ABA问题
1.CAS原理:C–compare;A–And; S–Swap;比较并交换
CAS有三个操作数:内存值(V), 预期原值(A), 新值(B);如果内存值与预期原值相同时,处理器自动将该位置的值(V)修改为新值(B),否则,处理器不做任何的操作。
伪代码可以表示为:
do{
备份旧数据;
基于旧数据构造新数据;
}while(!CAS(内存地址,备份的旧数据,新数据))
2.ABA问题:CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了;比如链表的头部在变化了两次后恢复了原值,但是不代表链表就没有变化,,,所以java提供了:AtomicStampedReference/AtomicMarkableReference 来处理会发生ABA问题的场景,主要是在对象中额外在增加一个标记来标识对象是否有变更过。
四.volatile和synchronized关键字
1)volatile关键字
volatile变量用来确保将变量的更新操作通知到其他线程;能在变量级别使用;能够实现变量修改的可见性,不能保证其原子性;在访问volatile变量时不会执行加锁操作,因此也就不会使执行线发生阻塞,,并且用它标记的变量不会被编译器优化。
public class ThreadSecurity01 {
static volatile boolean flag = false; // 线程安全,实现了变量的可见性
public static void main(String[] args) throws Exception {
Thread a = new ThreadA();
Thread b = new ThreadB();
a.start();
Thread.sleep(1000);
b.start();
}
public static class ThreadA extends Thread {
@Override
public void run() {
while (true) {
if (flag) {
System.out.println("A>>" + flag);
try {
Thread.sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
}
}
public static class ThreadB extends Thread {
@Override
public void run() {
flag = true;
System.out.println("B>>flag>>" + flag);
try {
Thread.sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2)synchronized关键字
本质上时锁定当前变量,只有当前线程可以访问该变量,其他线程被堵塞住。
使用级别:在变量,方法,和类级别的
可以保证变量的修改可见性和原子性
可能会造成线程的阻塞
标记的变量可以被编译器优化
synchronized时java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1.修饰代码块:被修饰的代码块称为同步语句块,范围是{}内的语句,作用的对象是调用这个代码块的对象。
2.修饰一个方法,被修饰的方法称为同步方法,范围是整个方法,作用对象是调用这个方法的对象。
3.修饰一个类,作用范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
4.修饰一个静态方法,作用范围是整个静态方法,作用的对象是这个类的所有对象。
代码如下:
public class ThreadSynchronized {
public static void main(String[] args) {
Ticket ticket = new Ticket();
TicketThread a1 = new TicketThread(ticket, "A");
TicketThread a2 = new TicketThread(ticket, "B");
TicketThread a3 = new TicketThread(ticket, "C");
TicketThread a4 = new TicketThread(ticket, "D");
TicketThread a5 = new TicketThread(ticket, "E");
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
/**
* 解决方法一,synchronized锁定方法
*
* @author DELL
*
*/
// public static class Ticket {
// int num = 100;
//
// synchronized void sold(String name) { // 站点卖票
// System.out.println(name + "卖出一张后,还余多少张:" + (--num));
// }
//
// int getNum() {
// return this.num;
// }
// }
/**
* 方法二:synchronized锁定语句块--
*
* @author DELL
*
*/
public static class Ticket {
int num = 100;
void sold(String name) {// 站点卖票
synchronized (this) {
System.out.println(name + "卖出一张后,还余多少张:" + (--num));
}
}
int getNum() {
return this.num;
}
}
public static class TicketThread extends Thread {
private Ticket ticket;
private String name;
public TicketThread(Ticket ticket, String name) {
this.ticket = ticket;
this.name = name;
}
@Override
public void run() {
while (ticket.getNum() > 0) {
ticket.sold(this.name);
try {
Thread.sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
以上是关于Java线程总结的主要内容,如果未能解决你的问题,请参考以下文章