Java从入门到精通 第22章 多线程

Java从入门到精通 第22章 多线程

//单一线程的运行流程
public class Nanjing {
    
    public static void main(String[] args) {
    	
    	// TODO, add your application code
    	System.out.println("Hello World!");
    	//new TestThread.run();书上这样写的,会报错,找不到符号
    	TestThread tt=new TestThread();
    	tt.run();
    	new TestThread().run();//这种写法也是可以的
    	for(int i=0;i<5;i++)
    		System.out.println("main is runing");
    }
}

class TestThread{
	public void run(){
		for(int i=0;i<5;i++)
			System.out.println("TestThread is Running...");
	}
}

Hello World!
TestThread is Running…
TestThread is Running…
TestThread is Running…
TestThread is Running…
TestThread is Running…
main is runing
main is runing
main is runing
main is runing
main is runing

22.2.1 通过继承Thread类实现多线程

//同时激活多个线程
public class Nanjing {
    
    public static void main(String[] args) {
    	
    	// TODO, add your application code
    	System.out.println("Hello World!");
    	new TestThread().start();//隐式的创建是可以的
    	for(int i=0;i<5;i++)
    	{
    		System.out.println("main is runing");
    		try{
    			Thread.sleep(1000);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    	}
    }
}

class TestThread extends Thread{
	public void run(){
		for(int i=0;i<5;i++)
		{
			System.out.println("TestThread is Running...");
			try{
				Thread.sleep(1000);
			}catch(InterruptedException ex){
				ex.printStackTrace();
			}
		}
	}
}

Hello World!
main is runing
TestThread is Running…
main is runing
TestThread is Running…
main is runing
TestThread is Running…
main is runing
TestThread is Running…
main is runing
TestThread is Running…

22.2.2 通过继承Runnable接口实现多线程

public class Nanjing {
    
    public static void main(String[] args) {
    	
    	// TODO, add your application code
    	System.out.println("Hello World!");
    	TestThread t=new TestThread();
        new Thread(t).start();
    	for(int i=0;i<5;i++)
    	{
    		System.out.println("main is runing");
    		try{
    			Thread.sleep(1000);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    	}
    }
}

class TestThread implements Runnable{
	public void run(){
		for(int i=0;i<5;i++)
		{
			System.out.println("TestThread is Running...");
			try{
				Thread.sleep(1000);
			}catch(InterruptedException ex){
				ex.printStackTrace();
			}
		}
	}
}

Hello World!
main is runing
TestThread is Running…
TestThread is Running…
main is runing
main is runing
TestThread is Running…
main is runing
TestThread is Running…
TestThread is Running…
main is runing

22.2.3 两种多线程实现机制的比较

本质上,Thread类是Runnable接口的一个子类

public class Nanjing {
    
    public static void main(String[] args) {
    	
    	TestThread t=new TestThread();
    	t.start();
    	t.start();//一个线程只能启动一次
    }
}

class TestThread extends Thread{
	private int ticketes=5;
	public void run(){
		while(ticketes>0){
			System.out.println(Thread.currentThread().getName()+" Saling ticket"+ticketes);
			ticketes-=1;
		}
	}
}

Thread-0 Saling ticket5
Thread-0 Saling ticket4
Thread-0 Saling ticket3
Thread-0 Saling ticket2
Thread-0 Saling ticket1
Exception in thread “main” java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:571)
at Test.main(Test.java:7)

//创建三个线程同时售票
public class Nanjing {
    
    public static void main(String[] args) {
    	
    	new TestThread().start();
    	new TestThread().start();
    	new TestThread().start();
    }
}

class TestThread extends Thread{
	private int ticketes=5;
	public void run(){
		while(ticketes>0){
			System.out.println(Thread.currentThread().getName()+" Saling ticket"+ticketes);
			ticketes-=1;
		}
	}
}

Thread-1 Saling ticket5
Thread-1 Saling ticket4
Thread-1 Saling ticket3
Thread-1 Saling ticket2
Thread-1 Saling ticket1
Thread-0 Saling ticket5
Thread-0 Saling ticket4
Thread-0 Saling ticket3
Thread-0 Saling ticket2
Thread-0 Saling ticket1
Thread-2 Saling ticket5
Thread-2 Saling ticket4
Thread-2 Saling ticket3
Thread-2 Saling ticket2
Thread-2 Saling ticket1
//实际上Thread很难达到资源共享的目的,但是可以通过静态变量达到资源共享

//Runnble相对于Thread有优势
①避免Java单继承带来的局限
②可以使多个线程共享相同的资源,以达到资源共享之目的

public class Nanjing {
    
