java线程

1.每一个分支就是一个线程,main()方法是主分支也叫做主线程.

2进程:进程其实是一个静态的概念,一个.exe文件,一个.class文件
都叫做进程,把它们放入代码区,等待执行,执行正在准备期间叫做
进程.
那么进程的执行是这么回事呢?
进程的执行指的是进程里面的主方法也就是main()方法开始执行
了,进程是一个静态的概念,在我们机器上运行的都是线程.
windows,linux,unix都是支持多进程.多线程的,但是dos是只支持
单进程的.

3创建新的线程有两种办法
		->要new出来一个新的继承了Thread类的对象才可以的.
		->要new出来一个新的实现了runnable接口的对象也行.
		其中runnable接口只有一个方法就是run(); 
		
		实际上Thread类本身也是实现了runnable接口,否则它里
		面就不会有run()这个方法了.
		
在run()方法里面些什么代码就执行什么代码.
一个的对象调用run()方法不叫做"开启线程,而叫做方法调用"
而对象调用Start()方法才叫做开启线程.为什么你呢?
因为当一个对象调用run()方法的时候,它并没有通知cpu说我这边
已经有了一个新的线程出来了,它只是按照main()方法的顺序,在轮
到他执行的时候去执行就是了,跟普通的类实现了接口之后被main()
方法调用时一样一样的,但是呢如果一个对象它有Thread类来创建
的话那结果就不一样了,当这个对象调用了.Start()方法之后它就会
通知cpu告诉它我已经有了一个新的线程出来了,什么时候有时间
就分配给我点来执行我的程序,所以这个线程就会在某些时候被执行
当然这也就是cpu工作的原理,在,某些事件调用一些线程.



public class TestRunnableAndThread {
	public static void main(String[] args) {
		f t1=new f();
		Thread t2=new Thread(t1);
		//t1.run();
		t2.start();
		for(int i=0;i<=100;i++){
			System.out.println("main:\t"+i);
		}
		/*
		 * 如果单独的运行t1.run();而不去运行
		 * t2.start()的话,结果就是先把run()里面
		 * 的全部打印完了再去打印后面main()方法
		 * 里面的,而如果说是把t1.run()给注释掉了
		 * 的话,那结果就是什么呢?就是这样的:
		 *  main:	34
		 *	main:	35
		 *  main:	36
		 *	Runnable:	0
		 *	Runnable:	1
		 *	Runnable:	2
		 *	Runnable:	3
		 *	Runnable:	4
		 *	Runnable:	5
		 *	Runnable:	6
		 *	Runnable:	7
		 *	Runnable:	8
		 *这个结果就说明在调用了一个继承了Thread类的
		 *start()方法之后,它就是一个新的线程,而且这个
		 *线程会在cpu有空的时候就会被执行.
		 */
	}
}

class f implements Runnable {

	@Override
	public void run() {
		for(int i=0;i<100;i++){
			System.out.println("Runnable:\t"+i);
		}
	}
	
}



public class TestRunnableAndThread {
	public static void main(String[] args) {
//		f1 t=new f1();
//		t.start();
//		t.display();
		/*
		 * 通过这个程序可以发现,其实对,象的.start()
		 * 方法也是调用的对象的重写过的run()方法,
		 * 当然jdk里面也已经写明了是调用的run()方法
		 * 就是说不管你调用的是对象的run()方法,还是
		 * 对象的.start()方法,结果都是调用的对象的
		 * run()方法,当然区别在于,调用run方法只需要
		 *实现了runnable方法即可,但是要调用start()
		 *方法就要继承Thread类才行,知道了吧,还有一点
		 *比较有趣的就是,看下面的实例:就是尽管调用了
		 *tt1.start(),但是它调用的方法最终跟
		 *tt1.run()的结果是一样的,这时因为在
		 * 	Thread tt1=new Thread(tt);一步上实际上
		 * tt1它是作为一个已经继承了Thread类的实例来
		 * 出现的,而它所调用的run()方法要么是在继承
		 * Thread类的时候重写的run()方法,要么是在实行
		 * runnable接口时重写的方法,两个有一个就好了
		 * 当然这个地方在继承Thread类的时候没有重写它
		 * 的run()方法.
		 * 如果class f extends Thread implements Runnable 
		 * 这个样子来写的话,那么结果就是什么呢?
		 * 就是你就不用去重写run()方法了,为什么呢?
		 * 因为你本身已经继承了Thread,而Thread已经实行了runnable
		 * 接口,重写run()方法里面什么都没有罢了.
		 * 你不可能把继承Thread和实现runnable分开写,并且分别重写
		 * run()方法,那样的话就相当于出现了两个一样的类了,类就重名了.
		 * 
		 *当然如果这个类本身就是继承了Thread类的类的话,那就不要去new
		 *一个Thread的了,因为你本身就是Thread,只需要实例化对象就可以
		 *了.
		 */
		f tt=new f();
		Thread tt1=new Thread(tt);
		tt1.start();
		tt1.run();
		
	}
}

