多线程:
(一)进程与线程
进程特点
并发与并行的区别:
多线程编程的好处:
(二)多线程的建立
1,通过继承Thread类,代码如下:
class MyThread extendsThread {private static int K = 10;//类共享变量
private int M=10;//成员共享变量
MyThread(){super();
}
@Overridepublic voidrun() {
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 classThreadDemo1 {public static voidmain(String[] args) {//多线程Thead方式1
MyThread thread2 = newMyThread();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 classThreadDemo1 {public static voidmain(String[] args) {//RUNNABLE 方式2
MyRunnable myRunnable = newMyRunnable();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 implementsRunnable {private static int K = 10;private int M = 10;
@Overridepublic voidrun() {
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包装来建立:
importjava.util.Random;importjava.util.concurrent.Callable;importjava.util.concurrent.ExecutionException;importjava.util.concurrent.FutureTask;public classCallableThread {public static voidmain(String[] args) {
Callable callable = new Callable() {public Integer call() throwsException {return new Random().nextInt(100);
}
};
FutureTask future = new FutureTask(callable);newThread(future).start();try{
Thread.sleep(5000);//可能做一些事情
System.out.println(future.get());
}catch(InterruptedException e) {
e.printStackTrace();
}catch(ExecutionException e) {
e.printStackTrace();
}
}
}
View Code
三种建立多线程的优劣比较有:
(三)线程的生命周期
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 implementsRunnable {private int flag=0;static Object object=new Object();//类变量,只有一份,每个实例共享
static Object object1=new Object();// public voidrun(){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 voidmain(String[] args) {
DeadThread deadThread=newDeadThread();
DeadThread deadThread1=newDeadThread();
deadThread.flag=0;
deadThread.flag=1;
Thread thread=newThread(deadThread);
Thread thread1=newThread(deadThread1);
thread.start();
thread1.start();
}
}
2,多线程多态:
public classThreadTest {public static voidmain(String[] args) {for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+ " " +i);if (i == 30) {
Runnable myRunnable= newMyRunnable();
Thread thread= new MyThread(myRunnable);//输出的是MyThread中的Run方法,多态的体现
Thread thread1=new Thread(myRunnalbe);//输出myRunnable中run方法。
thread1.start();
thread.start();
}
}
}
}class MyRunnable implementsRunnable {private int i = 0;
@Overridepublic voidrun() {
System.out.println("in MyRunnable run");for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+ " " +i);
}
}
}class MyThread extendsThread {private int i = 0;publicMyThread(Runnable runnable){super(runnable);
}
@Overridepublic voidrun() {
System.out.println("in MyThread run");for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+ " " +i);
}
}
}
3,生产者消费者模型:
public classProducerConsumer {public static voidmain(String[] args) {
SycnStack ss=newSycnStack();
Runnable producer=newProducer(ss);
Runnable consumer=newConsumer(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();
}
}classWoTo{intid;
WoTo(intid){this.id=id;
}
@OverridepublicString toString() {return "WOTO"+":"+id;
}
}classSycnStack{
WoTo[] wotoArr=new WoTo[6];int index=0;public synchronized voidpush(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 synchronizedWoTo pop(){while (index==0){try{
System.out.println("null");this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
index-=1;this.notifyAll();//消费完后通知生产者,唤醒生产者来生产,注释掉会进入死锁状态即,消费者等生产者生产,而生产者在wait中。
returnwotoArr[index];
}
}class Producer implementsRunnable{
SycnStack ss=null;
Producer(SycnStack ss){this.ss=ss;
}
@Overridepublic voidrun() {for (int i=0;i<20;i+=1){//一个线程最多生产20个馒头
WoTo woto=newWoTo(i);
ss.push(woto);try{
Thread.sleep(200);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":produce:"+woto);
}
}
}class Consumer implementsRunnable{
SycnStack ss=null;
Consumer(SycnStack ss){this.ss=ss;
}
@Overridepublic voidrun() {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 implementsRunnable {int num=100;/*m1()和m2()都加了同步锁,执行流程显示调用run方法中的m1(),对num加同步锁,然后让出cpu,开始main线程
调用m2方法,不能访问不能修改num,结束然后,输出num,然后让出cpu,继续执行m1()方法。sleep并不代表结束就能马上运行,处于就绪状态
需抢占*/
synchronized voidm1(){
num=1000;try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("num"+num);
}synchronized voidm2(){try{
Thread.sleep(500);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(num);
num=2000;//System.out.println(num);
}public voidrun(){
m1();
}public static voidmain(String[] args) {
SyncThread1 syncThread1=newSyncThread1();
Thread thread=newThread(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 classPhilosopherDemo {public static voidmain(String[] args) {
Fork fork=newFork();//五个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);newThread(philosopher).start();newThread(philosopher1).start();newThread(philosopher2).start();newThread(philosopher3).start();newThread(philosopher4).start();
}
}class Philosopher implementsRunnable{
Fork fork=null;intid;
Philosopher(Fork fork,intid){this.fork=fork;this.id=id;
}
@Overridepublic voidrun() {while(true){
think();
fork.getFork(this);
eat();
fork.offFork(this);
}
}public voidthink(){try{
System.out.println(id+"in thinking");
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}public voideat(){try{
System.out.println(id+"in eating");
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}classFork{boolean[] fork={false,false,false,false,false};//模拟五把叉
public synchronized voidgetFork(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 voidoffFork(Philosopher p){
fork[p.id]=false;
fork[(p.id+1)%5]=false;
System.out.println("p:"+p.id+"offFork");this.notifyAll();
}
}