第十七章【线程、同步】

一、线程

1、线程原理

内存原理:线程开启后,都会有一个自己独立的栈内存空间,进行压栈和弹栈。
每个线程中运行的方法其局部变量都是独立的,不能共享。如果数据保存在堆中,那么此时堆中的数据是可以给其他线程给共享

2、Thread类

构造方法:
public Thread():分配一个新的线程对象。–用于继承Thread
public Thread(String name):分配一个指定名字的新的线程对象。–用于继承Thread
public Thread(Runnable target):分配一个带有指定目标新的线程对象。–用于创建实现Runnable接口的对象
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字–用于创建实现Runnable接口的对象
常用方法:
public String getName():获取当前线程名称。
public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run():此线程要执行的任务在此处定义代码。
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread():返回对当前正在执行的线程对象的引用。

3、线程的创建方式之一:继承Thread

线程的两种创建方式:
1)使用子类继承Thread,创建线程
2)实现Runnable接口,创建其对象,进行直接创建Thread对象
public Thread(String name)的使用,自定义线程名字

public static void main(String[] args) {
        //Thread(String name)创建对象
        MyThread mt3 = new MyThread("小强");
        mt3.start();//结果是:小强线程执行起来
public class MyThread extends Thread {
    public MyThread(){
        super(); // public Thread()
    }
    public MyThread(String name) {
        super(name);/// public Thread(String name)
    }
    @Override
    public void run() {
        //如果没有给线程起名:默认 Thread-x
        String name = getName(); //获取线程的名字
        System.out.println(name +"线程执行起来");
    }
}

综上可知,继承创建线程方法下,自定义线程名字时:需要子类中构建空参和name参数

4、线程的创建方式之二:实现Runnable接口

一般使用步骤:
1)创建实现Runnable接口的子类,重写run方法
2)构造实现Runnable接口的子类的实例,此实例作为Thread的target来创建Thread的对象,此时的对象才是真正的线程对象
3)调用线程的start方法开启线程

//1. public Thread(Runnable target):分配一个带有指定目标新的线程对象。
//2. public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
	public static void main(String[] args) {
		Runnable runnable = new MyRunnable();
		Thread mth1 = new Thread(runnable);
		mth1.start();//输出结果:Thread-0线程执行run方法【匿名内部类实现】
		Thread mth2 = new Thread(runnable, "旺财");
        mth2.start();}//输出结果:旺财线程执行run方法【匿名内部类实现】
//匿名内部类方法的实现:
        Runnable target = new Runnable() {
            @Override
            public void run() {
                Thread thread = Thread.currentThread();
                String name = thread.getName();
                System.out.println(name+"线程执行run方法【匿名内部类实现】");}};
        new Thread(target).start();
        new Thread(target,"旺财").start();
public class MyRunnable(){
	@Override
	public void run() {
	Thread thread = Thread.currentThread();
	String name = thread.getName();
	System.out.println(name+"线程执行run方法【匿名内部类实现】");
}}

:Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法

5、Thread和Runnable的区别

实现Runnable接口比继承Thread类所具有的的优势为:
1、适合多个相同的程序代码的线程去共享同一个资源,如:可重复调用(接口中的成员变量存在堆中,可以重复调用,类的成员变量不可以重复调用)
2、可以避免java中的单继承的局限性
3、增加程序的健壮性,实现解耦操作(耦合容易出错,解耦不容易出错),代码可以被多个线程共享,代码和线程独立
4、线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类

二、线程安全

结合Runnable使用,因其拥有可共享的堆资源,可使安全方法顺利使用(多次调用下可以上锁使其按照顺序“睡觉时间“排队操作)从而实现安全操作,但类中没有共享的堆资源,每一个都独立存在,故而不能多次按照顺序调用,只能同时调用同时运行,存在隐患。

1、同步代码块:

synchronized(同步锁){
     需要同步操作的代码
}

锁对象 可以是任意类型。多个线程对象 要使用同一把锁。

public class TicketSafe1 implements Runnable {
    int ticketNum = 100;//在空间中共享
    Object lock = new Object();
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Object lock = new Object(); //不能方放在这里
            //同步锁,把需要共同操作资源的代码给包裹起来
            synchronized (lock) {
                if (ticketNum > 0) {
                    String name = Thread.currentThread().getName();
                    System.out.println(name + ticketNum);
                    ticketNum--;}}}}}

2、同步方法

使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等

public synchronized void method(){
   	可能会产生线程安全问题的代码
}
public class Ticket2Safe2 implements Runnable {
    int ticketNum = 100;
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sell();}} //卖票功能
    private synchronized void sell() {  //同步方法,锁是this
            if (ticketNum > 0) {
                String name = Thread.currentThread().getName();
                System.out.println(name + ticketNum);
                ticketNum--;
            }}}

3、Lock锁

public void lock():加同步锁。
public void unlock():释放同步锁

public class Ticket implements Runnable{
	private int ticket = 100;
	Lock lock = new ReentrantLock();
	@Override
	public void run() {
		while(true){	//每个窗口卖票的操作、窗口永远开启
			lock.lock();// synchronized(){
			if(ticket>0){//有票 可以卖
				try {	//出票操作、使用sleep模拟一下出票时间
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				String name = Thread.currentThread().getName();
				System.out.println(name+"正在卖:"+ticket--);//获取当前线程对象的名字
			}
			lock.unlock();}}

三、线程状态

线程状态导致状态发生条件
NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。
RUNNABLE(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器(cpu)。
BLOCKED(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
WAITING(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TIMED_WAITING(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Object.wait。
Terminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值