JAVA学习笔记整理八(多线程)

程序、进程、线程

1.    程序:计算机指令的集合,以文件的形式存储在磁盘上

2.    进程:是程序的一次动态执行过程,经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程

3.    线程:是进程中的一个单一的连续控制流程,多线程值一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行


    多线程:线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程)。抢占式调度模型就是许多线程处于可以运行状态(等待状态),但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态(等待状态),或者另一个具有更高优先级的线程变成可运行状态。在后一种情况下,低优先级的线程被高优先级的线程抢占,高优先级的线程获得运行的机会。

    JAVA中所有线程都是同时启动的,哪个线程先抢到了CPU资源,哪个就先运行

    优点:多线程机制可以同时运行多个程序块,使程序运行的效率变得更高,克服一些传统程序语言无法解决的问题(例如有些包含循环的线程可能需要好一段时间来运算,此时便可以让另一个线程来做其他的处理)

线程的实现

1.    继承Thread类,并覆盖run()方法

public class ThreadDemo1 {
	public static void main(String[] args) {
		test1();
		System.out.println("主线程执行的方法");
	}

	private static void test1() {
		Thread thread1 = new MyThread1("线程A");
		thread1.start();//启动线程,执行run方法
		Thread thread2 = new MyThread1("线程B");
		thread2.start();
	}
}
class MyThread1 extends Thread{
private String name;
	public MyThread1(String name) {
		super();
		this.name = name;
	}

	@Override
	public void run() {
		System.out.println("这是线程" + name + "执行的方法");
	}
}

//其中 一种可能为(谁先抢到CPU则谁先执行):
//主线程执行的方法
//这是线程线程B执行的方法
//这是线程线程A执行的方法

为何启动线程使用start()方法而不是直接调用覆写后的run()方法?

public class ThreadDemo2{
	public static void main(String[] args) {
		test1();
		System.out.println("主线程执行的方法");
	}

	private static void test1() {
		Thread thread1 = new MyThread1();
		// thread1.start();//启动线程,执行run方法
		thread1.run();// 没有启动线程,只是在主线程调用run方法,该方法执行完成后才会继续主线程的运行
	}
}

class MyThread1 extends Thread {
	@Override
	public void run() {
		System.out.println("这是线程执行的方法");
		while (true) {

		}
	}
}
//结果:这是线程执行的方法

→1).没有了多线程的特性,只会和传统程序语言一样按照顺序进行执行,当前一个线程进入死循环时,则永远无法开始下一个线程

→2).使用start()方法时,该线程只能调用一次,否则会抛出异常,但直接调用run()方法的话,则会多次调用

2.    实现Runnable接口(必须依靠Thread类才可以启动多线程)

public class ThreadTest {
	public static void main(String[] args) {
		test1();
		for (int i = 0; i < 5; i++) {
			System.out.println("这是主线程:" + i);
		}
	}

	private static void test1() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 5; i++) {
					System.out.println("这是匿名类Runable:" + i);
				}
			}
		}).start();;
		
		MyThread1 thread1 = new MyThread1();//实例化Runnable子类对象
		Thread thread = new Thread(thread1);//实例化Thread类对象
		thread.start();
	}
}
class MyThread1 implements Runnable {
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("这是Runnable方法:" + i);
		}
	}
}

//这是匿名类Runable:0
//这是匿名类Runable:1
//这是匿名类Runable:2
//这是匿名类Runable:3
//这是匿名类Runable:4
//这是主线程:0
//这是主线程:1
//这是主线程:2
//这是主线程:3
//这是主线程:4
//这是Runnable方法:0
//这是Runnable方法:1
//这是Runnable方法:2
//这是Runnable方法:3
//这是Runnable方法:4

Thread类和Runnable接口的联系与区别

1.    联系:在Thread类中的run()方法调用的是Runnable接口中的run()方法,也就是说此方法是由Runnable子类完成的,所以说如果继承Thread类实现多线程,必须覆写run()方法。

2.    区别(实现Runnable接口相对于继承Thread类来说,有以下优势)

     1)   避免由于JAVA的单继承性所带来的局限

     2)   增加了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的

     3)   适合多个相同代码的线程去处理统一资源的情况