    public static void main(String[] args) {
    	TestThread t=new TestThread();
    	new Thread(t).start();
    	new Thread(t).start();
    	new Thread(t).start();
    }
}

class TestThread implements Runnable{
	private int ticketes=5;
	public void run(){
		while(ticketes>0){
			System.out.println(Thread.currentThread().getName()+" Saling ticket"+ticketes);
			ticketes-=1;
		}
	}
}

//result
Thread-0 Saling ticket5
Thread-0 Saling ticket4
Thread-0 Saling ticket3
Thread-0 Saling ticket2
Thread-0 Saling ticket1

22.3 线程的状态

//演示线程的生命周期
import java.util.Scanner;
public class Nanjing {
    
    public static void main(String[] args) {
    	Thread t=new Thread(new TestThread());
    	System.out.println("Thead is at build state");
    	t.start();
    	System.out.println("Ready state");
    }
}

class TestThread implements Runnable{
	private int ticketes=5;
	public void run(){
			System.out.println("Runing...");
			Scanner scanner=new Scanner(System.in);
			System.out.println("Waiting I/O,Blocking");
			System.out.println("Please input String:");
			scanner.next();
			scanner.close();
			System.out.println("Block over,Re-enter the ready state and then enter the running state");
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			System.out.println("Thread enter dead state");
		}
}

//result
Thead is at build state
Ready state
Runing…
Waiting I/O,Blocking
Please input String:
Naning science and technology institute//输入完后停顿了1秒
Block over,Re-enter the ready state and then enter the running state
Thread enter dead state

22.4 线程操作的一些方法

22.4.1取得和设置线程名称

//线程名称的操作
public class Nanjing {
    
    public static void main(String[] args) {
    	TestThread t=new TestThread();
    	t.start();
    	for(int i=0;i<3;i++){
    		t.printMsg();
    		try{
    			Thread.sleep(1000);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
    	}
    }
}

class TestThread extends Thread{
	public void run(){
		for(int i=0;i<3;i++){
			printMsg();
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}
	}
	public void printMsg(){
		Thread t=Thread.currentThread();//获得运行此代码的线程的引用
		String name=t.getName();
		System.out.println("name="+name);
	}
}

//result
name=main
name=Thread-0
name=main
name=Thread-0
name=main
name=Thread-0

//设置线程名称
t.setName("BIT");
//判断线程是否启动
t.isAlive()

22.4.3守护线程与setDaemon方法

ThreadTest t=new ThreadTest();
Treat tt=newThread(t);
tt.setDaemon(true);//设置守护线程,一定要在start()之前
tt.start();
//主线程main结束就随之终止了
//当java虚拟机中没有非守护线程在运行的时候,java虚拟机会关闭。当所有常规线程运行完毕以后,守护线程不管运行到哪里,虚拟机都会退出运行。所以你的守护线程最好不要写一些会影响程序的业务逻辑。否则无法预料程序到底 会出现什么问题。

22.4.4 线程的联合

//演示线程的强制执行
ThreadTest t=new ThreadTest(t);
pp.start();
int i=0;
for(int x=0;x<5;x++)
if(i==3)
try{
pp.join();//强制运行完pp线程后,再运行后面的进程,使主线程暂时挂起
}

22.4.5 线程的中断

//线程中断的使用范例
public class Nanjing {
    
