Java_线程,Timer,线程组以及同步的方法

进程和线程:
一个程序就是一个进程,可以打开资源管理器查看现在有多少个进程同时运行。
计算机是支持多进程的。
多线程的作用:
以游戏为例(需要一个线程来控制主角的移动,一个线程控制敌人的AI攻击行为),
当需要多个任务同时运行时,就要使用多线程。
主线程:
main方法处于一个默认的线程中,这个线程称为主线程,由系统默认创建出来(JVM创建启动)。

创建额外的线程:

public class MyThread extends Thread{   //额外的线程类

@Override
public void run() { //线程的start()会自动调用该线程的run()
	for(int i = 1;i<=500;i++)
		System.out.println("MyThread :"+i);
}
}

在运行的主线程中开启额外的线程:

public static void main(String[] args) {
	
	MyThread mt = new MyThread();
	mt.start();//right,调用线程类的run()会开启一个线程,同时启动run()
//	mt.run();//error,相当于调用一个普通方法,不开线程
	
	for(int i = 1;i<=500;i++)
		System.out.println("主线程 :"+i);
	
}

Thread.currentThread() 可以返回当前方法所处的线程,可以用该方法获取main线程。

线程调度规则(线程调度会整体上是遵守下面的规则,但是从单个上来看是随机的)
1、分时调度(平均分配)
2、抢占式调度(按照优先级)—>Java使用的调度规则
优先级高的,有更高的机率被CPU所执行

Thread的常用方法:
.getPriority() 获取当前线程对象的优先级
.setPriority() 设置当前线程对象的优先级,不在下述优先级会抛出异常
NORM_PRIORITY–>5,MAX_PRIORITY–>10,MIN_PRIORITY–>1

创建线程对象时系统会自动分配name,规则为:
Thread-0,Thread-1…若为主线程,name为main
除非在new Thread(String name)时改名,否则分配的name会始终占位。
可使用.setName(),.getName()更改和获取线程对象的线程名。

线程休眠,让当前线程休眠
Thread.sleep(); 使用Thread调用则使当前所处的线程休眠x毫秒,不使用对象进行调用!!
.join();等待该线程终止。若该对象线程被其他线程中断则抛出异常。
.setDaemon(true)在start之前将该线程对象设置为守护线程
如果程序中只剩下守护线程在运行,那么程序会停止。
.interrupt() 让线程处于中断状态,使用static boolean interrupted()配合使用判断当前线程是否处于中断状态,并调整,释放资源再终止自身的运行。

二、使用Runnable创建线程:
a)实现Runnable接口
b)实现run方法
c)创建当前类的对象和Thread的对象,并使用Thread对象的.start()启动

其他
a)获取当前线程(implements Runnable的Runnable)的名字
	Thread.currentThread().getName()
	设置名字通过Thread对象
b)构造方法
	Thread t = new Thread(Runnable target);
	Thread t = new Thread(Runnable target,String name);

使用implements Runnable较extends Thread创建线程的好处:
1、更方便进行数据的共享
2、方便后续继续继承其他类

三、使用匿名内部类的方式创建线程:
在某一方法中使用匿名内部类:

Runnable r = new Runnable() {
		@Override
		public void run() {
			for (int i = 1; i <= 50; i++)
				System.out.println(Thread.currentThread().getName() + ": " + i);
		}

	};
	new Thread(r, "线程1").start();

	new Thread("线程2") {
		public void run() {
			for (int i = 1; i <= 50; i++)
				System.out.println(getName() + ": " + i);
		}
	}.start();

线程安全问题:!!!!!
CPU在某一时间只能执行某个线程的一句代码(以;结尾为一句代码)。
最重要的一点:
【线程start()后,运行线程的run方法体,只有方法体中的循环语句体会被重复执行,循环语句体外的语句只会被执行一次!】
如,在4个站点同时售票的问题:

//4个站点Thread共用的Runnable
/*仅当4个Thread都使用同一个Runnable r对象构建时(因为这时4个Thread执行时需要运行的是
  共同的几句关键代码),可以使用下述synchronized上锁来解决重复售票问题!!*/

private int ticket = 100;
//钥匙s必须是Runnable仅有的一把钥匙(不在run()中声明)
private String s = new String();

public void run() {
	while (ticket > 0) {
		synchronized (s) {
			if (ticket > 0) {
			
			    /*下面两句代码如果不使用synchronized上锁,可能会出现多站点
			      重复售出第100张票。【也可以不对这两句代码上锁,而是将ticket--进行输出,
			      (在cpu运行的这一时间点同时完成两个任务)。】*/
			      
				System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
				ticket--;
			}
		}//在此处将钥匙s归还,并由于处于循环体中,每次卖出后进行sleep()
	
	
	try {
		System.out.println(Thread.currentThread().getName()+"等待了一次");
		Thread.sleep(100);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	}//while ticket>0

}// run

线程安全问题:
多个线程同时要修改一个变量的时候,引起冲突.
线程安全问题解决

synchronized(Object key){
		//被上锁的代码行,只有拿到key的线程可以运行
		}
		/*synchronized代码块运行结束后自动归还key,
		使key可以被 其他等待拿key的(执行上锁代码块的)线程 争夺。*/

对synchronized的深入理解:
在方法体内synchronized(Object key){ xxx代码行 },key要为多个线程所共有的对象且不能空指向 【在Thread中static可使某对象变为多Thread对象的共有对象】才可以锁住 xxx代码行 ,使其在某一时间段只被一个线程执行。 Object key也可为this—>指当前类对象。
synchronized也可修饰方法,使该方法直接被上锁。
【前述:
线程安全的类 :
安全: StringBuffer Vector
不安全:StringBuilder ArrayList
其之所以线程安全,就是因为其方法被synchronized修饰,也导致其效率相当较慢。】

第二种解决线程安全的方法(同步锁的第二种使用方式):
a)创建锁对象 ReentrantLock lock = new ReentrantLock();
b)加锁和解锁
使用tryfinally
lock.lock();
lock.unlock();
注意事项:ReentrantLock对象 lock要为多个线程所共有的。
如果lock()与unlock()之间上锁的代码行运行异常,则会导致无法解锁其他线程持续等待。所以要对上锁的代码行try包裹,并finally包裹unlock()。

死锁:
死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

线程组ThreadGroup
默认创建的线程处于同一个组里面。
使用线程组可以统一设置这个组内线程的一些东西。比如设置最大优先级,中断其中的所有线程…
可以在创建线程时:Thread(ThreadGroup group, Runnable target, String name)将线程归组。

特殊的线程 Timer
Timer是安排在后台由于执行任务的线程,TimerTask使其要及执行的任务。

public class TimerDemo {

public static void main(String[] args) {
	
	Timer ti = new Timer();//创建一个Timer线程
	
	ti.schedule(new timerT(), 2000); //线程要执行的任务,延迟2000毫秒执行
	ti.schedule(new timerT(), 2000,2000);//延迟2000毫秒执行后,每隔3秒再执行一次
	//timer.schedule(TimerTask task, Date time)  -->在某一时间定时执行
	ti.cancel();//线程取消
}
}
class timerT extends TimerTask{
@Override
public void run() {
	System.out.println("定时执行的任务");
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章