public class ThreadShare {
	public static void main(String[] args) {
		Thread thread = new MyThread();
		thread.start();
		Thread thread1 = new MyThread();
		thread1.start();
	}
}

class MyThread extends Thread {
	private int ticket = 3;
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if (ticket > 0) {
				System.out.println("还有" + (ticket--) + "张余票");
			}
		}
	}
}
//还有3张余票
//还有2张余票
//还有1张余票
//还有3张余票
//还有2张余票
//还有1张余票

    当使用Thread类实现多线程的时候,程序中启动了2个线程,但是2个线程却分别卖出了各自的3张票,没有达到资源共享的目的。

    而Runnable则可以达到这一目的:

public class RunnableShare {
	public static void main(String[] args) {
		MyRunnable my = new MyRunnable();//实例化Runnable子类
		new Thread(my).start();//启动3个线程
		new Thread(my).start();
		new Thread(my).start();
	}
}

class MyRunnable implements Runnable{
	private int ticket = 3;
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if (ticket > 0) {
				System.out.println("还有" + (ticket--) + "张余票");
			}
		}
	}
}
//还有3张余票
//还有2张余票
//还有1张余票

线程的状态

 

1.    创建状态

在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存和其他资源,但还处于不可运行状态。

2.    就绪状态

调用start()方法后就开始启动线程,线程进入就绪状态,但并不是马上就开始运行,而是进入一种就绪状态,此时它已经具备了运行条件,只不过在线程队列里排队,等待CPU服务。

3.    运行状态

当处在就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态,此时,自动调用该线程的run()方法。

4.    阻塞状态


5.    终止状态

线程调用stop()方法或者run()方法执行结束后,即处于死亡状态,不具有继续运行的能力。

线程的一些其他方法

1.    取得和设置线程名称

public class ThreadOtherMethod1 {
	public static void main(String[] args) {
		MyThread11 my = new MyThread11();//定义Runnable子类对象
		new Thread(my).start();//系统自动设置线程名称
		new Thread(my,"线程A").start();//手工设置线程名称
		new Thread(my,"线程B").start();
		new Thread(my).start();
	}
}

class MyThread11 implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 3; i++) {
			System.out.println(Thread.currentThread().getName() + "正在运行第" + i
					+ "次");//取得当前运行线程的名称
		}
	}
}
//线程A正在运行第0次
//线程A正在运行第1次
//线程A正在运行第2次
//Thread-1正在运行第0次
//Thread-1正在运行第1次
//Thread-1正在运行第2次
//Thread-0正在运行第0次
//Thread-0正在运行第1次
//Thread-0正在运行第2次
//线程B正在运行第0次
//线程B正在运行第1次
//线程B正在运行第2次


2.    .isAlive()---------------线程是否启动

3.    .jion()-------------------线程强制运行,运行期间其他线程无法运行,只能等待此线程完成之后才可以继续执行,需要进行异常处理

4.    .sleep(time)----------------线程进行休眠,需要进行异常处理

5.    .interrupt()-----------------中断运行,可中断休眠等(唤醒:.notify()、notifyAll())


6.    setDaemon(boolean)--------------后台线程(守护),当仅有守护线程存在时,结束线程(设置为在后台运行,控制台则不会再运行),一般用于死循环等,需要在start()之前

7.    setPriority(Thread.xxx)---------------设置优先级,优先级越高,则越有可能被优先执行(并不完全一定,只是可能性大,只会先运行,但是执行顺序还是由CPU的调度决定)


8.    .yield()--------------线程礼让,让其他线程先执行,其他线程执行完了自己再继续执行

同步与死锁

1.    同步:指多个操作在同一个时段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行(避免一些因为例如延迟之类所导致的异常,例如卖票票数出现负数情况)

同步代码块: Synchronized(同步对象(一般this)){需要同步的代码块}

同步方法:public synchronized void sale(){代码}

2.    死锁:两个程序都在等待彼此先完成,造成程序停滞

3.    关于同步与死锁:多个线程共享同一资源时需要进行同步,以保证资源的完整性,但是过多同步有可能会造成死锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值