【学习笔记】线程基础

本文介绍了程序、进程和线程的概念,以及并发和并行的区别。通过示例展示了如何在Java中通过继承Thread类和实现Runnable接口创建线程,并演示了线程并发执行可能导致的问题——线程安全。文章通过售票场景解释了线程同步的重要性,并给出了使用synchronized关键字实现线程同步的方法。
摘要由CSDN通过智能技术生成

线程学习笔记

一些相关概念

程序

程序:完成特定任务,用某种编程语言所写的一组指令的集合。

进程

进程:进程是正在运行的程序,有自己的产生、存在和消亡的过程。

线程

线程:由程序创建的、是进程的一个实体,一个进程可以有多个线程。

并发与并行

并发

并发:同一时刻,多个任务交替执行。即,一个处理器同时处理多个任务。

并行

并行:同一个时刻,多个任务同时执行。即,多个处理器处理同时处理多个不同的任务。

创建一个线程

创建线程的两种方法

可以通过继承实现了Runnable接口的Thread类实现,也可以直接实现Runnable接口实现。
在这里插入图片描述

Runnable接口提供的方法

在这里插入图片描述

场景描述

创建一个线程,每秒打印你好,打印八次后退出。

继承Thread类
继承Thread类实现代码:
public class Thread01 extends Thread {
	private int count = 0;
	private boolean loop = true;  //设置循环条件

	public void setLoop(boolean loop) {
		this.loop = loop;
	}

	@Override
	public void run() {
		while (loop){
			sleep( 1000 ); //延时1000ms
			sayHi(); 			//打印hi
			breakThread();		//线程跳出程序
		}
	}

	public void sayHi(){
		System.out.println("你好	"+"这是由"+Thread.currentThread().getName()+"创建的"+ (++count) );
	}

	public void sleep(int time){
		try {
			Thread.sleep( time );
		} catch (InterruptedException e) {
			throw new RuntimeException( e );
		}

	}

	public void breakThread(){
		if( count >= 8)
			this.loop = false;

	}

}
开启创建的线程
public class ThreadTest {
	public static void main (String args[]){

		//thread01 由继承Thread方法实现
		Thread01 thread01 = new Thread01();
		thread01.start();
}
控制台输出

在这里插入图片描述

实现Runnable接口

由于java是单继承机制,当前类继承了其他父类时,就无法继承Thread类,所以需要实现Runnable接口实现线程

实现Runnable接口的代码

public class Thread02 extends Person implements Runnable {
	private int count = 0;
	private boolean loop = true;
	@Override
	public void run() {
		while (loop){
			sleep( 1000 );
			sayHello();
			breakThread();
		}
	}
	public void sayHello(){
		System.out.println("hello "+"这是由"+Thread.currentThread().getName()+"创建的"+ (++count) );
	}

	public void sleep(int time){
		try {
			Thread.sleep( time );
		} catch (InterruptedException e) {
			throw new RuntimeException( e );
		}

	}

	public void breakThread(){
		if( count >= 8)
			this.loop = false;

	}
}

//Thread02类的父类
class Person{

}

开启创建的线程
public class ThreadTest {
	public static void main (String args[]){
		//thread02 由实现Runnable接口实现
		Thread thread02 = new Thread(new Thread02() );
		thread02.start();
	}
}
控制台输出

在这里插入图片描述

两种方式比较

1、实现Runnable接口方法可以实现资源复用,可以如下开启多个线程,但共享一个资源

		//创建一个共享的资源
		Thread02 resourse = new Thread02();
		//创建三个线程,共享一个资源
		Thread thread1 = new Thread(resourse);
		Thread thread2 = new Thread(resourse);
		Thread thread3 = new Thread(resourse);
		//开启线程
		thread1.start();
		thread2.start();
		thread3.start();

2、如果当前类继承了其他父类就不能继承了Thread类,所以只能通过实现Runnable接口来实现多线程

run()方法和start()方法

run方法由Runnable接口提供,但是当我们直接调用run()方法时,只是普通调用一个方法,并没有开启线程。

		//thread02 由实现Runnable接口实现
		Thread thread02 = new Thread(new Thread02() );
		//直接调用run()方法
		thread02.run();
		
输出结果

在这里插入图片描述
可以和前两次输出结果对比,这里打印的线程名是主线程名,并未开启一个新的线程。

start()方法由Thread类提供,并最终执行start0()方法

官方源码如下:

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
     private native void start0();

用售票方法引出线程安全问题

有三个售票站点,共计25张票,每次售票后打印剩余票数,当票数小于0时,停止售票。

用Runnable接口类模拟售票

模拟售票代码
public class SaleTicket implements Runnable {
	private int num = 25; //25张票
	private boolean loop = true; // 循环控制

	public void setLoop(boolean loop) {
		this.loop = loop;
	}

	//把业务代码放到run中
	@Override
	public void run() {
		while (loop){
			sell(); //卖票业务
			delayTime();//延时1s
		}

	}

	public void sell(){

			if(num > 0)
				System.out.println(Thread.currentThread().getName()+"售票完成,当前票数" + (--num));
			else {
				System.out.println(Thread.currentThread().getName()+"售票失败,当前票数	"+num);
				setLoop( false );
			}
		}



	public void delayTime(){
		try {
			Thread.sleep( 1000 );
		} catch (InterruptedException e) {
			throw new RuntimeException( e );
		}
	}

}

开启三个线程,共享一个资源
public class ThreadTest {
	public static void main (String args[]){
			//注定不安全
		SaleTicket saleTicket = new SaleTicket();
		Thread sell01 = new Thread(saleTicket,"御坂01号贩卖机");
		Thread sell02 = new Thread(saleTicket,"御坂02号贩卖机");
		Thread sell03 = new Thread(saleTicket,"御坂03号贩卖机");

		sell01.start();
		sell02.start();
		sell03.start();
	}
}
结果:

在这里插入图片描述

存在问题

存在0票且贩卖成功的情况,这是因为在线程01执行--num 前,02线程就执行了if(num > 0) ,且在线程01执行 后执行了自己的售票代码--num,所以出现了负票的情况。

解决方法:使用关键字synchronized实现线程同步

方法1:在核心方法上加入synchronized关键字实现线程同步
public synchronized void sell(){

			if(num > 0)
				System.out.println(Thread.currentThread().getName()+"售票完成,当前票数" + (--num));
			else {
				System.out.println(Thread.currentThread().getName()+"售票失败,当前票数	"+num);
				setLoop( false );
			}
		}
结果:

在这里插入图片描述
没有出现负票的情况。

方法2 在代码块上使用synchronized关键字
	public /*synchronized */void sell(){
	
	//this可以替换成任意当前共有的类。
			synchronized (this){

				if(num > 0)
					System.out.println(Thread.currentThread().getName()+"售票完成,当前票数" + (--num));
				else {
					System.out.println(Thread.currentThread().getName()+"售票失败,当前票数	"+num);
					setLoop( false );
				}
			}
		}

结果同上。

注意事项

1、同步方法如果没有static修饰,则默认锁对象为this
2、同步方法如果有static修饰,则默认锁对象为当前类.class
3、当某个对象用synchronized修饰时,在同一时刻只能由一个线程访问该对象

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值