Java多线程
一、什么是线程
1、进程的概念
进程是指可执行程序并存放在计算机存储器的一个指令序列,它是一个动态执行的过程、
2、线程的概念
线程是比进程还要小的运行单位,一个进程包含多个线程
二、创建线程
1、通过Thred类创建线程
class Mythread extend thread{
public void(){
System.out.println(getName()+"该线程正在执行");
}
}
public class ThreadTest{
public static void main(String[] args){
Mythread mythred = new Mythread();
mythred.start();
}
}
2、实现Runnable接口创建线程
public class Mythred02{
public static void main(String[] args){
Dog dog = new Dog();
Thread t = new Thread(dog);
t.start;
}
}
class dog implemnets Runnable{
int count = 0;
public void run(){
while(true){
System.out.println((count++)+Thread.currentThred().getName());
//休眠1秒
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
if(count){
break;
}
}
}
}
三、多线程实例
public class Mythred02{
public static void main(String[] args){
T1 t1 = new T1();
T2 t1 = new T2();
Thread t00 = new Thread(t1);
Thread t01 = new Thread(t2);
t00.start;
t01.start;
}
}
class T1 implemnets Runnable{
int count = 0;
public void run(){
while(true){
System.out.println((count++)+Thread.currentThred().getName());
//休眠1秒
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
if(count==10){
break;
}
}
}
}
class T2 implemnets Runnable{
int count = 0;
public void run(){
while(true){
System.out.println((count++)+Thread.currentThred().getName());
//休眠1秒
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
if(count==5){
break;
}
}
}
}
四、通知线程退出
需求:启动一个线程t,要求在main线程中去停止线程t
public class Mythred02 throws EXception{
public static void main(String[] args){
T t = new T();
t.start;
System.out.println("main线程休眠10秒");
Thread,sleep(10*1000);
t.setLoop(false);
}
}
class T extends Thread{
private int count = 0;
private boolean loop = true;
public void run(){
while(loop){
System.out.println((count++)+Thread.currentThred().getName());
}
}
public void setLoop(boolean loop){
this.loop = loop;
}
}
五、线程中断
1、线程常用方法
第一组
1.setName //设置线程名称,使之与参数name相同
2.getName //返回该线程的名称
3.start //开始线程
4.run //调用线程对象run方法
5.setPriority //线程线程优先级
6.getPriority //获得线程优先级
7.sleep //正在执行的线程进入休眠(暂停执行)
8.interrupt //中断线程
第二组
1、yield:线程的礼让,让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。
2、join:线程插队,插队的线程一旦插队成功,则肯定先执行完插入的线程的所有任务
案例:
main线程创建一个子线程,每隔1秒输出hello,输出20次,主线程每隔1秒,输出hi,输出20次,要求:两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续。
public class Mythred02 throws EXception{
public static void main(String[] args){
T t = new T();
t.start;
for(int i=1;i<=20;i++){
Thread.sleep(1000);
System.out.println("主线程执行"+i+"次");
System.out.println("子线程插队");
t.join();
}
}
class T extends Thread{
public void run(){
for(int i=1;i<=20;i++ ){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("子线程执行"+i+"次");
}
}
}
用户线程和守护线程
- 用户线程:也叫工作线程,当线程的任务执行完后或通知方式结束
- 守护线程:一般是为了工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程:垃圾回收机制
public class Mythred02 throws EXception{
public static void main(String[] args){
T t = new T();
t.setDaemon(true);
t.start;
System.out.println("子线程为守护线程");
for(int i=1;i<=20;i++){
Thread.sleep(1000);
System.out.println("主线程执行"+i+"次");
}
}
class T extends Thread{
public void run(){
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("子线程执行"+i+"次");
}
}
}
2、注意事项和细节
1、start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
2、线程优先级的范围
3、interrupt,中断线程,但并没有真正的中断线程,所以一般用于中断正在休眠线程
4、sleep,线程的静态方法,使当前线程休眠。
六、线程的七大状态
NEw:创建
Ready:就绪
Running:运行
TimedWaiting:休眠
Waiting:等待
Blocked:锁
Teminated:终止
七、线程同步机制
Synchronized
线程同步机制:
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性
- 也可以理解为:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
同步具体方法-Synchronized
同步代码块
synchronized(对象){ //得到对象的锁,才能操作同步代码
//需要被同步代码
}
synchronized还可以放在方法声明中,表示整个方法为同步方法
public sychronized void m(String name){
//需要被同步代码块
}
案例
public class TestSell{
public static void main(String []args){
Thread01 t= new Thread01;
new Thread1(t).start();
new Thread2(t).start();
new Thread3(t).start();
}
}
class Thread01 implements Runnable(){
private int ticketNum = 50;
private boolean loop = true;
public synchronized void sell(){ //同步方法
if(ticketNum <=0){
System.out.println("售票结束");
loop = false;
}
try{
Thred.sleep(50);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(Thread.getCurruentThread()+"售出一张票")
}
public void run(){
while(loop){
sell();
}
}
}
互斥锁
- 在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象都应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只有由一个线程来访问该对象
- 关键字synchronized来与对象的互斥锁进行联系,当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问该对象。
- 同步的局限性:导致程序的执行效率要降低
- 同步方法(非静态的)锁可以是this,也可以是其他对象(要求是同一个对象)
- 同步方法(静态的)为当前类本身,锁是加在类名.class
案例
public class TestSell{
public static void main(String []args){
Thread01 t= new Thread01;
new Thread1(t).start();
new Thread2(t).start();
new Thread3(t).start();
}
}
class Thread01 implements Runnable(){
private int ticketNum = 50;
private boolean loop = true;
public void sell(){
sychronized(this){ //同步代码块
if(ticketNum <=0){
System.out.println("售票结束");
loop = false;
}
try{
Thred.sleep(50);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(Thread.getCurruentThread()+"售出一张票")
}
}
public void run(){
while(loop){
sell();
}
}
}
注意事项和细节
- 同步方法如果没有使用static修饰,默认锁对象为:this
- 如果同步方法使用static,默认锁对象为:当前类.class
- 实现的落地步骤:
需要先分析上锁的代码
选择同步代码块或同步方法
要求多个线程的锁对象为同一个即可
线程死锁
多个线程都占用了对方的资源,但不肯相让,导致了死锁,在编程时一定要避免死锁的发生。
public class TestLock{
public static void main(String []args){
DeadLockDemo d1 = new DeadLockDemo(true);
DeadLockDemo d2 =new DeadLockDemo(false);
d1.start;
d2.start;
}
}
class DeadLockDemo extends Thred{
static Object o1 = new Object();
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag){
this.flag = flag;
}
public void run(){
//业务逻辑分析
//1.如果flag为T,线程A就会得到o1对象锁,然后去尝试去获取o2对象锁
//2.如果A线程得不到o2对象锁,就会blocked
//3.如果flag为F,线程A就会得到o2对象锁,然后去尝试去获取o1对象锁
//4.如果A线程得不到o1对象锁,就会blocked
if(flag){
synchronized(o1){
System.out.println(Thread.currentThred.getName()+"进入1");
synchronized(o2){
System.out.println(Thread.currentThred.getName()+"进入2");
}
}
}else{
synchronized(o2){
System.out.println(Thread.currentThred.getName()+"进入3");
synchronized(o1){
System.out.println(Thread.currentThred.getName()+"进入4");
}
}
}
}
}