JAVA advance 多线程

线程

使用场景:多线程可以解决资源耗时 提升效率

进程和线程

进程是一个内存中运行的应用程序,它是系统运行程序的基本单位。

线程是进程中的一个执行单元,负责当前进程执行的一个执行单元,一个进程中至少有一个线程。

JVM支持多线程(如垃圾回收器)
public class Test01_JVM {
	public static void main(String[] args) throws InterruptedException {
		for (int i = 1; i < 1000000; i++) {
			new Test(i);
		}
		// 通知垃圾回收器 需要回收对象(不一定回收)
		System.gc();
		
		// 当前线程睡眠3秒 此时不占用资源
		Thread.sleep(300000); 
		System.err.println("main end...");
	}
}

//main线程休眠3s 
class Test {
	int n;

	public Test(int n) {
		this.n = n;
	}

//当GC进行垃圾回收指定对象的时候,对象的finalize方法会被自动
	@Override
	protected void finalize() throws Throwable {
		System.out.println("Test被销毁, n: " + n);
		super.finalize();
	}
}
可以使用jsconsole命令查看线程情况(在cmd窗口执行);
线程调度策略(并发 单核)
  • 多线程共享同一个cpu的 此时是轮流获取cpu

  • 哪个线程获取了cpu,此时才能运行,没有获取cpu的,就等待

    • 获取到cpu的这段时间,叫时间片

    • 时间片不会被某线程独享,会随机切换分配

  • 线程调度策略(分配时间片方案)

    • 轮流

    • 抢占(jvm)

      • 线程等级高的优先获取时间片,如果等级一样,随机分配

内存分配
  • 每个线程拥有各自的栈空间 互相独立

  • 线程共享堆和方法区

线程的创建方式 (线程的执行顺序不可控)
  • 创建Thread/Thread子类

  • 通过Runnable创建Thread (复用run方法)

  • 通过Callable创建有返回值的线程

    • call方法虽然有返回值,但是会导致阻塞

  • 通过线程池创建

线程的种类
  • 前台线程

    • 如果存在前台线程,jvm不会关闭

    • 默认开启的线程是前台线程,可以手动设置为守护线程

  • 守护线程

线程的方法
  • 静态方法

    • currentThread

  • 普通方法

    • run 线程的执行逻辑 如果直接调用,不是新线程 是main线程

    • start 开启线程,会自动调用run

    • set/getName() 设置、获取线程名字

    • setPriority(10) 设置线程优先级

    • setDaemon(true) 守护线程

    • stop() 停止正在运行的线程(不建议使用 有安全隐患)

==触发线程状态转换的方法==
  • sleep(时间) == TimeUnit.SECONDS.sleep(10);

    • 当前线程阻塞睡眠(timed_waitting),10秒后会自动进入可运行状态(runnable)

    • 期间不获取时间片,无法运行

    • 在匿名内部类中不能抛出异常,只能捕获异常

    • 单位是毫秒

  • join() t1.join

    • 当前线程阻塞无限等待(waitting),等插入的线程执行完进入可运行状态(runnable)

  • join(时间) t1.join 类似sleep

    • 当前线程阻塞有限等待(timed_waitting),等时间过后完进入可运行状态(runnable)

  • wait()

  • notify();

==线程状态变化(线程有五大状态)==
  • NEW

    • 创建线程时

  • RUNNABLE

    • start-》就绪 时间片-》运行

    • sleep时间结束回到就绪状态

    • join插入的线程执行完

    • join时间过后

    • 处于锁阻塞的线程抢到锁后

    • notify/notify();

  • TIMED_WAITTING

    • sleep(时间)

    • join(时间)

  • WAITTING

    • join(); 本质就是调用wait() t1.join()

    • wait();

  • BLOCKED

    • 线程没抢到锁

  • TERMINATED

    • run方法结束

==线程状态==

  • 正常状态 NEW -> RUNNBALE(Runnable(就绪)/Running(运行)) -> TERMINED

  • 阻塞状态

    • timed_waitting 有限等待

    • waitting 无限等待

    • blocked 锁阻塞

线程安全问题 (多线程 并发 共享资源,可能导致读的预期效果不一致)

