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();
				}

			}
		}
	}

}

五.AtomicInteger与int的区别

1.AtomicInteger这个类是为了满足在高并发的情况下,原生的整数型值自增线程不安全问题,int是线程不安全的;用AtomicInteger可确保安全,,但是只能保证在自增或者自减的情况下保证线程安全

public class ThreadSecurity02 {
//	static  volatile int x = 0;   //此时线程不安全,只保证了可见性,++i,i++,,原子性不安全,,,,解决办法如下
//
//	public static void main(String[] args) {
//		Thread a1 = new AtomicThread();
//		Thread a2 = new AtomicThread();
//		Thread a3 = new AtomicThread();
//
//		a1.start();
//		a2.start();
//		a3.start();
//
//	}
//
//	public static class AtomicThread extends Thread {
//		@Override
//		public void run() {
//			for (int i = 0; i <= 100; i++) {
//				System.out.println("X>>" + (++x));
//				try {
//					Thread.sleep(100);
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
//			}
//		}
//	}

	static volatile AtomicInteger x = new AtomicInteger(0);

	public static void main(String[] args) {
		Thread a1 = new AtomicThread();
		Thread a2 = new AtomicThread();
		Thread a3 = new AtomicThread();

		a1.start();
		a2.start();
		a3.start();

	}

	public static class AtomicThread extends Thread {
		@Override
		public void run() {
			for (int i = 0; i <= 100; i++) {
				System.out.println("X>>" + (x.incrementAndGet()));
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

六.线程的三大特性

程序在运行时,如果不满足这三大特性,就有可能产生线程安全问题。

1)原子性

一个操作或者多个操作要么全部执行并且执行过程中不被任何因素打断,要么就不执行

/**
 * 原子性实例
 * 
 * @author DELL
 *
 */

public class ThreadSecurity02 {
//	static  volatile int x = 0;   //++i,i++,,原子性不安全,,,,解决办法如下
//
//	public static void main(String[] args) {
//		Thread a1 = new AtomicThread();
//		Thread a2 = new AtomicThread();
//		Thread a3 = new AtomicThread();
//
//		a1.start();
//		a2.start();
//		a3.start();
//
//	}
//
//	public static class AtomicThread extends Thread {
//		@Override
//		public void run() {
//			for (int i = 0; i <= 100; i++) {
//				System.out.println("X>>" + (++x));
//				try {
//					Thread.sleep(100);
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
//			}
//		}
//	}

	static volatile AtomicInteger x = new AtomicInteger(0);

	public static void main(String[] args) {
		Thread a1 = new AtomicThread();
		Thread a2 = new AtomicThread();
		Thread a3 = new AtomicThread();

		a1.start();
		a2.start();
		a3.start();

	}

	public static class AtomicThread extends Thread {
		@Override
		public void run() {
			for (int i = 0; i <= 100; i++) {
				System.out.println("X>>" + (x.incrementAndGet()));
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

类型引用可确保原子性安全,如下:
在这里插入图片描述

2)有序性

在java内存模型中,允许编译器和处理器对指令进行重排序,但是排序过程中不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性

public class ThreadYouXu {
	public static class Vars {
		static int i = 0, j = 0, k = 0; // 定义三个变量
	}

	public static void main(String[] args) {
		Vars va = new Vars();

		Thread a1 = new Thread(new Runnable() {

			@Override
			public void run() {
				va.i = 1;
				System.out.print("i>>" + va.i + "!!!");
			}
		});

		Thread a2 = new Thread(new Runnable() {

			@Override
			public void run() {
				va.j = 1;
				System.out.print("j>>" + va.j + "!!!");
			}
		});
		Thread a3 = new Thread(new Runnable() {

			@Override
			public void run() {
				va.k = 1;
				System.out.print("k>>" + va.k);
			}
		});

		a1.start();
		a2.start();
		a3.start();

	}
	// 输出第一次结果:j>>1!!!i>>1!!!k>>1
	// 输出第二次结果:j>>1!!!k>>1i>>1!!!
	// 输出第三次结果:i>>1!!!j>>1!!!k>>1

}

3)可见性

当多个线程同时访问一个变量时,一个线程修改了这个变量的值,其他线程能立即看得到它修改的值

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();
			}
		}

	}
}

七.死锁的四个必要条件

代码实例:

/**
	 * 死锁问题
	 * 
	 * @param args
	 */

	public static void main(String[] args) {
		DeadLock a = new DeadLock("A");
		DeadLock b = new DeadLock("B");
		DeadLock c = new DeadLock("C");

		Thread a1 = new DeadLockThread(a, b, c);
		Thread a2 = new DeadLockThread(b, c, a);
		Thread a3 = new DeadLockThread(c, a, b);

		a1.start();
		a2.start();
		a3.start();
	}

	public static class DeadLock {
		private String name;

		public DeadLock(String name) {
			this.name = name;
		}

		public String getName() {
			return this.name;
		}
	}

	public static class DeadLockThread extends Thread {
		private DeadLock a;
		private DeadLock b;
		private DeadLock c;

		public DeadLockThread(DeadLock a, DeadLock b, DeadLock c) {
			this.a = a;
			this.b = b;
			this.c = c;
		}

		@Override
		public void run() {
			synchronized (a) {
				System.out.println("锁住:" + a.getName() + "资源成功");
				System.out.println("准备锁住:" + b.getName() + "资源成功");
				synchronized (b) {
					System.out.println("锁住:" + b.getName() + "资源成功");
					System.out.println("准备锁住:" + c.getName() + "资源成功");
					synchronized (c) {
						System.out.println("锁住:" + c.getName() + "资源成功");
					}
					System.out.println("释放:" + c.getName() + "资源成功");
				}
				System.out.println("释放:" + b.getName() + "资源成功");
			}
			System.out.println("释放:" + a.getName() + "资源成功");
		}

	}

}
// 输出:锁住:A资源成功
         // 锁住:C资源成功
         //锁住:B资源成功
       //准备锁住:A资源成功
       //准备锁住:B资源成功
        //准备锁住:C资源成功

1)互斥

一个资源同时只能被一个线程所使用

2)不可抢占

进程以及获得的资源,没有外界力量来抢占它,资源自能够自动放弃

3)请求与保持

进程必须占有资源,再去申请,例如请求第二把锁的时候,需要保持自身的第一把锁不被释放

4)循环等待

两个线程时,是你等我释放锁,我等你释放锁,多个线程时,是头尾相接的等待----如同一个环形公路一样

八.可重入锁

可重入锁,也叫递归锁,值得是同一线程 外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不影响。 使用灵活,但必须要有释放锁的动作,且手动释放和开启锁,只能适用于代码块锁。
Lock 是一个接口提供无条件、可轮训的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显示的。
ReentrantLock(可重入锁)是Lock接口的实现类。
代码实例:

/**
 * 可重入锁
 * 
 * @author DELL
 *
 */

public class ReenTrantLockThread {
	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();
	}

	public static class Ticket {
		private final ReentrantLock lock = new ReentrantLock(); // 创建
		int num = 100;

		void sold(String name) { // 站点卖票
			lock.lock(); // 表示要开始启动锁了
			try {
				System.out.println(name + "卖出一张后,还余多少张:" + (--num));
			} finally {
				lock.unlock(); // 处理
			}
		}

		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();
				}

			}
		}
	}
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值