java学习day18~19---多线程

一.线程和进程(单核)
    进程:CPU中有多个程序“同时”在使用,CPU在一个时间点上只能执行一件事情,而CPU分片段执行不同程序,
            但是切换速率十分快,给人感觉是在同时使用
          进程的作用不是提高执行速度,而是提高CPU的使用率
    线程:一个进程中的执行场景,一个进程有多个线程,如淘宝抢购,都在使用百度搜索
            线程的作用不是提高执行速度,而是提高应用程序的使用率

    线程的存在:
    线程之间的堆内存和方法区内存是共享的,但不同线程都是不同的栈内存,
二.线程的创建和启动
方式1:子类继承Thread,重写里面的run方法,调用start方法启动(也可以不用子类,直接new一个Thread,用匿名内部类的方式)
注意:run方法不能抛出异常

public class ThreadWork
{
    public static void main(String[] args) {
        Thread t = new Processer();
        t.start();   //这个代码告诉JVM给t线程分配一个新的栈,启动之后自动调用run方法
        for(int i = 0; i < 100; i++) {
            System.out.println("main---" + i);
        }
    }
}

class Processer extends Thread
{
    public void run() {
        for(int i = 0; i < 100; i++) {
            System.out.println("thread---" + i);
        }
    }
}


//匿名内部类的方式

public class ThreadWork
{
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run() {
                for(int i = 0; i < 100; i++) {
                    System.out.println("thread---" + i);
                }
            }
        };

        t.start();
        for(int i = 0; i < 100; i++) {
            System.out.println("main---" + i);
        }
    }
}

//拉姆达表达式简化后

public class ThreadWork
{
    public static void main(String[] args) {
        new Thread(() -> {
            for(int i = 0; i < 100; i++) {
                System.out.println("thread---" + i);
            }
        }).start();

        for(int i = 0; i < 100; i++) {
            System.out.println("main---" + i);
        }
    }
}

//运行结果:两个循环随机交互执行
//结论:main方法结束之后只是主线程结束,但是其他线程还有栈帧,所以main方法结束,程序可能还在运行
//注意:如果只调用run方法,那么这就只是普通的方法调用,整个程序只有一个主线程,等到run方法执行结束之后,才会执行main方法中的循环

方式2:子类继承Runnable接口,实现里面的run方法(推荐使用)

public class ThreadWork
{
    public static void main(String[] args) {
        Thread t = new Thread(new Processer());   //Thread t = new Thread(Runnable接口);
        t.start();   //这个代码告诉JVM给t线程分配一个新的栈,启动之后自动调用run方法
        for(int i = 0; i < 100; i++) {
            System.out.println("main---" + i);
        }
    }
}

class Processer implements Runnable
{
    public void run() {
        for(int i = 0; i < 100; i++) {
            System.out.println("thread---" + i);
        }
    }
}

三.线程的生命周期

进入就绪状态之后,线程有权利去抢夺CPU时间片,这个时间片就是执行权,抢到之后就可以进入运行状态,当时间
用完而运行还没有结束之后,就会继续返回到就绪状态,继续抢夺时间片,然后继续接着执行run方法,直至方法执行完毕

用第一个创建线程的例子来讲,得到的结果是两个循环交替输出,这就是因为两个线程抢夺时间片,先抢到的先执行,
抢到的时间不够返回来继续抢夺执行,直至线程结束

四.常用的方法

getName();   获取线程名字
currentThread();  获取当前的线程
sleep();   让当前线程休眠一段指定时间,阻塞线程,让CPU腾出时间片,让其他线程执行,是一个静态方法
getPriority();   获取线程优先级
join();  等待某个线程执行结束
interrupt() 可以打断正在等待的线程(包括sleep, join的等待)
yield()      让位,给同一个优先级的线程让位,但是让位时间不固定,也是静态方法

不推荐使用的方法
stop() 让线程停止
suspend() 让线程暂停
resume() 让线程继续


sleep()详解

public class ThreadWork
{
    public static void main(String[] args) throws Exception{
        Thread t = new Thread(() -> {
            for(int i = 0; i < 30; i++) {
                System.out.println(Thread.currentThread().getName() + "---" + i);
            }
        });
        
        t.setName("t1");    //将线程命名为t1
        t.start();   //让线程运行
        t.sleep(5000);  //此时调用sleep方法会让t线程休眠5秒吗   

        System.out.println("hello");
    }
}

解析:不会,因为sleep是静态方法,与对象无关,t.start()相当于是Thread.start(), 而Thread当前是主线程
       所以t线程不会休眠,而是主线程休眠

//interrupt()详解