class f implements Runnable {

	@Override
	public void run() {
		for(int i=0;i<100;i++){
			System.out.println("Runnable:\t"+i);
		}
	}
	
}

class f1 extends Thread {
	public void display(){
		System.out.println("Extend Thread");
	}
	
}


那么在新建线程的时候既可以用extends Thread
也可以implements Runnable,到底用哪个呢?
记住这个原则,能够用implements Runnable来实
现的就不要用extends Thread,因为你继承了Thread
就继承不了其他的类了,而你实现了Runnable之后还
可以去实现其他的接口,比较灵活.


cpu就像是一个大厕所,你的线程虽然调用了,但是我里面
的坑已经满了,你就是start了也不会对你进行执行的.
老师讲什么都拿厕所举例子.start()方法的调用只能说明
这个线程已经准备好了,但是下一步要做的不一定就是让你
进厕所了,你很可能要去排队的.

start()准备去厕所,就绪排队调度就是

你要去厕所:
start()(准备好了)-> 就绪(排队)->调度(进厕所)->运行(开始
办事)->导致阻塞的事(没手纸了)->阻塞状态(等待手纸)->
阻塞解除(手纸来了)->进行下一个循环. 

当然排队的时候还是有优先级的,比如你的县长跟你排在一个队
里面,他比你来的晚,里面现在正好有一个位置可以去,但是县长
优先级比你高,所以县长先进你后进

sleep会抛异常,就相当于一个人正在睡着,你给他身上泼了一盆
冷水,它自然会抛异常了. 
在那个线程里面调用的sleep()放过就让哪个线程睡眠.

stop()的方法现在已经废弃了,它比interrupt还粗暴,interrupt相
当于是给人泼盆冷水,而stop()就相当于是一棍子打死了,永远都
不会执行了.

run()方法结束,线程就结束了.看例子:



public class TestSleep {
	public static void main(String[] args) {
		T t=new T();
		t.start();
		
	}
	
}
class T extends Thread {
	boolean flag=true;
	@Override
	public void run(){
		while(flag){
			System.out.println(new Date());
			try {
				sleep(1000);
				/*
				 * 注意这个地方,对于重写的这个
				 * run()方法,对于异常的捕捉只能
				 * 够使用try{}cattch(){}
				 * 为什么呢?
				 * 因为作为run()方法是被重写的方法
				 * 所以作为重写的方法,不能够抛出比
				 * 自己父类更大的错误,也不允许抛出
				 * 不同的异常.
				 */
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}



join()方法,就是把某个线程跟当前的线程给合并在一起
看例子:


public class TestJoin {
	public static void main(String[] args) {
		j jj=new j();
		jj.start();
		try {
			jj.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		/*
		 * 如果把上面的join()方法给注释掉的话,结果就是
		 * 两个线程main()线程和jj线程交替的打印,如果是
		 * 把注释拿掉的话,结果就是先打印jj线程的东西,之
		 * 后才会去打印main()线程里面的东西,因为它们已经
		 * 合并成为了一个线程,它必须要按照顺序来执行了.
		 */
		for(int i=0;i<100;i++){
			System.out.println("main Thread");
		}
	}
}

class j extends Thread {
	
	
	@Override
	public void run(){
		for(int i=0;i<100;i++){
		
				System.out.println("join Thread"+i);
				try {
					sleep(1);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			
		}
	}
}

要注意的都是:只有调用了.start()方法的才叫做开启了新线程,
只是调用了.run()方法是不算作新线程的,因为它不用通知cpu,
它只会按照自己位置等待被执行就可以了.



yield(),方法就相当于于是让给别人来办事,但是就只是让了一下
而已,并不是我不再去执行了,我还没完,我还要执行,只是我先让一
下,sleep()和yield()方法都是对于当前的线程来说的.所以不需要
有对象来调用它,对象就是默认的当前对象.
看例子:


public class TestYield {
	public static void main(String[] args) {
		display d1=new display("线程1");
		display d2=new display("线程2");
		d1.start();
		d2.start();
	}
}

class display extends Thread {
	public display(String s){
		super(s);
	}
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println(getName()+":"+i);
			if(i%10==0){
			 yield();
			}
		}
		
	}
}

一般情况下yield()方法用到的不多,但是也是有的.



优先级的意思是:同样两个处于就绪的线程,优先级高的那个
线程在执行的过程中所占用的cpu时间相对的比优先级低的
那个要长,并不是说优先级高的就一定要先执行,执行完了之后
优先级低的才会去执行,而是两个都会执行,只是优先级越高它
的执行时间就越长而已.


线程同步:对于访问同一个资源的多个线程进行协调,使它们按照
正常的顺序去执行就叫做线程同步.它就相当于你在上厕所的时候
进去了之后先把门给关上,这段期间归你独占这个坑.其他的人进不来
就相当于是其他的线程在此期间不能访问这块资源一样.这个时候就
加上一把锁就行了.


public class TestSyncronize implements Runnable {
	timer t=new timer();
	@Override
	public void run() {
		t.add(Thread.currentThread().getName());
	}

	public static void main(String[] args) {
		TestSyncronize test=new TestSyncronize();
		Thread t1=new Thread(test);
		Thread t2=new Thread(test);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		t2.start();
		/*
		 * 为什么打印的结果是两个都是第二个用户呢?
		 * 不应该是t1是第一个,而t2是第二个用户吗?
		 * 不是的,这是因为在t1执行的过程中被interrupt
		 * 了,所以呢就在他要去执行下面打印的话的时候
		 * 它sleep()了,而这个时候num已经由0变成了1了
		 * 然后再执行一个新的test对象的时候,它又由1变成
		 * 了2了,这个时候t1醒过来了,它继续执行下面的打印
		 * 的话结果就是第二个用户,而同样t2也是第二个用户
		 * 这个地方是用的sleep()方法来使得第一个线程sleep()
		 * 或者说用sleep()方法打断了线程1的但是,实际上
		 * 就是不用这种方法,它t1仍然可能被打断的,虽然说
		 * 不能说一定会被打断,但是完全存在这种可能,而这种
		 * 情况的出现将会是很危险的,比如夫妻两个人去取款,
		 * 一个用的是邮政卡,一个用的是存折,同时不同地的去
		 * 取钱,那么这个时候就会有两个线程同时的访问这个账户
		 * 里面的资源,如果有个线程被另外一个线程打断的话,那么
		 * 银行就亏死了,存折中工4000,一个人取了3000.结果里面
		 * 还剩下1000,可笑,所以呢就要注意了是不能这样做的,要
		 * 在一个线程访问一块资源的时候先把这块资源给锁住,只有
		 * 这一个线程可以用,等这个线程搞完了之后再把锁给解开
		 * 就行了.
		 */
	}
}

class timer {
	private static int num=0;
	public void add(String name){
		num++;
		
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		System.out.println("你是第"+num+"个使用timer的用户");
	}
}

怎样解决呢就是加上锁,在方法add()上加上锁就好了,它的意思就是在
执行当前方法的时候锁定当前的对象,而不是说只是锁定这个方法,只
有等这个方法执行完了之后其他的人才可以对这个对象进行访问。

死锁的问题:就是一个进程要完成一件事情的时候,它要在锁住一个
对象的同时要锁住另外一个对象,而另一进程则正好跟他一样,也是
要锁住同样的这两个对象,只是它们锁的顺序正好是相反的,比如进
程a要锁住对象o1和o2,即在锁住o1的内部还要锁住o2才能完成这个
任务,而对象b则相反,它要先锁住o2,之后再锁住o2的内部再锁住
o1才能完成任务,而这两个线程又正好是同时执行的,那么两个进程
都会被对方所锁住不能向下执行,这个就是死锁。

要注意这个死锁是在锁住一个对象的内部,再锁住另外的一个对象,而
不是并排的先锁住一个o1,执行完后把o1给放开,然后在执行o2.
而且这两个对象都必须是static类型的,否则也是锁不住的。为什么呢?
因为,static是静态的全局的相当于,它是属于类所拥有的东西,因此
不管是new几个对象,在使用它们的时候必须是有顺序的,不可能一个
静态变量被两个对象同时的去访问,否则它就没有意义了。
而如果不是静态的,那么这两个对象就会成为对象的变量,它们分布在
堆空间中,这个时候每个new出来的对象所拥有的变量都是它们自己的
而不是全局的,所以可以任意的改变。

对于死锁有一个故事:哲学家吃饭的问题,五个哲学家围坐在一个圆桌
边上,桌上面摆放着大鱼大肉的,但是每个哲学家都是只有右手有一支
筷子,所以大家都等着左边的那个人能把那支筷子给他,让他先吃,可
是没有一个人愿意把自己的筷子给其他人,结果,五个人就看着饭被饿
死了。它的这个死锁相当于是多线程死锁,而上面的例子是两个线程之
间的死锁。

解决死锁的问题办法:加粗粒度,就是不要只是锁住一个类里面的一两
属性,而是锁住整个类。

对于数据库里面的数据来说有该和读的两个方法,一个是该,可以允许
两个方法同时的来读数据,但是不允许两个线程同时的来该数据,所以
对于修改数据的方法要加锁,对于读取数据的方法来说不应该加锁。

如果两个方法同时都修改了一个参数的值的话,那么这两个方法应该都
加锁。

Wait()方法跟sleep()方法之间的区别?
Wait()方法跟sleep()方法区别太大了,Wait()方法是Object的方法,而sleep()
是Thread类的方法.
在调用Wait()方法的时候要必须锁定该对象,因为Wait()一发生就会解锁,
所以要在调用的时候锁定对象
而sleep()则不一样,在调用它的时候线程还是锁定着的.
notify(),notifyAll()都是Object类的方法.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值