解决思路: 控制临界资源只能同时被一个线程访问

==安全和效率==

  • 线程安全 效率低 StringBuffer Vector

  • 线程不安全 效率高 StringBuilder ArrayList

兼顾安全和效率(并发专题 无锁机制 cas)
  • 通过工具类将线程不安全的转为线程安全

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * StringBuilder 拼接字符串
 * @author vanse
 *
 */
public class TestStringBuilder {
	public static void main(String[] args) {
		StringBuffer sb1 = new StringBuffer("a");
		// 链式编程 在字符数组自身改变
		StringBuilder sb = new StringBuilder("a");
		String finalS = sb.append("b").append("c").toString();
		System.out.println(finalS);
		
//		s+= "bc"; 字符数组没有提供设置内容的方法
		
		List<String> list = new ArrayList<>();
		// 线程安全的ArrayList
		List<String> synchronizedList = Collections.synchronizedList(list);
	}
}
具体方案 锁机制
  • synchronized jdk提供的默认关键字,可对临界区上锁,保护资源

    • 修饰代码块 同步(原子)代码块

      • 用对象充当锁

      • 用类充当锁

    • 修饰方法 同步方法 只要保证同一个锁对象即可

      • 如果是普通方法 锁对象是当前对象this

      • 如果是静态方法 锁对象是当前类

==注意: 多个线程必须使用同一把锁对象==

线程通信 (解决线程之间以某种规则运行的问题)

生产消费队列模型(mq)

通过等待唤醒机制 Object的方法wait和notify
  • wait() 当前线程进入无限等待状态,除非调用同一个锁对象的唤醒方法

    • 会释放锁

    • 可能存在虚假唤醒的问题

  • notify() 将等待池中的某个线程唤醒

  • notfifyAll() 唤醒等待池中的所有线程唤醒

这两个方法都需要在同步资源中使用,并且需要用锁对象调用。

线程学习案例1:

证明共享同一个对象调用方法不加锁
第二个线程不需要等待线程一释放锁,哪怕他先执行;

public class Demo01 {
	public static void main(String[] args) throws InterruptedException {
		Myclass mc=new Myclass();
	    Thread t1=new MyThread(mc);
	    Thread t2=new MyThread(mc);
	    t1.setName("虎子");
	    t2.setName("子虎");
	    t1.start();
	    Thread.sleep(100);//让主程序先睡会保证t1线程先执行;
	    t2.start();
	}
}

