目录
一、进程与线程的区别
二、实现多线程
三、线程 Thread 中常用的API
四、线程的生命周期
五、线程调度
六、守护线程
七、线程安全问题
一、进程与线程的区别 1. 什么是进程? 一个进程对应一个应用程序。例如:在 windows *** 作系统启动 Word 就表示启动了一个 进程。在 java 的开发环境下启动 JVM ,就表示启动了一个进程。现代的计算机都是支持多 进程的,在同一个 *** 作系统中,可以同时启动多个进程。进程与进程之间的内存是独立的 2. 什么是线程? 线程是一个进程中的执行场景。一个进程可以启动多个线程。 3.进程与线程的区别: 每个进程是一个应用程序,都有独立的内存空间 同一个进程中的线程 共享 其进程中的内存和资源 ( 共享的内存是堆内存和方法区内存,栈内存不共享,每个线程有自己的栈 。) 4.多线程有什么作用? 多线程不是为了提高执行速度,而是提高 应用程序 的使用率。 线程和线程共享“堆内存和方法区内存”,栈内存是独立的, 一个线程一个栈 。 可以给现实世界中的人类一种错觉:感觉多个线程在同时并发执行。 二、实现多线程
Java虚拟机的主线程入口是main方法,用户可以自己创建线程,创建多线程的俩种方法:
* 继承 Thread 类
* 实现 Runnable 接口(推荐使用,因为Java只支持单继承)
第一种方法:继承 Thread 类
Thread 类种俩个重要的方法:
*public void run() //继承 Thread 类时,可重写 run 方法
* public void start() // 用来启动多线程
public class Test01 { public static void main(String[] args) { Thread t = new ThreadTest(); //启动线程 t.start(); //这不是启动线程,单纯的调用方法!! t.run(); } } class ThreadTest extends Thread { @Override public void run(){ System.out.println("多线程!!"); } }
start方法的作用是:采用 start 启动线程,在JVM中开辟一个新的栈空间,不是直接调用 run ,start 不是马上执行线程,而是使线程进入就绪 状态,线程的正真执行是由 Java 的线程调度机制完成的
第二种方法:实现 Runnable 接口
Thread是Runnable 的一个实现类,所以Thread中的方法,实现Runnable接口也可以用。
public class Test01 { public static void main(String[] args) { Thread t = new Thread(new ThreadTest()); //启动线程 t.start(); } } class ThreadTest implements Runnable { @Override public void run() { System.out.println("多线程!!"); } }三、线程 Thread 中常用的API
API测试:
public class Test01 { public static void main(String[] args) { //线程1 Thread t = new Thread(new ThreadTest()); //线程2 Thread tt = new Thread(new ThreadTest1()); System.out.println( "线程1的初始名字 = " + t.getName() + ",线程2的初始名字 = " + tt.getName()); t.setName("t1"); tt.setName("t2"); System.out.println( "线程1修改后名字 = " + t.getName() + ",线程2修改后名字 = " + tt.getName()); //获取主线程 Thread thread = Thread.currentThread(); System.out.println("主线程的初始名字 = " + thread.getName()); //启动线程 t.start(); tt.start(); } } //线程1 class ThreadTest implements Runnable { @Override public void run() { } } //线程2 class ThreadTest1 implements Runnable{ @Override public void run() { } }
主线程的初始名字是 main,其他分支线程的初始名字:Thread-0,Thread-1.....
public class Test01 { public static void main(String[] args) { //线程1 Thread t = new Thread(new ThreadTest()); //线程2 Thread tt = new Thread(new ThreadTest1()); //启动线程 t.start(); tt.start(); try { //虽然是用线程1的引用掉用了此方法,但是这并不会让线程1睡眠 3s,sleep写在哪就会让哪个线程休眠 //所以执行到此处,主线程会休眠 3 s t.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是主线程"); } } //线程1 class ThreadTest implements Runnable { @Override public void run() { System.out.println("我是线程1"); } } //线程2 class ThreadTest1 implements Runnable{ @Override public void run() { try { //sleep写在这里会让 线程 2 休眠5s Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是线程2"); } }
public class Test01 { public static void main(String[] args) { //线程1 Thread t = new Thread(new ThreadTest()); //线程2 Thread tt = new Thread(new ThreadTest1()); //启动线程 t.start(); tt.start(); // interrupt 会中断线程2的休眠。但不会终止线程的执行,所以线程2的for语句还会执行。 //interrupt依靠的是 sleep方法 的异常处理机制。 tt.interrupt(); //查询中断状态:true 为中断成功,false 为中断失败 boolean interrupted = tt.isInterrupted(); System.out.println(interrupted); } } //线程1 class ThreadTest implements Runnable { @Override public void run() { } } //线程2 class ThreadTest1 implements Runnable{ @Override public void run() { try { Thread.sleep(50000000); } catch (InterruptedException e) { System.err.println("已经终止睡眠"); } for (int i = 0; i < 10; i++) { System.out.print(i + "t"); } } }四、线程的生命周期
线程的生命周期分为以下五个:
五、线程调度使线程进入阻塞状态的方法:
sleep、wait、join、yield
1、常见的调度模型有哪些?
抢占式调度模型
哪个线程的优先级较高,抢夺CPU时间片的概率就会高,Java采用的就是这种模型
均分式调度模型
平均分配CPU时间片,每个时间占用的时间长度一样
2、线程调度的方法
六、守护线程更改线程的优先级
注意:优先级高低,只是将线程抢夺CPU时间片的概率高一些,并不一定会优先执行
Thread.MIN_PRIORITY:最小优先级1 Thread.MAX_PRIORITY:最大优先级10 Thread.NORM_PRIORITY:默认优先级5yield()方法 暂停当前线程的运行,让给其他线程执行。没有终止线程,只是将当前线程从运行状态转换到就绪状态
join() 方法,合并俩个线程
1、Java中的守护线程分为俩类:
一类是守护线程,一类是用户线程
比如:垃圾回收器就是守护线程,主线程是一个用户线程
2、守护线程的特点
一般是一个死循环,当所有用户线程结束,守护线程自动结束
public class Test2 { public static void main(String[] args) { Thread t = new Thread(new ThreadTest2()); t.setName("守护线程"); //将线程t设置为守护线程 t.setDaemon(true); t.start(); for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程 i-->" + i + "t"); } } } class ThreadTest2 implements Runnable { @Override public void run() { int i = 0; while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("守护线程 i-->" + (++i)); } } }
七、线程安全问题虽然守护线程是个死循环,但是当用户线程结束时,守护线程会自动结束
★★★★★ 1、什么时候数据在多线程开发的时候会遇到安全问题?
多线程并发
共享数据
修改数据
共同存在以上三种行为有可能会引发多线程安全问题
2、同步编程模型和异步编程模型的概念
同步编程模型:假设俩个线程 t1和t2,线程 t1 执行的时候必须等待 t2 执行完才能执行,也就是线程排队机制
异步编程模型:t1,t2 自己执行自己的,谁也不管谁,也就是多线程并发
3、怎么解决线程安全问题
线程排队执行,叫做线程同步机制
4、线程排队机制的语法
synchronized(){ //线程同步代码块 } //synchronized后面这个小括号里面传的这个 “数据” 非常重要, //这个数据必须是多线程共享的数据,才能达到多线程排队。
5、 实现synchronized 的三种方法
第一种方法:
synchronized(){}
第二种方法:
出现在实例方法上,这个同步的是整个方法,效率第,共享的对象只能是 this
public static void synchronized(){}
第三种方法:
出现在类上,无论创建多少个对象,类锁只有一个
6、通过一个例子体现线程安全的重要性以及实现线程同步的用法
设计一个程序,实现简单的银行转账功能。
目的:假设银行账户余额为10000,俩个人同时去银行对同一个账户进行取钱 *** 作,假设每个人取5000,不使用线程同步时,账户余额是否有可能还剩下 5000 ?
public class AccountTest { public static void main(String[] args) { //初始化账户 Account account = new Account(123, 10000); //俩个线程代表俩个用户 Thread t1 = new Thread(new AccountThread(account)); Thread t2 = new Thread(new AccountThread(account)); //用户开始取钱 t1.start(); t2.start(); } } //账户类 class Account { //账号 int no; //余额 double balance; //构造器 public Account(int no, double balance) { this.no = no; this.balance = balance; } //取钱的方法 public void getMoney(double money) { //保证出现实验效果,每个线程休眠1s try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //剩余余额 this.balance = balance - money; } } //一个用户就是一个线程 class AccountThread implements Runnable { //模拟线程安全问题,保证多个线程都共享一个对象 private Account account; public AccountThread(Account account) { this.account = account; } @Override public void run() { double money = 5000; account.getMoney(money); System.out.println("账号 = " + account.no + ", 取走 = " + money + ", 余额 = " + account.balance); } }
实验结果:我们可以看到,当俩个用户取钱时,账户余额本应该剩余 0,但是却剩余 5000,这个时候就出现线程安全问题
解决办法就是使用线程排队机制:每个用户进行取钱 *** 作时,一个执行,另一个等待。
此时,取钱 *** 作就不会出现安全问题了。共享的对象就是 银行账户。也就是 this。
线程排队的原理:
在java中,每个对象都有一把 "锁",100个对象有100把 "锁"。线程 t1 和 t2 共享一个对象,所以只会有一把 "锁",假设 t1 先执行到 synchronized 处,会霸占这把 "锁" ,拿着这把 "锁" 会先执行取钱 *** 作,而当 t2 执行到此处时,由于这把 "锁" 被 t1 霸占,所以 t2 会等着 t1 先执行完取钱 *** 作, t2 才会执行,这样就不会出问题
6、关于Java中的变量,哪种变量永远不会有线程安全问题,哪种变量可能存在安全问题?
Java中的变量:
实例变量【全局变量】:保存在堆内存中
局部变量:保存在栈内存中
静态变量:保存在方法区中
只有局部变量永远不会有线程安全问题,因为在多线程访问变量时,每个线程都有一个栈内存。而实例变量,静态变量可能会存在线程安全问题
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)