文章目录

  • 前言
  • 线程与进程
    • 线程
    • 进程
  • 线程的创建
    • 线程的三种创建方式
      • 1.继承Thread类
      • 2.实现Runnable接口
      • 3.实现Callable接口
  • 线程状态
    • 线程的五大状态
    • 获取线程状态
  • 线程常用方法
    • 停止线程
    • 线程休眠sleep()
    • 线程的礼让yield()
    • 线程强制执行join()
    • 线程的优先级setPriority(int newPriority)
    • 守护线程setDeamon()
  • 线程同步
    • 对象共享问题
    • 同步方法和同步代码块
      • 同步方法
      • 同步代码块
    • 死锁
      • 死锁产生的条件
      • Lock锁
      • 公平锁
    • synchronized 和 Lock的区别
    • 关于多线程访问同步方法的7种情况
  • 线程协作
    • 线程的等待wati()
    • 线程的唤醒notify()和notifyALL()
    • 管程法
    • 信号灯法
  • 线程池

前言

提示:狂神多线程教程的笔记,加上自己理解.
如有不足,请多指正


线程与进程

线程

  • 线程就是独立的执行路径,是cpu调度和执行的单位

  • 线程是序执行流中最小执行单位,是进程中实际运行单位。

  • main()称为主线程,为系统的入口,用于执行整个程序

  • 一个进程中如果开辟了多个线程,线程的运行是由调度器安排的。调度器是与操作系统紧密相关的,先后顺序看CPU心情,不能人为干预

  • 线程对共享资源进行操作时,会出现安全问题,需要加入并发控制

  • 线程会消耗资源,如CPU调度时间,并发控制资源开销

  • 每个线程会在自己的工作内存中进行交互,内存控制不当会造成数据不一致问题

进程

  • 进程是执行程序的一次执行过程,是一个动态的过程,是一个活动的实体,是系统资源分配的单位。
  • 一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者。可以说,进程中包含了多个可以同时运行的线程。

举一个简单的例子

你打开一个视频,视频应用程序运行就可以被看做一个进程。

而视频播放时,视频的画面,音频,弹幕,都是独立的执行路径,这些就可以被看做一个进程

线程的创建

线程的三种创建方式

1.继承Thread类

  1. 继承Thread类

  2. 重写run()方法,编写线程体

  3. 然后通过start()启动线程(线程开启不一定立即执行,由cpu进行调度)

//1.继承Thread类
public class ThreadTest extends Thread {//2.重写run()方法,编写线程体@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "线程方法被调用了");}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();//3.然后通过start()启动线程threadTest.start();}
}

继承Thread类的缺点,oop单继承的局限性,于是有了Runnable接口

推荐使用实现Runnable接口,方便同一对象被多个线程使用,实现资源共享

2.实现Runnable接口

  1. 实现Runable接口

  2. 重写Run()方法,编写线程体

  3. 创建Runnable对象

  4. 再通过Thread进行静态代理,启动线程

//1.实现Runnable接口
class TestRunnable implements Runnable {//2.重写Run()方法,编写线程体,@Overridepublic void run() {System.out.println("实现了run方法");}
}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();threadTest.start();//3.创建Runnable对象//4.通过Thread进行静态代理,启动线程Thread thread = new Thread(new TestRunnable());thread.start();
}

3.实现Callable接口

实现Callable接口

重写call方法

线程状态

线程的五大状态

  • 创建状态
  • 就绪状态
  • 阻塞状态
  • 运行状态
  • 死亡状态


获取线程状态

线程常用方法

线程方法 使用说明
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void join() 插入调用该方法的线程对象,直至该线程终结
void interrupt 中断线程,不建议使用
setPriority(int newPriority) 更改线程的优先级,用数字来表示,范围从1~10 。10最高,默认为5
boolean isAlive 测试线程是否处于活动状态

停止线程

  • jdk中提供stop()、destroy()方法,不建议使用,已废弃
  • 推荐使用一个标志位,终止线程的运行
class DemoStop implements Runnable {//定义线程体使用的标识private boolean flag = true;@Overridepublic void run() {int i = 0;//线程体中使用该标识while (flag) {System.out.println("run===Thread" + i++);}}public void stop() {this.flag = false;System.out.println("线程停止");}
}public class TestStop {public static void main(String[] args) {DemoStop demoStop = new DemoStop();new Thread(demoStop).start();for (int i = 0; i < 1000; i++) {System.out.println(i);//main线程中,i=500时,通过调用stop方法改变标志位,从而停止线程if (i == 500) {demoStop.stop();}}}
}

线程休眠sleep()

  • sleep(毫秒数)指定当前线程停止的时间,进入阻塞状态
  • sleep()时间到达后,被自动唤醒,线程进入就绪状态
  • sleep()存在异常InteruptedException(中断异常)
  • sleep()可以模拟网络延时,倒计时等,用于放大线程问题的发生性
  • 使用sleep()时,不会释放锁资源
  • sleep()可以在任意位置使用
//用slee()方法打印当前时间
class DemoSleep implements  Runnable {Date startTime = new Date(System.currentTimeMillis());@Overridepublic void run() {while (true){try {//一秒后线程进入就绪状态Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));startTime=new Date(System.currentTimeMillis());} catch (InterruptedException e) {e.printStackTrace();}}}
}public class TestSleep {public static void main(String[] args) {Date startTime = new Date(System.currentTimeMillis());while (true){try {Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));startTime=new Date(System.currentTimeMillis());} catch (InterruptedException e) {e.printStackTrace();}}}
}

线程的礼让yield()

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞,进入就绪状态
  • 让CPU重新调度,下一次依旧可能调用到原来的礼让线程,所以礼让不一定成功,看CPU心情
class DemoYield implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"线程开始");//线程进行礼让Thread.yield();System.out.println(Thread.currentThread().getName()+"线程结束");}
}public class TestYield {public static void main(String[] args) {DemoYield demoYield = new DemoYield();new Thread(demoYield,"线程A").start();new Thread(demoYield,"线程B").start();}
}

线程强制执行join()

  • Join合并线程,待此线程执行完成后,在执行其他线程,其他线程阻塞
  • 可以想象成银行排队时插队,vip有特权,优先执行,有钱真好
class DemoJoin implements Runnable{@Overridepublic void run() {for (int i = 0; i <1000 ; i++) {System.out.println("vip来了"+i);}}}public class TestJoin {public static void main(String[] args) throws InterruptedException {DemoJoin demoJoin = new DemoJoin();Thread thread = new Thread(demoJoin);thread.start();for (int i = 0; i <800 ; i++) {//当main线程执行到200时,thread线程就来插队了,main线程阻塞,等thread线程执行完,才轮到mainif (i==200){thread.join();}System.out.println("main"+i);}}
}

线程的优先级setPriority(int newPriority)

  • java 提供一个线程调度器来监视程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定该调度哪个线程来执行

  • 线程的优先级用数字来表示,范围从110。优先级从101依次递减,线程默认的优先级为5

    Thread.MIN_PRIORITY = 1(最小优先级)

    Thread.MAX_PRIORITY= 10(最大优先级)

    Thread.NORM_PRIORITY = 5(默认优先级)

  • 使用getPriority()和setPriority()来获取或改变优先级

  • 不是优先级越高,线程就先执行。优先级反映的是线程占用资源的多少,优先级越高,占用的资源越多,性能越好,被cpu优先调度的可能性就越高

class DemoPriority implements  Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"=====>>>"+Thread.currentThread().getPriority());}
}
public class TestPriority {public static void main(String[] args) {System.out.println(Thread.currentThread().getName()+"=====>>>"+Thread.currentThread().getPriority());DemoPriority demoPriority = new DemoPriority();Thread thread1 = new Thread(demoPriority,"线程1");Thread thread2 = new Thread(demoPriority,"线程2");Thread thread3 = new Thread(demoPriority,"线程3");Thread thread4 = new Thread(demoPriority,"线程4");thread1.setPriority(Thread.MIN_PRIORITY);//1thread1.start();thread2.setPriority(Thread.MAX_PRIORITY);//10thread2.start();thread3.setPriority(Thread.NORM_PRIORITY);//5thread3.start();thread4.setPriority(6);//6thread4.start();}
}

守护线程setDeamon()

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕,如后台记录操作日志,监控内存,垃圾回收等
//你
class  You implements  Runnable{@Overridepublic void run() {for (int i = 0; i < 36500; i++) {System.out.println("开心生活每一天");}}
}//上帝守护着你
class  God implements  Runnable{@Overridepublic void run() {while(true){System.out.println("上帝保佑着你");}}
}public class TestDeamo {public static void main(String[] args) {God god = new God();You you = new You();Thread thread = new Thread(god);//默认是false,表示用户线程,正常的线程都是用户线程thread.setDaemon(true);thread.start();new Thread(you).start();}
}

线程同步

对象共享问题

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步。线程同步其实是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程在使用

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问的冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制syncronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可,存在一下问题

  • 一个线程持有锁会导致其他所有需要此锁的进程挂起
  • 在多线程竞争的情况下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
  • 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题

对象锁是在一个类的对象上加的的锁,只有一把,不管有几个方法进行了同步。
这些同步方法都共有一把锁,只要一个线程获得了这个对象锁,其他的线程就不能访问该对象的任何一个同步方法

同步方法和同步代码块

同步方法

同步方法在方法上添加synchronized关键字, 由于java的每个对象都有一个内置锁,锁的是对象本身。当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

注:synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

例子1

class DemoSynchronized implements Runnable {private int ticketNums = 100;private boolean flag = true;@Overridepublic void run() {while (flag){try {buy();} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized  void buy() throws InterruptedException {while (ticketNums<=0){flag = false;System.out.println("票卖完了");return;}Thread.sleep(100);System.out.println(Thread.currentThread().getName()+ "拿票"+ticketNums--);}}
public class TestSynchronized {public static void main(String[] args) {DemoSynchronized demoSynchronized = new DemoSynchronized();new Thread(demoSynchronized,"苦逼的我").start();new Thread(demoSynchronized,"牛逼的你").start();new Thread(demoSynchronized,"可恶的黄牛").start();}}

同步代码块

synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动加上内置锁,从而实现同步

同步块: synchronized(obj){}

obj称之为同步监视器

  • obj可以是任何对象,但是推荐使用共享资源作为同步监视器
  • 同步方法中无需指定同步监视器,因为同步方法中的同步监视器就是this,就是这个对象本身,或者是class

同步监视器的执行过程

  • 第一个线程访问,锁定同步监视器,执行其中的代码
  • 第二个线程访问,发现同步监视器被锁定,无法访问
  • 第一个线程访问完毕,解锁同步监视器
  • 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

例子二

//不安全取钱
public class UnsafeBank {public static void main(String[] args) {//账户,两个人去银行取钱Account account=new Account(100,"结婚基金");Drawing you=new Drawing(account,50,"你");Drawing girlFriend=new Drawing(account,100,"女朋友");you.start();girlFriend.start();}
}
//账户
class Account{int money;//余额String name;//卡名public Account(int money, String name) {this.money = money;this.name = name;}
}
//银行:模拟取款
class Drawing extends Thread{Account account;//账户//取了多少钱;int drawingMoney;//现在手里又多少钱int nowMoney;public Drawing(Account account,int drawingMoney,String name){super(name);this.account=account;this.drawingMoney=drawingMoney;}//取钱@Overridepublic void run() {synchronized (account) {//锁的对象是变化的量,锁需要增删改的对象//判断有没有钱if (account.money - drawingMoney <= 0) {System.out.println(Thread.currentThread().getName() + "钱不够");return;}//卡内余额account.money -= drawingMoney;//手里的钱nowMoney += drawingMoney;System.out.println(account.name + "余额为:" + account.money);System.out.println(this.getName() + "手里的钱:" + nowMoney);}}
}


不同线程的实例对象不同,都是各自对象的锁

例子三

public class UnsafeList {public static void main(String[] args) {ArrayList<String> list=new ArrayList<String>();for (int i = 0; i <1000 ; i++) {new Thread(()->{synchronized (list){list.add(Thread.currentThread().getName());}}).start();}System.out.println(list.size());}
}

因为 ArrayList 不是线程安全的,在高并发情况下对list进行数据添加会出现数据丢失的情况。

main线程在遍历List,另一个线程修改List。

一个线程在遍历List,另一个线程修改List,可能会报ConcurrentModificationException(并发修改异常)错误

死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源释放才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有"两个以上的对象锁"时,就可能发生死锁现象

死锁产生的条件

  • 互斥条件: 一个资源每次只能被一个进程使用
  • 请求保持条件: 一个进程因请求资源而阻塞时.对以获得的资源保持不放
  • 不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺
  • 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系

Lock锁

  • 从JDK1.5开始,java提供了更为强大的线程同步机制——通过显示定义同步锁对象来实现同步,同步锁使用lock对象来充当

  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象

  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中。比较常用的是ReentrantLock,可以显示加锁,释放锁

public class TestLock {public static void main(String[] args) {TestLock2 testLock2 = new TestLock2();new Thread(testLock2).start();new Thread(testLock2).start();new Thread(testLock2).start();}
}class TestLock2 implements Runnable {int ticketNums = 10;//定义Lock锁private ReentrantLock reentrantLock = new ReentrantLock();@Overridepublic void run() {while (true) {try {reentrantLock.lock(); //加锁if(ticketNums > 0 ){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "购买了                                         第" + ticketNums-- + "张票");} else {break;}} finally {//解锁reentrantLock.unlock();}}}
}

公平锁

创建互斥锁对象的时候,通过构造方法传递 true 的时候,会成为公平锁

public class Demo01 {public static void main(String[] args) throws InterruptedException {Girl girl = new Girl();for (int i = 1; i < 11; i++) {//第二参数为线程的名字new Thread(girl,"第"+i+"个小女孩").start();Thread.sleep(100);}}}//任务类对象
class Girl implements Runnable{//卖出去的核弹private int bomb = 0;private static final Object obj = new Object();//当创建互斥锁对象的时候,通过构造方法传递 true 的时候,会成为公平锁private Lock myLock = new ReentrantLock(true);@Overridepublic void run() {try {sellBomb();} catch (InterruptedException e) {e.printStackTrace();}}/*** 卖核弹的方法*/private void sellBomb() throws InterruptedException {while(true){Thread.sleep(1);//获取锁myLock.lock();try{//无法使用wait()//myLock.wait();if(bomb == 300){System.out.println(Thread.currentThread().getName() + "卖出第" + (++bomb) + "个核弹,不卖回家了");break;}else if(bomb == 500){System.out.println(Thread.currentThread().getName() + "卖出第" + (++bomb) + "个核弹,也不卖回家了");break;} else if(bomb < 1000) {System.out.println(Thread.currentThread().getName() + "卖出第" + (++bomb) + "个核弹");}else{System.out.println(Thread.currentThread().getName()+"卖完了");break;}//为了增长同步的线程的运行时间Thread.sleep(5000);}finally {//释放锁myLock.unlock();}}}
}

synchronized 和 Lock的区别

  1. 同步锁是关键词,Lock是接口
  2. 同步锁形参的同步区域可以调用 wait() 使线程进入阻塞状态,使用 Lock 无法使用 wait()
  3. 同步锁是隐式获取锁释放锁,Lock 是显式获取锁和释放锁

关于多线程访问同步方法的7种情况

  1. 多个线程访问一个实例对象的同步方法
  2. 多个线程访问一个静态的同步方法
  3. 多个线程访问多个静态的同步方法
  4. 多线程访问一个实例对象的不同的同步方法
  5. 一个线程同时访问同步方法和非同步方法
  6. 一个线程同时访问多个不同的同步方法
  7. 同步方法抛出异常,锁的释放

线程协作

线程通信的常用方法

方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,不会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于就绪状态的线程
notifyAll() 唤醒同一个对象所有调用wait()方法的线程,优先级别高的线程优先调度

以上方法都是Object方法,都只能在同步方法或者同步代码块中使用,否则会抛出IIIegaIMonitorStateException(非法的监视状态)异常

线程的等待wati()

  • wati()来自于Object(),只能在同步区域内使用
  • 使用wait()方法,线程在同步方法区中进行等待,会释放资源
  • wait不会自动唤醒,只能通过notify()或者notifyAll()进行唤醒

线程的唤醒notify()和notifyALL()

  • wait()只能通过notify唤醒,notify唤醒的是第一个进入等待的线程,先进先出
  • notifyAll()唤醒所有线程我,最后进入等待的线程最先被唤醒:后进先出
//卖核弹的小女孩
public class Demo01 {public static void main(String[] args) {Girl girl = new Girl();for (int i = 1; i < 11; i++) {//第二参数为线程的名字new Thread(girl,"第"+i+"个小女孩").start();}}
}//任务类对象
class Girl implements Runnable{//卖出去的核弹private int bomb = 0;//同步监视器private static final Object obj = new Object();@Overridepublic void run() {try {sellBomb();} catch (InterruptedException e) {e.printStackTrace();}}/*** 卖核弹的方法*/private void sellBomb() throws InterruptedException {while(true){Thread.sleep(1);synchronized (obj){//卖出去的核弹小于1000if(bomb < 1000){System.out.println(Thread.currentThread().getName() + "卖出第" + (++bomb) + "个核弹");}else{System.out.println(Thread.currentThread().getName()+"卖完了");break;}//在同步区域中,使当前线程进入阻塞状态    --  休眠//当前线程没有释放资源,CPU 还是调用当前线程//Thread.sleep(10000);//在同步区域中,使当前线程进入阻塞状态   --  等待//当前线程会释放资源,CPU 会调用其他线程obj.wait();}//在同步区域外,使当前线程进入阻塞状态    --  等待//会出现异常//obj.wait();}}
}

管程法

并发协作模式"生产者/消费者模式" ===>管程法

  • 生产者: 负责生产数据的模块(可能是方法,对象,线程,数组)
  • 消费者: 负责处理数据的模块(可能是方法,对象,线程,数组)
  • 缓冲区: 消费者不能直接使用生产者的数据,他们之间有个缓冲区

核心:生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

//创建一个产品类
class Product {private int id;private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Product(int id) {this.id = id;}
}//创建一个生产者对象
class ProviderThread extends Thread {private SyncContainer syncContainer;//实例化的时候创建方法区ProviderThread(SyncContainer syncContainer) {this.syncContainer = syncContainer;}@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println("生产了第" + i + "只鸡!");try {syncContainer.push(new Product(i));} catch (InterruptedException e) {e.printStackTrace();}}}
}//创建一个容器类,用于存放数据
class SyncContainer {//容器大小Product[] products = new Product[10];//容器计数器int count = 0;//生产者,放入产品public synchronized void push(Product product) throws InterruptedException {//如果容器满了,就要等待消费者。通知消费者消费,生产者等待if (count == products.length) {this.wait();}//如果容器没有满,就要生产产品products[count] = product;count++;//唤醒等待的线程this.notifyAll();}//消费者消费产品public synchronized Product pop() throws InterruptedException {//判断容器是否为空,通知生产者生产,消费者等待if (count == 0) {this.wait();}count--;Product product = products[count];this.notifyAll();return product;}
}//创建一个消费者类
class ConsumerThread extends Thread {private SyncContainer syncContainer;//实例化的时候创建方法区ConsumerThread(SyncContainer syncContainer) {this.syncContainer = syncContainer;}@Overridepublic void run() {for (int i = 1; i <= 100; i++) {Product pop = null;try {pop = syncContainer.pop();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("消费了第" + pop.getId() + "号鸡");}}
}public class Main {public static void main(String[] args) {SyncContainer container = new SyncContainer();new ProviderThread(container).start();new ConsumerThread(container).start();}
}

信号灯法

并发协作模式"生产者/消费者模式" ===>信号灯法

//产品,电视节目
class Tv {//表演的节目String voice;//信号标志位,true等待 false通知boolean flag = true;//表演public synchronized void play(String voice) {if (!flag) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("演员该表演" + voice);this.voice = voice;this.notifyAll();this.flag = !flag;}//观看public synchronized void watch() {if (flag) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("观众观看了" + voice);//通知演员表演this.notifyAll();this.flag = !flag;}}}//演员
class Player extends Thread {private Tv tv = null;public Player(Tv tv) {this.tv = tv;}public void Player(Tv tv) {this.tv = tv;}@Overridepublic void run() {for (int i = 0; i < 100; i++) {tv.play(i + "号节目");}}}//观众
class Watcher extends Thread {private Tv tv = null;public Watcher(Tv tv) {this.tv = tv;}@Overridepublic void run() {for (int i = 0; i < 100; i++) {tv.watch();}}}public class TestLight {public static void main(String[] args) {Tv tv = new Tv();new Watcher(tv).start();new Player(tv).start();}
}

线程池

  • 背景:线程经常创建和销毁,使用量特别大,比如并发情况显示的线程,对性能影响很大

  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完毕放回池中,可以避免频繁的创建和销毁,实现重复利用,类似生活中的公共交通工具

  • 好处

    提高了响应速度(减少了线程的创建时间)

​ 降低资源的消耗(重复利用线程池中的线程,不需要每次使用都创建)

​ 便于线程的管理

corePoolSize:核心池的大小

maximumPoolSize:最大线程数

keepAliveTime:线程没有任务时最多保持多长时间后会终止

Java多线程入门(狂神说)相关推荐

  1. java多线程入门1

    java多线程入门1 并发和并行 并发: 主要是指多个任务交替执行.而且这个情况可能出现串行的 并行:一般是多个任务同时执行. 该图来自java高并发程序设计 死锁.饥饿.活锁的概念 死锁一般是指几个 ...

  2. Java多线程入门二

    Java多线程入门二 基类 package com.cv.DuoXianCheng;import lombok.extern.slf4j.Slf4j;@Slf4j public class Hero ...

  3. Java多线程学习(二)synchronized关键字(1)

    转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...

  4. java 多线程重温

    之前犯懒一直没有开过博客,然后一直学一直丢一直在重新学...重新学习的过程中找资源是很艰难的,所以今天开通了博客,希望能每天进步一点点,每天更快乐一点点. ---------------------- ...

  5. Java太密来福_这篇文章就是要让你入门java多线程【多线程入门】-Go语言中文社区...

    就在前几天,有位读者朋友私信宜春,说期待出一篇多线程的文章,我当时内心是小鹿乱撞啊-于是这几天茶不思饭不想,好几天深夜皆是辗转反侧,两目深凝,以至于这几天走起路来格外飘飘然,左摇右晃的,魔鬼般的步伐, ...

  6. 别翻了,这篇文章就是要让你入门java多线程!

    目录 1. 理解线程与进程 2.理解并行和并发 3.特殊的一个单线程:主线程(Main线程) 4. 创建多线程的四种方式 5.使用匿名内部类方式创建线程 6.线程安全问题 7.解决线程安全问题:线程同 ...

  7. 这篇文章就是要让你入门java多线程【多线程入门】

    就在前几天,有位读者朋友私信宜春,说期待出一篇多线程的文章,我当时内心是小鹿乱撞啊-于是这几天茶不思饭不想,好几天深夜皆是辗转反侧,两目深凝,以至于这几天走起路来格外飘飘然,左摇右晃的,魔鬼般的步伐, ...

  8. Java 多线程快速入门(面试概念解答一)

    Java 多线程快速入门 什么是进程,什么是线程,什么是多线程? 创建多线程有哪些方式? 启动线程是使用调用start方法还是run方法? 获取线程对象以及名称 守护线程 使用setDaemon(tr ...

  9. Java从入门到精通 第22章 多线程

    Java从入门到精通 第22章 多线程 //单一线程的运行流程 public class Nanjing {public static void main(String[] args) {// TOD ...

最新文章

  1. 使用未编译的XAML动态生成WPF控件
  2. python全栈开发笔记---基本数据类型--数字型魔法
  3. Codeforces Round #476 (Div. 2) B. Battleship
  4. python 接雨水
  5. buu 信息化时代的步伐
  6. 信息抽取(一)机器阅读理解——样本数据处理与Baseline模型搭建训练(2020语言与智能技术竞赛)
  7. PyTorch机器学习从入门到实战-CH2
  8. 加入阿里技术团队三年,哪些习惯让我在工作上持续受益?
  9. c语言long double位数,int long double 所占位数 和最大值
  10. oracle以32位运行,Oracle在 32位系统上运行突破sga1.7g的方法
  11. 习题1083字符转换
  12. 详解图像处理的算术运算与逻辑运算
  13. Android开发学习之电话、短信、联系人
  14. 6.看板方法---价值流映射
  15. JS 字符串全部替换 ,replace (/ /g,'')
  16. 想成为华为hcie网络工程师一定鸦知道的MSDP 基本介绍
  17. 冯大辉(@Fenng):信息真正的意义(图灵访谈)
  18. win10怎么在开机时自动连接拨号上网
  19. [安卓开发] 总结一些android的云测试平台
  20. uni-app 第三方授权登录

热门文章

  1. 霍比特E姐有约|Exploit Network 如何打造 Web3.0 匿名支付协议
  2. python房价预测_python预测房价
  3. Gym - 102920 C - Dessert Café (思维)
  4. 小米r2d做nas_老生常谈, 小米路由器作为轻nas的好处和二级路由器设置.
  5. win10删除Windows更新和预览的版本,关闭自动更新
  6. 操作系统期末总复习——绝地求生版
  7. Arduino + AD9851 DDS信号模块 频率控制字和相位控制字写入
  8. win10快捷方式变成空白图标怎么办?
  9. 格式化数字,逢千位数加逗号
  10. 批量 汉字 转 拼音方法