class MyThread extends Thread{
	private Myclass mc;
	public MyThread(Myclass mc) {
		this.mc=mc;
	}
	@Override
	public void run() {
		if("虎子".equals(Thread.currentThread().getName())) {
			mc.dosome();
		}else {
			mc.doOther();
		}
	}
	
}
class Myclass{
	public synchronized void dosome() {
		System.out.println("DSbegin");
		try {
			Thread.sleep(1000*3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("DSend");
	}
	
	public void doOther() {
		System.out.println("DObegin");
		System.out.println("DOend");
	}
}
线程案例2:

证明共享同一个对象调用方法都加锁
第二个线程需要等待线程一释放锁,线程一先执行;

public class Demo02 {
	public static void main(String[] args) throws InterruptedException {
		Myclass1 mc=new Myclass1();
	    Thread t1=new MyThread1(mc);
	    Thread t2=new MyThread1(mc);
	    t1.setName("虎子");
	    t2.setName("子虎");
	    t1.start();
	    Thread.sleep(100);//让主程序先睡会保证t1线程先执行;
	    t2.start();
	}
}

class MyThread1 extends Thread{
	private Myclass1 mc;
	public MyThread1(Myclass1 mc) {
		this.mc=mc;
	}
	@Override
	public void run() {
		if("虎子".equals(Thread.currentThread().getName())) {
			mc.dosome();
		}else {
			mc.doOther();
		}
	}
	
}
class Myclass1{
	public synchronized void dosome() {
		System.out.println("DSbegin");
		try {
			Thread.sleep(1000*3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("DSend");
	}
	
	public synchronized void doOther() {
		System.out.println("DObegin");
		System.out.println("DOend");
	}
}
线程案例3:

证明共享对象有两个(两把锁)调用方法都加锁
  第二个线程需要等待线程一释放锁嘛?线程一先执行;
 不需要,找各自的锁,有100个共享对象就会有100把锁

public class Demo03 {
	public static void main(String[] args) throws InterruptedException {
		Myclass2 mc1=new Myclass2();
		Myclass2 mc2=new Myclass2();
	    Thread t1=new MyThread2(mc1);
	    Thread t2=new MyThread2(mc2);
	    t1.setName("虎子");
	    t2.setName("子虎");
	    t1.start();
	    Thread.sleep(100);//让主程序先睡会保证t1线程先执行;
	    t2.start();
	}
}

class MyThread2 extends Thread{

	private Myclass2 mc;
	public MyThread2(Myclass2 mc) {
		this.mc=mc;
	}
	@Override
	public void run() {
		if("虎子".equals(Thread.currentThread().getName())) {
			mc.dosome();
		}else {
			mc.doOther();
		}
	}
	
}
class Myclass2{
	public synchronized void dosome() {
		System.out.println("DSbegin");
		try {
			Thread.sleep(1000*3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("DSend");
	}
	
	public  synchronized void doOther() {
		System.out.println("DObegin");
		System.out.println("DOend");
	}
}
线程案例4:

证明共享对象有两个(两把锁)调用静态方法都加锁(此时加的是类锁)
哪怕有100个对象但是也是同一个类创建的那就会占用
第二个线程需要等待线程一释放锁嘛?线程一先执行;
需要,找同一个类锁,有100个共享对象只有1把类锁(Myclass)

public class Demo04 {
	public static void main(String[] args) throws InterruptedException {
		Myclass4 mc1=new Myclass4();
		Myclass4 mc2=new Myclass4();
	    Thread t1=new MyThread4(mc1);
	    Thread t2=new MyThread4(mc2);
	    t1.setName("虎子");
	    t2.setName("子虎");
	    t1.start();
	    Thread.sleep(100);//让主程序先睡会保证t1线程先执行;
	    t2.start();
	}
}

class MyThread4 extends Thread{

	private Myclass4 mc;
	public MyThread4(Myclass4 mc) {
		this.mc=mc;
	}
	@Override
	public void run() {
		if("虎子".equals(Thread.currentThread().getName())) {
			mc.dosome();
		}else {
			mc.doOther();
		}
	}
	
}
class Myclass4{
	public synchronized static void dosome() {
		System.out.println("DSbegin");
		try {
			Thread.sleep(1000*3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("DSend");
	}
	
	public  synchronized static void doOther() {
		System.out.println("DObegin");
		System.out.println("DOend");
	}
}

线程安全案例

账户资产信息安全

账户类:
public class Account {

	private String aName;//账户信息
	private int money;//账户余额
	
	public Account(String aName, int money) {	
		this.aName = aName;
		this.money = money;
	}
	public Account() {}
	public String getaName() {
		return aName;
	}
	public void setaName(String aName) {
		this.aName = aName;
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
	//获取到账户余额
	//多线程并发执行造成取钱不稳定,取完还能取
	public void  getAccount(int gMoney) {
		//账户取出前:
		int before=this.getMoney();
		//账户取出后:
		int after=before-gMoney;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.setMoney(after);
     
	}
}
账户提取资产线程类:
public class AccountThread  implements Runnable {
	private Account at1;
	public AccountThread(Account at1) {
		this.at1=at1;
	}
	@Override
	public void run() {
		//两个栈,两个线程
		int money=5000;
		at1.getAccount(money);
		System.out.println(Thread.currentThread().getName()+"取钱:"
		+money+"元  账户还剩下"+at1.getMoney()+"元");
	}
}
账户初始设定(启动)类:
public class Test {
	public static void main(String[] args) {
		Account act=new Account("ada--1",10000);
	    AccountThread t = new AccountThread(act);
		Thread t1 = new Thread(t,"小祝");
		Thread t2 = new Thread(t,"小鹏");
		t1.start();
		t2.start();
	}
}

线程状态:

 线程生命周期图片

多线程并发隐患图片

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值