public class ThreadWork
{
    public static void main(String[] args) throws Exception{
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1000000000000L);  //很长时间段的休眠
            }
            catch (Exception e) {
                e.printStackTrace();
            }
            
            for(int i = 0; i < 30; i++) {
                System.out.println("thread" + i);
            }
        });
        
        t.start();   //让线程运行
        Thread.sleep(5000);  //主线程休眠五秒
        t.interrupt();  //五秒后打断t线程的休眠

        System.out.println("hello");
    }
}

//结果:五秒后t线程执行语句,因为休眠被打断
五.线程同步
同步编程模型:t1和t2线程必须等待一个执行完毕之后,再执行另一个
异步编程模型:t1和t2线程各自执行各自的,谁也不等谁引入线程同步的原因:为了数据安全
使用线程同步的条件:
    1.多线程环境    2.共享一个数据        3.共享的数据涉及到修改操作

public class ThreadWork
{
    public static void main(String[] args) {
        Account a = new Account("张三", 5000);
        Thread t1 = new Thread(new Processor(a));
        Thread t2 = new Thread(new Processor(a));
        t1.start();
        t2.start();
    }
}

class Processor implements Runnable
{
    Account a;

    public Processor(Account a) {
        this.a = a;
    }
    public void run() {
        a.withdraw(200);
        System.out.println("取了200,还剩" + a.getBalance());
    }
}

//账户类
class Account
{
    private String actno;
    private int balance;

    public Account(String actno, int balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public String getActno() {
        return actno;
    }

    public void setBalance(int balance) {
        this.balance = balance; 
    }

    public int getBalance() {
        return balance;
    }

    //提供一个取款方法
    public void withdraw(int money) {
        int after = balance - money;
        this.setBalance(after);
    }
}

//运行结果:随机输出4600,4800
//因为两个线程交互了,一个取款完毕之后,还没有更新,第二个有可能也已经执行,所以余额有问题

此时需要两个线程分别执行,引入线程同步

//其他代码不变,修改取款操作

public void withdraw(int money) {   
    synchronized(this) {   //用锁将取款的具体操作锁住
        try{
            Thread.sleep(1000);
        } catch(Exception e) {
        }
        int after = balance - money;
        this.setBalance(after);
    }
}

用synchronized(this)锁住,那么程序执行的时候,就回去寻找this对象锁,找到则执行,执行完毕归还对象锁,否则等待,直到获取对象锁

六.类锁
关于类锁:类锁只有一把,且与对象无关

虽然t1和t2是两个不同的线程,但是公用一个myClass对象,而该类中的方法用的是类锁,类锁只有一把,与对象没有关系

public class ThreadWork {
	public static void main(String[] args) throws Exception {
		myClass mc = new myClass();
		Processor p1 = new Processor(mc);
		Processor p2 = new Processor(mc);

		Thread t1 = new Thread(p1);
		Thread t2 = new Thread(p2);
		t1.setName("t1");
		t2.setName("t2");

		t1.start();
		Thread.sleep(1000); //为了保证t1线程先执行
		t2.start();
	}
}

class Processor implements Runnable {
	myClass mc;
	Processor(myClass mc) {
		this.mc = mc;
	} 
	public void run() {
		if(Thread.currentThread().getName().equals("t1")) {
			mc.m1();
		}
		if(Thread.currentThread().getName().equals("t1")) {
			mc.m2();
		}
	}
}

class myClass
{
	public synchronized static void m1() {
		try{
			Thread.sleep(5000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("m1...");
	}
	
	public synchronized static void m2() {
		System.out.println("m2...");
	}
}

七.死锁

两个线程互相锁住,如两个线程t1,t2,两个对象o1,o2,t1线程先锁住o1对象,然后锁o2对象,t2线程先锁o2对象,再锁o1对象,但是因为两个线程谁都不肯先放,所以进入了僵持状态,形成死锁

public class ThreadWork 
{
	public static void main(String[] args) {
		Object o1 = new Object();
		Object o2 = new Object();

		Thread t1 = new Thread(new T1(o1, o2));
		Thread t2 = new Thread(new T2(o1, o2));

		t1.start();
		t2.start();
	}
}

class T1 implements Runnable
{
	Object o1;
	Object o2;
	T1(Object o1, Object o2) {
		this.o1 = o1;
		this.o2 = o2;
	}

	public void run() {
		synchronized(o1) {
			try {
				Thread.sleep(1000);
			} catch(Exception e) {
					e.printStackTrace();
				}
			synchronized(o2) {}
		}
	}
}

class T2 implements Runnable
{
	Object o1;
	Object o2;
	T2(Object o1, Object o2) {
		this.o1 = o1;
		this.o2 = o2;
	}

	public void run() {
		synchronized(o2) {
			try {
				Thread.sleep(1000);
			} catch(Exception e) {
					e.printStackTrace();
				}
			synchronized(o1) {}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/szy2333/article/details/81877328
今日推荐