Java线程总结

Posted You295

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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线程总结的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

学习java第19天个人总结

java并发线程锁技术的使用

java中的进程,线程,线程池总结

号称史上最全Java多线程与并发面试题总结—基础篇

第十周java学习总结