    public static void main(String[] args) {
		TestThread tt=new TestThread();
		Thread t=new Thread(tt);
		t.start();
		try{
			Thread.sleep(2000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		System.out.println("at main()method,interrupt other thread");
		t.interrupt();
		System.out.println("at main()method,exit");
    }
}

class TestThread implements Runnable{
	public void run(){
		try{
			System.out.println("at run() method,let this thread sleep 10 seconds");
			Thread.sleep(10000);
			System.out.println("at run() method,continue runing");
		}catch(InterruptedException x){
			System.out.println("at run() method,interrupt thread");
			return;
		}
		System.out.println("at run() method,continue to complete after sleep");
		System.out.println("at run() method,normally exit");
	}
}

//result
at run() method,let this thread sleep 10 seconds
at main()method,interrupt other thread
at run() method,interrupt thread
at main()method,exit

22.5多线程的同步

同步代码块
synchronized{
}

//同步代码块的使用
public class Nanjing {
    
    public static void main(String[] args) {
		TestThread tt=new TestThread();
		new Thread(tt).start();
		new Thread(tt).start();
		new Thread(tt).start();
    }
}

class TestThread implements Runnable{
	private int tickets=5;
	public void run(){
		while(true){
			synchronized (this){
				if(tickets<=0) break;
				try{
					Thread.sleep(1000);
				}catch(Exception e){
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+"saling ticket"+tickets);
			tickets-=1;
		}
	}
}

//result
Thread-0saling ticket5
Thread-1saling ticket4
Thread-2saling ticket3
Thread-1saling ticket2
Thread-0saling ticket1
Thread-1saling ticket0

同步方法
public sychoronized void sale(){}

//同步方法的使用
public class Nanjing {
    
    public static void main(String[] args) {
		TestThread tt=new TestThread();
		new Thread(tt).start();
		new Thread(tt).start();
		new Thread(tt).start();
    }
}

class TestThread implements Runnable{
	private int tickets=5;
	public void run(){
		while(tickets>0){
			sale();
		}
	}
	public synchronized void sale(){
		if(tickets>0){
			try{
				Thread.sleep(1000);
			}catch(Exception e){
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+" saling ticket"+tickets);
			tickets-=1;
		}
	}
}

Thread-0 saling ticket5
Thread-2 saling ticket4
Thread-1 saling ticket3
Thread-0 saling ticket2
Thread-2 saling ticket1

22.5.4死锁

如果一组进程,其中的每个都无限等待该组进程中的另一个进程所占据的资源,就会产生无限期的僵持下去的局面,这种现象就是死锁

Java多线程中,最常见的死锁形式是当线程1已经占据资源R1,并持有资源R1上的锁,而正在等待资源2开锁;而线程2已经占据资源R2,并且持有R2上的锁,正在等待资源R1开锁。如果俩个线程不释放自己占据的资源锁,而还申请对方资源上的锁,申请不到时就等待,那么他们只能永远等待下去

//模拟死锁的发生
public class DeadLock{
	static String knife="knife",fork="fork";
	static class A extends Thread{
		public void run(){
			synchronized(knife){
				System.out.println("A take "+knife+", waiting "+fork+"...");
				try{
					Thread.sleep(100);
				}catch(InterruptedException e){}
				synchronized(fork){//由于死锁的原因,这里永远不会执行
					System.out.println("A take "+fork);
				}
			}
		}
	}
	static class B extends Thread{
		public void run(){
			synchronized(fork){
				System.out.println("B take "+fork+", waiting for "+knife+"...");
				try{
					Thread.sleep(1000);
				}catch(InterruptedException e){}
				synchronized(knife){
					System.out.println("B take knife");
				}	
			}
		}
	}
	static class Demo extends Thread{
		public Demo(){
			this.setDaemon(true);
		}
		public void run(){
			while(true){
				try{
					Thread.sleep(1000);
				}catch(InterruptedException e){}
				System.out.println("protected thread:program is running...");
			}
		}
	}
	public static void main(String[] args){
		new A().start();
		new B().start();
		new Demo().start();
	}
}

//result
A take knife, waiting fork…
B take fork, waiting for knife…
protected thread:program is running…
protected thread:program is running…
protected thread:program is running…
protected thread:program is running…

22.6线程间的通信

22.6.1问题的引出

一个线程向数据存储空间中添加数据(生产者),另一个线程从数据空间中取出数据(消费者)。这个程序有两种以外需要考虑。
第一种以外,假设生产者线程刚向数据存储空间中添加了一个人的姓名,还没有加入这个人的性别,CPU就切换到了消费者线程,消费者线程则把这个人的姓名和上一个人的性别联系到了一起

第二种意外,生产者放入了若干数据,消费者才开始读取数据,或者是消费者读取完一个数据后,还没等到生产者放入新的数据,又重新取出已经取过的数据。

在操作系统里,上面的案例属于经典的同步问题——生产者消费者问题
可以过线程间的通信来解决以上问题

//进程间的通信
class Producer implements Runnable{
	Person p=null;
	public Producer(Person p){
		this.p=p;
	}
	public void run(){
		for(int i=0;i<10;i++){
			if(i%2==0){
				p.name="ZhangSan";
				try{
					Thread.sleep(1000);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				p.sex="male";
			}else{
				p.name="LiSi";
				try{
					Thread.sleep(1000);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				p.sex="female";
			}
		}
	}
}

class Consumer implements Runnable{
	Person q=null;
	public Consumer(Person q){
		this.q=q;
	}
	public void run(){
		for(int i=0;i<10;i++){
			System.out.println(q.name+"--->"+q.sex);
			try{
				Thread.sleep(1000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}
	}
}

class Person{
	String name="LiSi";
	String sex="female";
}
public class Test{
	public static void main(String[] args){
		Person q=new Person();
		new Thread(new Producer(q)).start();
		new Thread(new Consumer(q)).start();
	}
}

//result
ZhangSan—>female
ZhangSan—>female
LiSi—>male
ZhangSan—>female
ZhangSan—>female
LiSi—>male
LiSi—>male
ZhangSan—>female
LiSi—>male
ZhangSan—>female

//进程同步的使用
class Producer implements Runnable{
	Person p=null;
	public Producer(Person p){
		this.p=p;
	}
	public void run(){
		for(int i=0;i<10;i++){
			if(i%2==0){
				p.set("ZhangSan","male");
			}else{
				p.set("LiSi","male");
			}
		}
	}
}

class Consumer implements Runnable{
	Person q=null;
	public Consumer(Person q){
		this.q=q;
	}
	public void run(){
		for(int i=0;i<10;i++){
			q.get();
		}
	}
}

class Person{
	String name="LiSi";
	String sex="female";
	public synchronized void set(String name,String sex){
		this.name=name;
		this.sex=sex;
	}
	public synchronized void get(){
		System.out.println(this.name+"--->"+this.sex);
	}
}
public class Test{
	public static void main(String[] args){
		Person q=new Person();
		new Thread(new Producer(q)).start();
		new Thread(new Consumer(q)).start();
	}
}

//result
LiSi—>male
LiSi—>male
LiSi—>male
LiSi—>male
LiSi—>male
LiSi—>male
LiSi—>male
LiSi—>male
LiSi—>male
LiSi—>male
//example analysis
虽然保证了李四是女的,但是又产生了另外的问题,Consumer线程对Producer线程放入的数据连续的读取多次,多次输出:Lisi—>male这并不符合实际要求。合理的结果是Producer放入一次数据,Consumer就取一次;反之。Producer必须等到Consumer取完后才能放入新的数据,而这一问题就需要线程通信

Java通过Object类的wait() noify() notifyAll()这几个发方法来实现线程通信
wait() 通知当前线程进入睡眠状态
notify()唤醒在该同步代码块中第一个调用的wait()的线程
ntifyAll()唤醒该同步代码块中所有调用wait()的线程,具有高优先级的线程首先被唤醒并执行

//线程通信问题的解决
class Producer implements Runnable{
	Person p=null;
	public Producer(Person p){
		this.p=p;
	}
	public void run(){
		for(int i=0;i<10;i++){
			if(i%2==0){
				p.set("ZhangSan","male");
			}else{
				p.set("LiSi","female");
			}
		}
	}
}

class Consumer implements Runnable{
	Person q=null;
	public Consumer(Person q){
		this.q=q;
	}
	public void run(){
		for(int i=0;i<10;i++){
			q.get();
		}
	}
}

class Person{
	String name="LiSi";
	String sex="female";
	private boolean bFull=false;
	public synchronized void set(String name,String sex){
		if(bFull){
			try{
				wait();//后来的线程要等待
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}
		this.name=name;
		this.sex=sex;
		bFull=true;
		notify();//唤醒最先达到的线程
	}
	public synchronized void get(){
		if(!bFull){
			try{
				wait();
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		}
		System.out.println(this.name+"--->"+this.sex);
		bFull=false;
		notify();
	}
}

public class Test{
	public static void main(String[] args){
		Person q=new Person();
		new Thread(new Producer(q)).start();
		new Thread(new Consumer(q)).start();
	}
}

//result
ZhangSan—>male
LiSi—>female
ZhangSan—>male
LiSi—>female
ZhangSan—>male
LiSi—>female
ZhangSan—>male
LiSi—>female
ZhangSan—>male
LiSi—>female

22.8总结

1.线程的几个特点
(1)同步代码块和同步方法锁的是对象,而不是代码。即如果某个对象被同步代码块或同步方法锁住,那么其他使用该对象的代码必须等待,直到该对象的锁被释放
(2)如果一个进程只有后台进程,则这个进程就会结束
(3)每个已经被创建的线程在结束之前均会处于就绪、运行、阻塞状态之一

2.另外一种多线程同步的锁机制
java.util.concurrent.locks

3.Java 8新引入Lambda表达式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值