多线程(一)

一、概述

进程和线程:

   线程可以看作是对进程的再划分,比如eclipse是一个进程,而eclipse提供的各种功能就是一个个小小的线程
    进程应当是独立的应用程序,而线程,同类的进程能够共享同一块内存空间,这样在各个线程之间切换时,负担比进程之间的切换小
    进程为独立运行,而线程之间可能会相互影响,考虑到死锁和线程之间的通信,多线程就是多个线程同时运行

线程和CPU:
    CPU分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
    CPU抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
    CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度非常快,看上去就是在同一时刻运行。
    多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

线程的基本状态:
    创建,创建一个Thread对象,Thread thread = new Thread();
    就绪,执行start()方法,进入就绪状态,等待cpu资源
    执行,执行run()方法的内容,线程只能从就绪状态进入执行
    阻塞,
        1.获取到锁(synchronized,lock()等),未释放
        2.wait()线程等待,notify()唤醒后进入就绪状态,准备运行
        3.执行sleep()、join()等方法
    死亡,线程执行完毕,或异常导致退出

线程安全:在并发的情况下,该代码经过多线程使用,线程的调度顺序不影响任何结果,线程不安全指代码可能会因调度顺序出现问题。

实现多线程的两种方法:

    继承Thread类
    继承Runnable接口

二、多线程操作

case:卖票案例

实现一个多线程卖票的案例,关键的地方在于怎么让多线程共享一份数据(票数)。

看一下Thread类构造方法Thread(Runnable target)的源码:

看关于target参数的说明:target必须实现Runnable接口,具体是指当此线程启动时,run()方法被调用的对象。

所以要实现多个线程共享数据,在new Thread()的时候,必须传入同一个对象。代码如下。

//5个线程卖票
public class TicketDemo2 extends Thread {

	private int ticket = 5;

	// 可以在run()方法上加synchronized关键字
	@Override
	public void run() {
		// 加锁
		synchronized (this) {
			for (int i = 0; i < 10; i++) {
				if (ticket > 0) {
					System.out.println(Thread.currentThread().getName() + "--" + ticket--);
				}
			}
		}
	}
	// 多个线程共享一份数据
	public static void main(String[] args) {
		TicketDemo2 td = new TicketDemo2();
		Thread t1 = new Thread(td, "t1");
		Thread t2 = new Thread(td, "t2");
		Thread t3 = new Thread(td, "t3");
		Thread t4 = new Thread(td, "t4");
		Thread t5 = new Thread(td, "t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}

synchronized关键字:

在上面的卖票案例中,ticket--,的过程如下:
    取得原有ticket值
    计算i -1
    对i进行赋值
这样多个线程执行run()中的操作时,一个线程执行ticket--时,可能还没有执行完,另外一个线程就过来再执行ticket--,这样就导致ticket数量不会递减,因为那个线程执行到ticket--那个步骤并不确定。
加上synchronized关键字之后,当前线程会拿到锁,只有此线程执行完之后释放锁,其他线程才可以继续执行。

可以使用synchronized代码块,为关键步骤加锁,也可以在run()方法上加锁,只是这样会为整个run()方法中的内容都加锁,不如synchronized代码块灵活。

que:start()和run()的区别?

start()在Thread类中,作用是启动一个线程,线程进入就绪状态,此时线程未必会立即执行,要等待获取到CPU资源之后才会执行,run()是Runnable()接口中的唯一方法,覆写后的run()方法中是线程要执行的内容

线程的终止

Java提供了强制中止线程的方法stop(),不过是已弃用Deprecated,很显然强制终止线程会造成数据安全问题。

终止线程需要开发者手动进行终止,利用interuppt()isInterrupted()方法。

interuppt()方法不会真正的打断线程,只会为线程加上一个中断的标记,发者利用终止标记来用break或return等终止线程。

isInterrupted()判断线程是否被终止,此方法不会清除InterruptStatus,isInterrupted(boolean)如果传入参数为ture的话,会清除InterruptStatus。isInterrupted()其实就是isInterrupted(false)。

中断线程的例子:

public class InterruptDemo extends Thread {

	@Override
	public void run() {
			while (true) {
				//判断终止标志
				if (this.isInterrupted()) {
					System.out.println("ֹͣ停止了!");
					//停止run()方法,线程终止
					return;
				}
				System.out.println("timer=" + System.currentTimeMillis());
			}
	}

	public static void main(String[] args) {
		InterruptDemo t=new InterruptDemo();
		t.start();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//设置终止标志
		t.interrupt();
	}
}

接下来进一步讨论下Interrupt()方法的细节:

如果线程由于wait(),join(),sleep()这些方法进入阻塞状态,调用Interrupt()方法会报错InterruptedException,InterruptStatus被清空。

上面大意是说在NIO中线程阻塞时Interrupt()的结果。

刚开始看到是IO,但是又不认识上面那两个类,仔细看才知道是NIO中的类,然后百度发现,原来传统的IO是不支持中断的,如果在IO过程中被阻塞,Interrupt()无效,这样将无法按照上述的方法终止一个线程。

只凭这一点,传统IO中的PipedIO,已经是鸡肋,看来有时间还要去看看NIO。我去,我太难了!

最后,再mark一个setPriority()方法,可设置的三种优先级:

    MAX_PRIORITY    10
    NORM_PRIORITY    5
    MIN_PRIORITY    1

优先级就好像轮询中的权重一样,不是优先级高就一定会先获取到CPU资源,概率大而已。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值