1. 进程:一个程序是一个进程,一个程序主函数执行就是一个进程,电脑上可以同时有多个进程同时运行,叫并发。2个CPU4个核一共就能并行4个程序,进程多,按时间片轮转获得资源。
2. 线程:一个进程可以包含多个线程,如启动一个QQ是一个进程,QQ同时可以聊天,可以弹广告消息,线程是进程的一个执行线索,线程数多的时候,线程数跟核数相等就并行,不等就时间片轮转。
3. 进程之间交互通信,不方便,线程之间很很方便(一个进程内的)。
4. 程序运行时,先启动线程,按照代码(线路)来执行。
5. 继承Thread类实现多线程:1. 自己实现的线程类要继承Thread类 2. 重写run()方法,run方法里是该线程要执行的操作。
开启线程方法:new线程类对象,用该对象调用start()方法。start方法会调用ran方法。
如果直接调用run方法,如果run方法是死循环,就会一直执行run方法,这样就是自己实现的线程的线程一直在跑,
无法执行其他线程,就是资源都被这个线程占用,这个线程一直在执行,直到执行结束。
调用start方法会在线程启动前向系统申请资源,分配执行的时间片,申请到cpu后,再调用run()方法执行。
6 .实现Runnable接口: 1.自己实现的线程类要实现Runnable类,Runnable接口中只定义了一个run()方法,2.自己实现的类中实现run()方法。
开启线程方法:new Thread(new RunTread()).start(); 其实是Thread类中有个构造方法 Thread(Runnable runnable)可以传
实现了Runnable接口的子类,启动线程还是调用了Thread类的start()方法。
这种方式就是不用继承Thread类,自己实现的线程类可以继承别的类了。
7. start()方法如何申请资源: 源码中可以看成 start方法调用源码中一个native的start0方法来实现资源配置。native方法为java与非java代码交互的关键字,
如调用c代码,JVM将控制调用本地方法的所有细节,具体看另外native博客。
源码:
public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0(); /** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }
8. 进程分为前台进程和后台进程 后台进程在主函数(也是个进程)中执行的话,如果主函数进程结束,后台进程也结束,前台进程则不受影响。后台线程也叫守护线程。
public static void main(String[] args) { Thread t = new Ticket(); t.setDaemon(true);//设置为后台进程 t.start();
9. 线程的联合 join
package weiguoyuan.chainunicom.cn; class Ticket extends Thread { public void run() { while(true) { System.out.println(Thread.currentThread().getName()+ " ); } } } public class TestThread { public static void main(String[] args) throws InterruptedException { Thread t = new Ticket(); t.start(); int i = 0; while(true) { i++; System.out.println(Thread.currentThread().getName()); if(i==10000) { t.join(6666); //6666为毫秒数 ,将t线程联合过来 只执行t直到6666毫秒过后 恢复 } } } }
10. 例子 经典卖票程序 3窗口卖1000
<1. 3个线程 分别有自己的tickets 这样就卖出了 3000张票
package weiguoyuan.chainunicom.cn; class Ticket extends Thread { private int tickets = 1000; public void run() { while(tickets>0) { System.out.println(Thread.currentThread().getName()+ " "+tickets--); } } } public class TestThread { public static void main(String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); } }
<2. 在tickets上加上static 资源共享 3个线程动作相同 瓜分现象
private static int tickets = 1000;
<3. 线程的同步问题,当多个线程同时进入run()方法中,操作tickets--,如假如当前tickets为1,1,2线程两个线程,1线程先得到时间片判断tickets>0,然后还没执行到tickets--这句代码,2号线程抢到时间片进入run()方法也判断tickets>0,然后执行tickets--,这时tickets已经为0了,结束线程,这时1线程得到时间片执行tickets--,tickets=-1,显然已经出错。
解决办法:用锁锁住run()方法,锁住判断和操作资源,让这两步为一个原子,当一个线程进来判断时,不允许其他进程进入。
第一种锁: synchronized 同步代码块,同步的 的意思,实现方法:Object类,即每个对象都有2种状态,低电平0和高电平1,synchronized通过有线程进入该方法就改变,传入的对象的状态来锁定一段代码,如有线程进入则改变对象的状态为1,其他进程过来时synchronized判断自己的对象为1则不让该线程进入,进入的线程执行结束后将对象的状态改为0,其他线程才可以进入。
package weiguoyuan.chainunicom.cn; class Ticket extends Thread { private int tickets = 1000;
private Object obj; public void run() { while(true){ synchronized(obj) {//利用obj上锁 if (tickets>0) { System.out.println(Thread.currentThread().getName()+ " "+tickets--); } } } } } public class TestThread { public static void main(String[] args) { Ticket t = new Ticket(); t.start(); t.start(); t.start(); } }
第二种锁:synchronized 同步函数
package weiguoyuan.chainunicom.cn; class Ticket extends Thread { private int tickets = 1000; public void run() { while(true){ saleTickets();//原子性代码抽取成同步函数 } } public synchronized void saleTickets() {//这个谁调用synchronized 锁的就是谁 其实这3个线程都用的t这个对象调用该方法来同步 if (tickets>0) {
System.out.println(Thread.currentThread().getName()+ " "+tickets--); } } } public class TestThread { public static void main(String[] args) { Ticket t = new Ticket(); t.start(); t.start(); t.start(); } }
还可以用lock接口 主要用到的子类为ReentrantLock 来做同步(主要)有lock()和 unlock()方法 (finally 一定要执行的)
11 死锁问题:比如两个线程 都是要得到磁盘和cpu才能运行,一个得到了磁盘,一个得到了cpu,都不释放资源,谁都运行不了
12 线程之间的通信,Thread类中有 sleep()方法释放cpu和资源,Object类里有 wait(),notify()方法使当前线程等待和唤醒在此对象监视器上等待的单个线程,Object就是看成资源类(磁盘.notify()队列中一个等待的线程,这个线程就可以得到磁盘了)。