多线程基础
注:File file = new File("a.txt");不是指这个文件或者文件夹,而是引用。 一 : 进程和线程?? 1.1 :进程? ? ?? ? 指的是一个动态过程,就是代码从加载到执行完毕的过程 ??????? 特点: ??????????????? 1)独立性:每个进程是相互独立的,互不干涉 ??????????????? 2)动态性:每个进程是一直活动的 ??????????????? 3)并发性:多个进程在单个CPU上是并发进行的 ?? 1.2:线程?????????????? 线程是进程的一部分,一个进程可以有多个线程,每个线程取执行特定的任务 ????????????? 1)线程是抢占式的,多个线程共用一个CPU,其实就是 CPU快速切换 ?? 1.3 :两者关系???????????? 1)线程是进程的一部分; ???????????? 2)进程是不资源共享的,线程是资源共享 ???????????? 3)一个进程至少有一个线程 二:实现多进程的三种方法? 2.1)继承Thread?? 实现Runnable? 继承Callable ??????? 1.继承Thread:继承自Thread类,Thread类是所有线程类的父类,实现了对线程的抽取和封装继承Thread类创建并启动多线程的步骤: ????????????????????????????????? 3主线程是JVM自动创建的 ? 代码示例一 1 public class Demo1 { 2 public static void main(String[] args) { 3 /**因为Mythread是继承Thread,当然肯定继承了Thread的属性name, 4 * 但是没有继承他的构造方法,所以不能用构造方法是指线程名字 5 * thread.setName("11"); 6 * 设置优先级 7 * thread.setPriority(5); 8 * 设置是否后台运行 9 * thread.setDaemon(true); 10 */ 11 MyThread thread = new MyThread(); 12 thread.start(); 13 for (int i = 0; i < 10; i++) { 14 System.out.println("正在执行主线程"+i); 15 } 16 } 17 } 18 public class MyThread extends Thread{ 19 20 @Override 21 public void run() { 22 for (int i = 0; i < 10; i++) { 23 System.out.println("正在执行子线程"+i); 24 } 25 } 26 } ? ???????????? 注:获取线程名字两个方法:1:Thread.currentThread.getName();这个在哪都可以准确定位目前线程名字 ? 2:thread.getName(); ????????? 代码示例二(经典卖票) public class Demo2 { public static void main(String[] args) { /** * 有构造方法 * public Ticket(String name) { * super(name);} * 所以可以用构造方法定义线程名 */ Ticket ticket1 = new Ticket("窗口一"); Ticket ticket2 = new Ticket("窗口一"); Ticket ticket3 = new Ticket("窗口一"); Ticket ticket4 = new Ticket("窗口一"); ticket1.start(); ticket2.start(); ticket3.start(); ticket4.start(); } } public class Ticket extends Thread { int ticket = 100; public Ticket(String name) { super(name); } @Override public void run() { while (true){ if (ticket < 1)break; System.out.println(Thread.currentThread().getName()+"正在售卖第"+ticket+"张票"); ticket--; } } } 内存图解析:因为Ticket类是创建出来的,每次创建一个对象w1,w2就会进入栈,相应的堆内存也会分配空间,有各自属性int Ticket = 100,但是w1 优势各自独立的线程,所以它们又会自己创建一个栈,都有各自的栈内存 所以出现总共有400张票,每个线程栈空间是独立的,堆空间是共享的。 ????? 2? 实现Runnable接口: ?????????????????????? 步骤? : a.定义一个Runnable接口的实现类,并重写该接口中的run方法,该run方法的方法体同样是该线程的线程执行体 ??? 代码案例(案例:模拟售票员售票:四个窗口共卖100张票) ??? public class Demo3 { public static void main(String[] args) { Ticket ticket = new Ticket(); Thread w1 = new Thread(ticket,"w1"); Thread w2 = new Thread(ticket,"w2"); Thread w3 = new Thread(ticket,"w3"); Thread w4 = new Thread(ticket,"w4"); w1.start(); w2.start(); w3.start(); w4.start(); } } public class Ticket implements Runnable{ int ticket = 100; @Override public void run() { while (true){ if (ticket < 1)break; System.out.println(Thread.currentThread().getName()+"正在售卖第"+ticket+"张票"); ticket--; } } } ? ???????????????? 代码&源码分析 /** * 我们先看源码Run()方法 * @Override * public void run() { * if (target != null) { * target.run(); * } * } * run方法中有一个属性是 "target" 尤其重要的属性,它是为何四个线程公用100张票的原因 * 在看源码: * private Runnable target; * target 是Runnale类型的 * 源码416行 * this.target = target; * 这个target就是传过来的目标类,也就是票类 * 再看源码 Thread构造函数 * public Thread(Runnable target,String name) { * init(null,target,name,0); * } * Thread构造函数调用的方法init() * private void init(ThreadGroup g,Runnable target,String name,* long stackSize,AccessControlContext acc,* boolean inheritThreadLocals) { * String name就是线程名字,target就是Ticket类,四个线程共用一个target对,即共同消费属性 int ticket=100; * */ 分析内存图:main方法的Ticket先加载,在栈中创建Ticket引用“ticket”,在堆开辟内存空间,有属性 int ticket = 100;Thread在栈中压入引用,堆中分配内存空间属性target,四个引用都有相应的堆空间,target放入的就是目标类对象 target的地址,即都指向堆空间的target实行int target= 100;四个线程消费也都是消费同一个target ? 案例二(你和你女朋友公用一张银行卡,你向银行卡中存钱、你女朋友取钱!) ? public class Demo1 { public static void main(String[] args) { BankCard card = new BankCard(); AddMoney addMoney = new AddMoney(card); SubMoney subMoney = new SubMoney(card); Thread w1 = new Thread(addMoney); Thread w2 = new Thread(subMoney); w1.start(); w2.start(); } } public class BankCard { //银行卡里有钱 private int money; public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } public class AddMoney implements Runnable { //因为需要car所以私有化bankcard private BankCard card; public AddMoney(BankCard card) { this.card = card; } @Override public void run() { for (int i = 0; i < 10; i++) { card.setMoney(card.getMoney()+1000); System.out.println("增加1000,余额"+card.getMoney()); } } } public class SubMoney implements Runnable { //因为需要car所以私有化bankcard private BankCard card; public SubMoney(BankCard card) { this.card = card; } @Override public void run() { for (int i = 0; i < 10; i++) { card.setMoney(card.getMoney()-1000); System.out.println("钱少了,余额"+card.getMoney()); } } } 三 : 两种方式的比较 ????????? 1 继承Thread类的方式 ??????????????????? 【问题】调用start()? 和 run()区别? ?????????????????????? 当调用start()方法时将创建新的线程,并且执行run()方法里的代码,但是如果直接调用run()方法,不会创建新的线程。 ???? 3)第三种? 实现? Callable接口import java.util.concurrent.FutureTask; /** * 1.有返回值 * 2.需要借助FutureTask */ public class Demo1 { public static void main(String[] args)throws Exception { myCallable callable = new myCallable(); FutureTask<Integer> task = new FutureTask<Integer>(callable); Thread thread = new Thread(task); thread.start(); Integer integer = task.get(); System.out.println(integer); } } import java.util.concurrent.Callable; public class myCallable implements Callable<Integer> { int sum = 0; @Override public Integer call() throws Exception { for (int i = 1; i <=100 ; i++) { sum +=i; } return sum; } }
? ???????? 3.1)获取设置线程名字 ???????? ? ? ? ? ? 设置: 1.使用构造方法 ??????????? ?? ? ????????????? 2.set.name(""); ?????????????????? 获取:1.thread.getName(); ????????????????????????????? 2.Thread.currentThread.getName(); ?????? ??????????????????? 使得当前正在执行的线程休眠一段时间,释放时间片,导致线程进入阻塞状态 ?????? 3.3)设置线程优先级 ?????????????? 可以通过设置优先级来改变线程抢到时间片的概率,优先级高的线程获得较多的执行机会。
public class JoinThread extends Thread{ @Override public void run() { for(int i=0;i<50;i++) { System.out.println(Thread.currentThread().getName()+"....."+i); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static void main(String[] args) throws Exception { //1创建线程对象 JoinThread joinThread=new JoinThread(); //2启动 joinThread.start(); //加入线程(阻塞了主线程,等join执行完完毕才继续执行) joinThread.join(); //3for for(int i=0;i<30;i++) { System.out.println("主线程--------------"+i); Thread.sleep(20); } } ? ?
? ? ? ? ? ? ? ? ? public class DeamonThread extends Thread { @Override public void run() { for(int i=0;i<100;i++) { System.out.println(Thread.currentThread().getName()+"...."+i); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static void main(String[] args) throws Exception{ //1创建线程对象 DeamonThread deamonThread=new DeamonThread(); //设置线程为后台线程 deamonThread.setDaemon(true); //2启动线程 deamonThread.start(); for(int i=0;i<20;i++) { System.out.println("主线程:"+i); Thread.sleep(20); } } ? 3.6)线程让步(虚伪的让步) ????????? ?? 可以让当前正在执行的线程暂停,但它不会阻塞该线程,他只是将该线程转入就绪状态,完全可能出现的情况是:当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。 ? ? ? ? ? ? ? ? ? ?? 实际上,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程才会获得执行的机会。 public class YieldFunctionDemo01 { public static void main(String[] args) { YieldThread t0 = new YieldThread("线程000"); //t0.setPriority(8); t0.start(); YieldThread t1 = new YieldThread("线程111"); t1.start(); } } class YieldThread extends Thread { public YieldThread(){} public YieldThread(String name) { super(name); } @Override public void run() { for(int i = 0;i < 50;i++) { System.out.println(Thread.currentThread().getName() + " " + i); if(i==20) { //线程让步,不会让线程进入阻塞状态 Thread.yield(); } } } } ? 6)声明周期五个状态?? ?新生--->就绪---->运行---->阻塞----->死亡 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |