java线程

来源:互联网 发布:linux oracle 安装 编辑:程序博客网 时间:2024/06/01 14:31
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 {@Overridepublic 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 {@Overridepublic 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;@Overridepublic void run(){while(flag){System.out.println(new Date());try {sleep(1000);/* * 注意这个地方,对于重写的这个 * run()方法,对于异常的捕捉只能 * 够使用try{}cattch(){} * 为什么呢? * 因为作为run()方法是被重写的方法 * 所以作为重写的方法,不能够抛出比 * 自己父类更大的错误,也不允许抛出 * 不同的异常. */} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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 blocke.printStackTrace();}/* * 如果把上面的join()方法给注释掉的话,结果就是 * 两个线程main()线程和jj线程交替的打印,如果是 * 把注释拿掉的话,结果就是先打印jj线程的东西,之 * 后才会去打印main()线程里面的东西,因为它们已经 * 合并成为了一个线程,它必须要按照顺序来执行了. */for(int i=0;i<100;i++){System.out.println("main Thread");}}}class j extends Thread {@Overridepublic 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 blocke.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();@Overridepublic 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 blocke.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类的方法.

原创粉丝点击