多线程
线程
- 线程是独立的执行路径;
- 在线程运行时,即使没有自己创建线程,后台也会有多个线程,如:gc线程、主线程
- main()称之为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
- 对同一根资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如cpu的调度,并发控制的开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程开启不一定立即执行,听从cpu调度
创建线程:继承Thread/Runable/Callable
Thread基本使用
重写run,创建对象,调用start
public class TestThread extends Thread {
@Override
public void run() {
System.out.println("我在学习多线程");
}
public static void main(String[] args) {
TestThread testThread1 = new TestThread();
testThread1.start();
}
}
Runnable
实现Runnable接口,重写run,创建对象,将对象放入Thread对象,启动start
public class TestThread implements Runnable {
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("我在学习多线程111111111");
}
}
public static void main(String[] args) {
TestThread testThread1 = new TestThread();
Thread thread = new Thread(testThread1);
thread.start();
for (int j=0;j<1000;j++){
System.out.println("我在学习编程");
}
}
}
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.net.*;
//sleep 模拟网络延时,放大问题
//每个对象都有一把锁,sleep不会释放锁
public class TestThread implements Runnable {
static int pick = 10;
@Override
public void run() {
try{
Thread.sleep(100L);
}catch(Exception e){
e.printStackTrace();
}
while(true){
if(pick<=0){
break;
}
System.out.println(Thread.currentThread()+":拿到"+pick--);
}
}
public static void main(String[] args) {
TestThread testThread1 = new TestThread();
Thread thread1 = new Thread(testThread1,"小明");
Thread thread2 = new Thread(testThread1,"小王");
Thread thread3 = new Thread(testThread1,"小张");
thread1.start();
thread2.start();
thread3.start();
}
}
结果:Thread[小明,5,main]:拿到10//重复
Thread[小张,5,main]:拿到9
Thread[小王,5,main]:拿到10//重复
Thread[小张,5,main]:拿到7
Thread[小王,5,main]:拿到6
Thread[小明,5,main]:拿到8
Thread[小王,5,main]:拿到4
Thread[小张,5,main]:拿到5
Thread[小王,5,main]:拿到2
Thread[小明,5,main]:拿到3
Thread[小张,5,main]:拿到1
//sleep 模拟倒计时
public class TestThread {
public static void main(String[] args) {
TestThread thread = new TestThread();
try {
thread.tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void tenDown() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);
if(num>=0){
System.out.println(num--);
}else {
break;
}
}
}
}
Callable
public class TestThread implements Callable<Boolean> {
public static void main(String[] args) {
TestThread testCallable = new TestThread();
//创建执行服务
ExecutorService service = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> r = service.submit(testCallable);
//获取结果
try {
boolean rs = r.get();
System.out.println(rs);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//关闭服务
service.shutdownNow();
}
@Override
public Boolean call(){
return true;
}
}
停止线程
- 建议使用线程正常停止---->利用次数,不建议死循环
- 建议使用标志位----->设置一个标志位
- 不要使用stop或者destruy等过时或者JDK不建议使用的方法
public class TestStop implements Runnable{
//1.设置一个标志位
private static boolean flag = true;
public void stop(){
flag = false;
}
public static void main(String[] args) {
TestStop testStop =new TestStop();
Thread thread = new Thread(testStop);
thread.start();
for(int i=0;i<10000000;i++){
if(i==9000000){
testStop.stop();
}
}
}
@Override
public void run() {
int i= 0;
while (flag){
System.out.println("run....Thread"+i++);
}
}
}
线程礼让
//礼让不一定成功
public class TestYield {
public static void main(String[] args) {
Yield yield = new Yield();
Thread threadA = new Thread(yield,"A");
Thread threadB = new Thread(yield,"B");
threadA.start();
threadB.start();
}
}
class Yield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程合并(插队)
public class TestJoin implements Runnable{
public static void main(String[] args) throws InterruptedException {
Runnable runable = new TestJoin();
Thread thread = new Thread(runable);
thread.start();
for(int i=0;i<500;i++){
if(i==200){
thread.join();//插队
}
System.out.println("main:"+i);
}
}
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("关系户来了~~~");
}
}
}
观测线程
线程状态:NEW(创建)、就绪状态(调用start()后进入就绪状态)、运行 状态(执行线程体的代码块)、阻塞状态(调用sleep/wait/同 步锁定时进入阻塞状态,解除后重新进入就绪状态)、 dead(线程结束,结束后不能再次启动)
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("//");
});
//观测线程状态
Thread.State state = thread.getState();
System.out.println(state);//NEW
//观测启动后
thread.start();//启动线程
state = thread.getState();
System.out.println(state);//RUN
while (state !=Thread.State.TERMINATED) {//只要线程不终止就一直输出
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
线程的优先级
线程调度器按照优先级确定应该调度哪个县城来执行,具体还是看CPU,CPU优先调度优先级低的线程叫:线程倒置
线程优先级从1~10,越大优先级越高
设置优先级要在start()之前
public class TestPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
Runnable runnable = new TestPriority();
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable);
thread.start();
Thread thread2 = new Thread(runnable);
thread1.setPriority(8);
thread1.start();
thread2.setPriority(1);
thread2.start();
Thread thread3 = new Thread(runnable);
thread3.setPriority(10);
thread3.start();
Thread thread4 = new Thread(runnable);
thread4.setPriority(7);
thread4.start();
Thread thread5 = new Thread(runnable);
thread5.setPriority(2);
thread5.start();
Thread thread6 = new Thread(runnable);
thread6.setPriority(6);
thread6.start();
}
}
结果:Thread-1--->8
Thread-3--->10
Thread-4--->7
Thread-0--->5
Thread-6--->6
Thread-2--->1
Thread-5--->2
守护线程
- 线程分为守护线程和用户线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不需要等待守护线程执行完毕(如:垃圾回收、监控日志、后台记录操作日志)
//测试守护线程
//上帝守护你
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);
thread.start();//上帝守护线程启动
new Thread(you).start();//你 用户线程启动
}
}
//上帝
class God implements Runnable {
@Override
public void run() {
while(true){
System.out.println("上帝保佑着你!");
}
}
}
//你
class You implements Runnable {
@Override
public void run() {
for (int i=0;i<36500;i++){
System.out.println("你一生都开心的活着!");
}
System.out.println("-=====GOODBYE! WORD====-");
}
}
线程同步
- 一个线程持有锁会导致其他所有需要此锁的线程挂起;
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题
线程不安全的例子
//不安全的买票
public class TestSyn {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
Thread thread = new Thread(buyTicket,"苦逼的我们");
Thread thread1 = new Thread(buyTicket,"可恶的黄牛党");
Thread thread2 = new Thread(buyTicket,"牛逼的你们");
thread.start();
thread1.start();
thread2.start();
}
}
class BuyTicket implements Runnable {
private int TicketNums = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
buy();
}
}
private void buy(){
//判断是否有票
if(TicketNums<=0) {
flag = false;
return;
}
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到"+TicketNums--);
}
}
结果:牛逼的你们拿到10
苦逼的我们拿到8
可恶的黄牛党拿到9
苦逼的我们拿到7
牛逼的你们拿到6
可恶的黄牛党拿到5
苦逼的我们拿到4
可恶的黄牛党拿到2
牛逼的你们拿到3
牛逼的你们拿到1
可恶的黄牛党拿到0
苦逼的我们拿到1
Process finished with exit code 0
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnSafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i=0;i<10000;i++){
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try{
Thread.sleep(100000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
结果:9998
synchronized同步
//不安全的买票
public class TestSyn {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
Thread thread = new Thread(buyTicket,"苦逼的我们");
Thread thread1 = new Thread(buyTicket,"可恶的黄牛党");
Thread thread2 = new Thread(buyTicket,"牛逼的你们");
thread.start();
thread1.start();
thread2.start();
}
}
class BuyTicket implements Runnable {
private int TicketNums = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
buy();
}
}
private synchronized void buy(){
//判断是否有票
if(TicketNums<=0) {
flag = false;
return;
}
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到"+TicketNums--);
}
}
结果:苦逼的我们拿到10
苦逼的我们拿到9
可恶的黄牛党拿到8
可恶的黄牛党拿到7
可恶的黄牛党拿到6
可恶的黄牛党拿到5
牛逼的你们拿到4
可恶的黄牛党拿到3
可恶的黄牛党拿到2
可恶的黄牛党拿到1
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnSafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i=0;i<10000;i++){
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try{
Thread.sleep(100000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
结果:10000
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for(int i=0;i<10000;i++){
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
结果:10000
同步块:synchronized(Obj){}
Obj称为同步监视器
- Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
死锁
多个线程成各自占有一些共享资源,并且相互等待其他线程占用的资源释放才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有“两个以上对象的锁”时,就可能发生“死锁”
//多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup makeup = new Makeup(0,"灰姑娘");
Makeup makeup1 = new Makeup(1,"白雪公主");
makeup.start();
makeup1.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread {
//需要的资源只有一份,用static保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
//化妆
makeup();
}
//化妆,互相持有对方的锁,就是需要拿到对方的资源
private void makeup(){
if(choice==0){
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
try {
Thread.sleep(1000);
}catch (Exception E){
E.printStackTrace();
}
synchronized (mirror) {
System.out.println(this.girlName+"获得镜子的锁");
}
}
}else {
synchronized (mirror) {
System.out.println(this.girlName+"获得镜子的锁");
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
synchronized (lipstick) {
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
}
结果:灰姑娘获得口红的锁
白雪公主获得镜子的锁
//多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup makeup = new Makeup(0,"灰姑娘");
Makeup makeup1 = new Makeup(1,"白雪公主");
makeup.start();
makeup1.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread {
//需要的资源只有一份,用static保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
//化妆
makeup();
}
//化妆,互相持有对方的锁,就是需要拿到对方的资源
private void makeup(){
if(choice==0){
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
try {
Thread.sleep(1000);
}catch (Exception E){
E.printStackTrace();
}
}
synchronized (mirror) {
System.out.println(this.girlName+"获得镜子的锁");
}
}else {
synchronized (mirror) {
System.out.println(this.girlName+"获得镜子的锁");
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
synchronized (lipstick) {
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
结果:灰姑娘获得口红的锁
白雪公主获得镜子的锁
灰姑娘获得镜子的锁
白雪公主获得口红的锁
死锁避免方法
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能抢行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
(LOCK)锁
//不加锁
public class TestLock {
public static void main(String[] args) {
TestLock2 lock = new TestLock2();
new Thread(lock).start();
new Thread(lock).start();
new Thread(lock).start();
}
}
//
class TestLock2 implements Runnable {
int tickNums = 10;
@Override
public void run() {
while(true){
if(tickNums>0){
try{
Thread.sleep(1000);
}catch (Exception E){
E.printStackTrace();
}
System.out.println(tickNums--);
}else {
break;
}
}
}
}
结果:10
9
8
7
6
7
5
4
3
2
0
1
//---------------加锁------------------
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TestLock2 lock = new TestLock2();
new Thread(lock).start();
new Thread(lock).start();
new Thread(lock).start();
}
}
//
class TestLock2 implements Runnable {
int tickNums = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();//可重复锁
@Override
public void run() {
while(true){
try{
//加锁
lock.lock();
if(tickNums>0){
try{
Thread.sleep(1000);
}catch (Exception E){
E.printStackTrace();
}
System.out.println(tickNums--);
}else {
break;
}
}catch (Exception e) {
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}
}
}
结果:10
9
8
7
6
5
4
3
2
1
synchronized与Lock对比
-
Lock是显式锁(手动开启和关闭锁,开启之后要手动设置关闭位置)synchronized是隐式锁,出了作用域自动释放
-
Lock只有代码块锁,synchronized有代码块锁和方法锁
-
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多子类)
-
优先使用顺序
Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
线程协作
线程通信
应用场景:生产者和消费者问题
- 假设仓库中存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中得产品取走消费
- 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中得产品被消费者取走为止
- 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
这是一个线程同步问题,生产者和消费共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.
java提供的解决线程间通信问题的方法
- wait():表示线程一直等待,直到其他线程通知,与sleep不同,它会释放锁
- wait(long timeout):指定等待的毫秒数
- notify():唤醒一个处于等待的线程
- notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
注:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException
并发协作模型“生产者/消费者模式”—>管程法
- 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)
- 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)
- 缓冲区:消费者不能直接使用生产者的数据,他们之间有“缓冲区”
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
//测试生产者消费者模型---->利用缓冲区:管程法
//生产者、消费者、产品、缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread {
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("生产了"+i+"只鸡");
container.push(new Chicken(i));
}
}
}
//消费者
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
//消费
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("消费了"+container.pop().id+"只鸡");
}
}
}
//产品
class Chicken extends Thread {
int id; //生产编号
public Chicken(int id){
this.id = id;
}
}
//缓冲区
class SynContainer {
int count = 0;
//需要一个容器大小
Chicken[] chicken = new Chicken[10];
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,就需要等待消费者消费
if(this.chicken.length==count){
//通知消费者消费,生产等待
try{
this.wait();
}catch (Exception e) {
e.printStackTrace();
}
}
//如果容器没有满,我们需要丢入产品
this.chicken[count] = chicken;
count++;
//可以通知消费者消费了
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断是否消费
if(count==0){
//等待生产者生产,消费者等待
try{
this.wait();
}catch (Exception e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = this.chicken[count];
//消费结束,通知生产者生产
this.notifyAll();
return chicken;
}
}
并发协作模型“生产者/消费者模式”—>信号灯法
//测试生产者消费者模型---->信号灯法
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new ConSumer(tv).start();
}
}
//生产者---演员
class Player extends Thread {
TV TV;
public Player(TV tv){
this.TV = tv;
}
@Override
public void run() {
for(int i=0;i<20;i++){
if(i%2==0){
this.TV.play("快乐大本营");
}else {
this.TV.play("中国好声音");
}
}
}
}
//消费者---观众
class ConSumer extends Thread {
TV tv;
public ConSumer (TV tv){
this.tv = tv;
}
@Override
public void run() {
for(int i=0;i<20;i++){
tv.watch();
}
}
}
//产品---节目
class TV {
//演员表演:观众等待
//观众观看:演员等待
String voice;
boolean flag = true;
public synchronized void play(String voice) {
if(!flag){
try{
this.wait();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("演员表演了"+voice);
//通知观众观看
this.notifyAll();
this.voice = voice;
this.flag = false;
}
//观看
public synchronized void watch(){
if(flag){
try{
this.wait();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("观看了"+voice);
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
背景:经常创建和销毁、使用量特别大的资源,如:并发情况下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回线程池中。可以避免频繁创建销毁、实现重复利用。类似生活中的交通工具
好处:
-
提高响应速度(减少了创建线程的时间)
-
降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
-
便于线程管理。
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
ExecutorService:真正的线程池接口。常见子类ThreadPoolExceutor
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
- void shutdown():关闭连接池
Exexutors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
希望在码农的山坡上越攀越高,欢迎各位大佬看官的指正及补充