-
【JAVA】笔记(19)--- 线程概述;如何实现多线程并发;线程生命周期;Thread常
线程概述:
1.进程:指的是一个应用程序;而线程是进程的一个执行单元,一个进程可以启动多个线程;
2.对于java程序员来说,JAM就是一个进程,线程分为用户线程和守护线程;main方法就是一个用户线程(主线程),垃圾回收线程就是一个守护线程,java程序至少俩个线程并发,一个是垃圾回收线程,一个是main方法的主线程;
3.进程之间的内存是不会共享的,而对于线程而言,堆内存和方法区内存共享,但是栈内存独立,一个线程拥有一个独立的栈;
4.多线程并发:假设启动10个线程,就会有10个栈,各自运行,互不干扰,这就是多线程并发;
实现线程的俩种方式:
1.自定义一个类,直接继承 java . lang . Thread ,并重写Run方法;
创建线程 --- new 线程对象(继承了Thread);
启动线程 --- 调用 线程对象 . start ( ) 方法 // start ( ) 会在JVM中开辟一个新的栈空间;
栗子老师:
public class ThreadPra1 {
public static void main(String[] args) {
//创建分支线程对象(属于主线程代码)
ExtendsThread extendsThread=new ExtendsThread();
//启动分支线程,开辟新的栈空间(属于主线程代码)
extendsThread.start();
//主线程的循环(属于主线程代码)
for (int i=0;i<=100;i++){
System.out.println("主线程执行的代码--->"+i);
}
}
}
//ExtendsThread代表自定义一个继承Thread 的继承类
class ExtendsThread extends Thread{
//分支线程的循环(属于分支线程的代码)
public void run() {
for (int i=0;i<=100;i++){
System.out.println("分支线程执行的代码--->"+i);
}
}
}
运行实例
2.编写一个类,实现 java . lang . Runnable 接口,重写 Run 方法;
创建线程;
启动线程;
public class ThreadPra1 {
public static void main(String[] args) {
//创建线程对象
Thread thread=new Thread(new ImplementsRunnable());
//启动分支线程
thread.start();
}
}
//ImplementsRunnable代表实现了Runnable接口的类
class ImplementsRunnable implements Runnable{
public void run() {
System.out.println("new Thread(new ImplementsRunnable())+重写Run方法--->创建线程(start Thread)");
}
}
运行结果:
--------------------------
new Thread(new ImplementsRunnable())+重写Run方法--->创建线程(start Thread)
Process finished with exit code 0
注意:
1)第二个实现线程的方式更常用一些,因为一个类实现了一个接口的同时,还可以继承其他的类(面向接口编程);
2)通过匿名内部类也可以创建线程对象(实现 Runnable 接口):
public class ThreadPra1 {
public static void main(String[] args) {
//创建线程对象
Thread thread=new Thread(){
@Override
public void run() {
System.out.println("new Thread + 匿名内部类--->创建线程(start Thread)");
}
};
//启动分支线程
thread.start();
}
}
运行结果:
----------------------------
new Thread + 匿名内部类--->创建线程(start Thread)
Process finished with exit code 0
线程生命周期:
Thread 常用方法:
1.String getName ( ) // 返回该线程的名称。
2.void setName(String name) // 改变线程名称
3.void sleep(long millis)
// 使当前线程进入” 阻塞状态 “(休眠),放弃占有CPU时间片,让给其他线程使用(出现在main方法中,就是让主线程进行休眠)
4.void interrupt ( ) // 唤醒休眠线程(异常机理)
5.void join ( ) // 等待该线程终止
6.void run ( ) // 以不启动新线程的方式运行 run ( )
7.Thread . currentThread ( ) // 获取当前线程的引用
终止线程的三种方式:
1.Thread . stop ( )
//下下之选,容易丢失数据
2.Thread . interrupt ( )
//调用 interrupt ( ) 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程;Thread . interrupt ( ) 可以成功的唤醒正在休眠的线程;
3.使用标志位终止进程:
public class ThreadPra1 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread=new MyThread();
Thread thread=new Thread(myThread);
thread.setName("分支线程");
thread.start();
//希望5秒后终止分支线程
Thread.sleep(5000);
myThread.run=false;
}
}
class MyThread implements Runnable {
//打一个布尔标记
boolean run = true;
public void run() {
for (int i = 0; i <= 100; i++) {
if (run) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在执行");
} else{
//在这里可以选择保存一些我们需要保存的数据
return;
}
}
}
}
运行实例
线程安全问题:
1.在多线程并发的环境下,当满足以下三个条件时,会存在线程安全问题:
条件一,多线程并发;
条件二,有共享数据;
条件三:共享数据有修改行为;
2.怎么解决线程安全问题?
线程同步机制 --- 程序排队执行,会牺牲一部分效率;
3.这里会涉及到俩个模型:
1)异步编程模型:线程t1和线程t2,各自执行各自的,其实就是多线程并发(效率较高)
2)同步编程模型:当一个线程正在执行时,其他线程必须等此线程执行完毕才能执行,线程排队执行(效率较低);
线程同步机制:
#就下面这个代码讲讲如何通过 synchronized 实现线程同步:
此程序模拟两台机器对同一个账户进行取款,账户原余额1w,对其进行两次取款,每次5000元,由于多线程并发存在的隐患,可能导致第二次取款后,余额依然是5000
public class ThreadPra1 {
public static void main(String[] args) throws InterruptedException {
Account account=new Account("act-001",10000);
Thread t1=new AccountThread(account);
Thread t2=new AccountThread(account);
t1.start();
t2.start();
}
}
class Account{
private String acton;
private double balance;
public Account(String acton, double balance) {
this.acton = acton;
this.balance = balance;
}
public String getActon() {
return acton;
}
public void setActon(String acton) {
this.acton = acton;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void withdraw(double money){
this.setBalance(balance-money);
}
}
class AccountThread extends Thread{
private Account account;
public AccountThread(Account account) {
this.account = account;
}
public void run() {
account.withdraw(5000);
System.out.println("账户"+account.getActon()+"取款成功,余额:"+account.getBalance());
}
}
synchronized 的三种用法:
1.代码块锁://锁 Account (this)账户,因为此余额为1w的账户为两个线程的共有资源,所以当对账户进行取钱操作时,俩个线程将会排队取款(先拿到锁的先取款)
public void withdraw(double money){
synchronized (this){
this.setBalance(balance-money);
}
}
注意:synchronized 直接锁 AccountThread 中的取款5000代码也能达到同步线程的目的,但是扩大了同步的范围,会使效率降低;
2.实例方法上 synchronized://这种锁属于对象级别的锁,一个对象一把锁,一百个不同的对象就是100把锁
public synchronized void withdraw(double money){
this.setBalance(balance-money);
}
这种方式的优缺点:
缺点:第一,只能锁 this 对象 ;第二,可能会导致无故扩大同步范围,效率降低,所以使用较少;
优点:代码写的少了,节俭了一些,所以如果共享的对象就是this,并且需要同步的代码块就是整个方法体,建议使用这种方式;
3.静态方法上 synchronized:
//这种锁属于类锁,一种类就一把锁,一百个对象也还是一把锁;
#java 中的三种变量,哪种存在线程安全问题?
1)局部变量永远都不会存在线程安全问题,因为局部变量是不共享的(一个线程一一个栈);
2)实例变量在堆中,堆只有1个,所以堆是多线程共享的,导致实例变量可能会存在线程安全问题;
3)静态变量在方法区中,方法区只有1个,所以方法区是多线程共享的,导致静态变量可能会存在线程安全问题;
随笔:
1.main方法结束,有没有可能程序不会结束?
main方法结束只代表主线程结束了,主栈空了,其他的栈,可能还在压栈弹栈,所以程序在main方法结束之后可能不会结束;
2.启动成功的线程会自动调用run方法;run方法在分支栈的底部(最先压栈),main方法在主栈的底部(最先压栈),所以run方法和main方法可以说是同级的;
3.Thread . sleep ( ) 方法的一个面试题:
哪个线程会进入休眠?
package com.bjpowernode.javase.threadTest;
public class ThreadPra1 {
public static void main(String[] args) throws InterruptedException {
Thread thread=new MyThread();
thread.setName("分支");
thread.start();
thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"线程在执行");
}
}
class MyThread extends Thread{
public void run() {
System.out.println(Thread.currentThread().getName()+"线程在执行");
}
}
运行结果:
-----------------------------
分支线程在执行
main线程在执行
Process finished with exit code 0
由结果可以看出, thread . sleep ( 100 ) 使主线程休眠了,得出结论:
sleep ( ) 属于静态方法,调用 引用 . sleep ( ) 相当于 调用 Thread . sleep ( ) ,而 Thread . sleep ( ) 会使当前线程进入休眠,mian方法种的当前线程当然就是main线程,所以最后mian线程进入了休眠;
原文:https://www.cnblogs.com/Burning-youth/p/15640542.html