多线程:
(一)进程与线程
进程特点
并发与并行的区别:
多线程编程的好处:
(二)多线程的建立
1,通过继承Thread类,代码如下:
class MyThread extends Thread { private static int K = 10;//类共享变量 private int M=10;//成员共享变量 MyThread(){ super(); } @Override public void run() { System.out.println("in MyThread run"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "K:" + K--); System.out.println(Thread.currentThread().getName() + "M:" + M--); } } } public class ThreadDemo1 { public static void main(String[] args) { //多线程Thead方式1 MyThread thread2 = new MyThread(); new Thread(thread2,"t2").start();//此时成员变量被共享,静态也被共享,k和M的结果为-9 new Thread(thread2,"t3").start(); //多线程Thead方式1 MyThread thread3=new MyThread();//此时静态类变量被共享,k为-9,M为0. thread2.start(); thread3.start(); } }
2,通过实现Runnable接口(推荐),代码如下:
public class ThreadDemo1 { public static void main(String[] args) { //RUNNABLE 方式2 MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable,"t1").start();//此时成员变量被共享,静态也被共享,K和M为-9 //RUNNABLE 方式2 new Thread(myRunnable,"t3").start(); MyRunnable myRunnable1 = new MyRunnable();//此时成员变量不被共享,静态被共享,K为-9,M为0 new Thread(myRunnable,"t1").start(); new Thread(myRunnable1,"t3").start(); } } class MyRunnable implements Runnable { private static int K = 10; private int M = 10; @Override public void run() { System.out.println("in MyRunnable run"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " K:" + (K--)); System.out.println(Thread.currentThread().getName() + " M:" + (M--)); } } }
3,通过实现Callable接口和Future包装来建立:
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableThread { public static void main(String[] args) { Callable<Integer> callable = new Callable<Integer>() { public Integer call() throws Exception { return new Random().nextInt(100); } }; FutureTask<Integer> future = new FutureTask<Integer>(callable); new Thread(future).start(); try { Thread.sleep(5000);// 可能做一些事情 System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
三种建立多线程的优劣比较有:
(三)线程的生命周期
Java线程具有五中基本状态
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
关键字:
- yield:该线程让出CPU,进入就绪状态
- join:该子线程优先执行,相当于方法调用,父线程进入IO阻塞状态
- sleep:该线程让出CPU,进入IO阻塞状态,不释放对象锁
- wait:同步锁下进行使用,该线程进入阻塞状态,释放对象锁
- notify:唤醒wait下的线程,进入同步阻塞状态
- synchronized:同步锁,未获得锁的线程进入同步阻塞状态
(五),多线程同步(Thread Synchronized)
线程同步即保证某个线程访问某个共享资源时,其他的线程需要wait,即有顺序性
(六),多线程案例
1,线程死锁案例:
/*死锁: 线程1:synchronized(o1) { Thread.sleep() synchronized(o2) } 线程2:synchronized(o2) { Thread.sleep() synchronized(o1) } 首先线程1执行,对o1加同步锁,其他线程无法访问, 然后线程1睡眠,让出cpu,线程2执行,锁住o2 线程1睡眠结束,继续执行,要对o2加同步锁,但被线程2占据,所以上不了锁, 处于等待获得o2同步锁的状态,且线程1不能结束,释放不了o1对象。 线程2睡眠结束,继续执行,要对o1加同步锁,但被线程1占据,所以上不了锁, 处于等待获得o1同步锁的状态,且线程2不能结束,故释放不了o2 故处于死锁状态。 死锁经典问题:哲学家问题 方法:加粗锁定对象*/ public class DeadThread implements Runnable { private int flag=0; static Object object=new Object();//类变量,只有一份,每个实例共享 static Object object1=new Object();// public void run(){ if (flag==0){ synchronized (object) {//释放object同步锁,需要等object1锁释放, System.out.println(flag); try { Thread.sleep(40); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (object1) { //System.out.println(flag); } } } if (flag==1){ synchronized (object1) {//释放object1同步锁,需要等object锁释放.两个线程互相等待中 System.out.println(flag); try { Thread.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (object) { //System.out.println(flag); } } } } public static void main(String[] args) { DeadThread deadThread=new DeadThread(); DeadThread deadThread1=new DeadThread(); deadThread.flag=0; deadThread.flag=1; Thread thread=new Thread(deadThread); Thread thread1=new Thread(deadThread1); thread.start(); thread1.start(); } }
2,多线程多态:
public class ThreadTest { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Runnable myRunnable = new MyRunnable(); Thread thread = new MyThread(myRunnable);//输出的是MyThread中的Run方法,多态的体现 Thread thread1=new Thread(myRunnalbe);//输出myRunnable中run方法。 thread1.start(); thread.start(); } } } } class MyRunnable implements Runnable { private int i = 0; @Override public void run() { System.out.println("in MyRunnable run"); for (i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } } class MyThread extends Thread { private int i = 0; public MyThread(Runnable runnable){ super(runnable); } @Override public void run() { System.out.println("in MyThread run"); for (i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } }
3,生产者消费者模型:
public class ProducerConsumer { public static void main(String[] args) { SycnStack ss=new SycnStack(); Runnable producer=new Producer(ss); Runnable consumer=new Consumer(ss); new Thread(producer,"p1").start(); /*new Thread(producer,"p2").start(); new Thread(producer,"p3").start(); new Thread(producer,"p4").start(); new Thread(producer,"p5").start();*/ new Thread(consumer,"c1").start(); } } class WoTo{ int id; WoTo(int id){ this.id=id; } @Override public String toString() { return "WOTO"+":"+id; } } class SycnStack{ WoTo[] wotoArr=new WoTo[6]; int index=0; public synchronized void push(WoTo woto){ while (index==6){//wotoArr.length==6,当stack中满6个时,生产停止,需要消费者消费并notify生产者继续生产 try { System.out.println("full");//发生wait后,如果没有notify唤醒写在wait的方法不执行。 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } wotoArr[index]=woto; index+=1; this.notifyAll();//生产往后通知消费者,主要是唤醒消费者来消费,如果多线程也会唤醒生产者,但唤醒后任然可能会进入wait中 } public synchronized WoTo pop(){ while (index==0){ try { System.out.println("null"); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } index-=1; this.notifyAll();//消费完后通知生产者,唤醒生产者来生产,注释掉会进入死锁状态即,消费者等生产者生产,而生产者在wait中。 return wotoArr[index]; } } class Producer implements Runnable{ SycnStack ss=null; Producer(SycnStack ss){ this.ss=ss; } @Override public void run() { for (int i=0;i<20;i+=1){//一个线程最多生产20个馒头 WoTo woto=new WoTo(i); ss.push(woto); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":produce:"+woto); } } } class Consumer implements Runnable{ SycnStack ss=null; Consumer(SycnStack ss){ this.ss=ss; } @Override public void run() { for(int i=0;i<20;i+=1){//一个消费者最多消费20个 WoTo woto=ss.pop(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":consuemer:"+woto); } } }
4,笔试题1:
public class SyncThread1 implements Runnable { int num=100; /* m1()和m2()都加了同步锁,执行流程显示调用run方法中的m1(),对num加同步锁,然后让出cpu,开始main线程 调用m2方法,不能访问不能修改num,结束然后,输出num,然后让出cpu,继续执行m1()方法。sleep并不代表结束就能马上运行,处于就绪状态 需抢占 */ synchronized void m1(){ num=1000; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("num"+num); } synchronized void m2(){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(num); num=2000; //System.out.println(num); } public void run(){ m1(); } public static void main(String[] args) { SyncThread1 syncThread1=new SyncThread1(); Thread thread=new Thread(syncThread1); thread.start();//新开的线程,不和main线程共线程 syncThread1.m2();//在main线程中,因为m2()加了同步锁,即对num加锁了,m2无法对num修改,即不执行,直接执行下面这个输出 System.out.println(syncThread1.num);//main线程要等syncThread1.m2()执行完才执行 } }
5,哲学家问题:
/*问题描述:一圆桌前坐着5位哲学家,两个人中间有一只筷子,桌子中央有面条。哲学家思考问题, 当饿了的时候拿起左右两只筷子吃饭,必须拿到两只筷子才能吃饭。上述问题会产生死锁的情况, 当5个哲学家都拿起自己右手边的筷子,准备拿左手边的筷子时产生死锁现象。 解决办法: 1、添加一个服务生,只有当经过服务生同意之后才能拿筷子,服务生负责避免死锁发生。 2、每个哲学家必须确定自己左右手的筷子都可用的时候,才能同时拿起两只筷子进餐,吃完之后同时放下两只筷子。 代码实现:实现第2种方案*/ public class PhilosopherDemo { public static void main(String[] args) { Fork fork=new Fork(); //五个philosopher都指向同一个fork,所以此时相当于成员变量被多个线程共享了 Philosopher philosopher=new Philosopher(fork,0); Philosopher philosopher1=new Philosopher(fork,1); Philosopher philosopher2=new Philosopher(fork,2); Philosopher philosopher3=new Philosopher(fork,3); Philosopher philosopher4=new Philosopher(fork,4); new Thread(philosopher).start(); new Thread(philosopher1).start(); new Thread(philosopher2).start(); new Thread(philosopher3).start(); new Thread(philosopher4).start(); } } class Philosopher implements Runnable{ Fork fork=null; int id; Philosopher(Fork fork,int id){ this.fork=fork; this.id=id; } @Override public void run() { while(true){ think(); fork.getFork(this); eat(); fork.offFork(this); } } public void think(){ try { System.out.println(id+"in thinking"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } public void eat(){ try { System.out.println(id+"in eating"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } class Fork{ boolean[] fork={false,false,false,false,false};//模拟五把叉 public synchronized void getFork(Philosopher p){ while(fork[p.id]||fork[(p.id+1)%5]){ System.out.println("p:"+p.id+"waiting"); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } fork[p.id]=true; fork[(p.id+1)%5]=true; System.out.println("p:"+p.id+"getFork"); } public synchronized void offFork(Philosopher p){ fork[p.id]=false; fork[(p.id+1)%5]=false; System.out.println("p:"+p.id+"offFork"); this.notifyAll(); } }