java多线程基础(一)

java基础之多线程

1、线程生命周期


2、线程的创建有两种方式

(1)继承Thread类:

/*
 * 创建一个子线程,完成1-100之间自然数的输出。同样的,主线程执行同样的操作。
 * 创建多线程的第一种方式:继承Thread类。
 */
public class TestThread {
	public static void main(String[] args) {
		//3\创建一个子类对象
		SubThread subThread1 = new SubThread();
		SubThread subThread2 = new SubThread();
		//4\调用线程的start()方法:启动此线程,调用线程的run()方法。
		//一个线程只能执行一次start()
		subThread1.start();
		subThread2.start();
		for(int i =1;i<10000;i++){
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
//1、继承Thread类
class SubThread extends Thread{
	//2、重写run方法,方法类内实现此子线程压迫完成的功能
	public void run(){
		for(int i =1;i<10000;i++){
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
/*
 * Thread的chnagyong fangfa 
 * 1、start():启动线程并执行相应的run方法
 * 2、run():子线程要执行的方法,放入run()方法中
 * 3、currentThread():静态的,调取当前的线程
 * 4、getName():获取此线程的名字
 * 5、setName():设置此线程的名字
 * 6、yield():调用此方法的线程释放当钱CPU的执行权。
 * 7、join():在A线程中调用B线程的join()方法,表示,当执行到此方法,A线程停止执行,直至B线程执行完毕,A线程再接着执行。
 * 8、isAlive():判断当前线程是否还存活。
 * 9、sleep(long l):显示的让此线程睡眠l毫秒。
 * 10、线程间通信:wait()、notify()、notifyAll()
 */
public class TestThread1 {

	public static void main(String[] args) throws InterruptedException {
		//3\创建一个子类对象
		SubThread1 subThread1 = new SubThread1();
		SubThread1 subThread2 = new SubThread1();
		//4\调用线程的start()方法:启动此线程,调用线程的run()方法。
		//一个线程只能执行一次start()
		subThread1.setName("嘿嘿");
		subThread1.start();
		subThread1.setPriority(Thread.MAX_PRIORITY);
		subThread2.start();
		Thread.currentThread().setName("主线程");
		for(int i =1;i<10000;i++){
			//Thread.currentThread().sleep(1000);
			System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+"---"+i);
			/*if(i%10==0){
				Thread.currentThread().yield();
			}*/
			if(i==20){
				subThread1.join();
			}
		}
	}
}
//1、继承Thread类
class SubThread1 extends Thread{
	//2、重写run方法,方法类内实现此子线程要完成的功能
	public void run(){
		for(int i =1;i<10000;i++){
			System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+"---"+i);
		}
	}
}

(2)实现Runnable接口

/*
 * 创建线程的第二种方法:通过实现Runnable 接口来实现
 * 1、创建一个实现了Runable接口的类
 * 2、实现接口的抽象方法
 * 3、创建一个Runnable接口实现类的对象
 * 4、对象作为形参传递给Thread类的构造器中,创建Thread对象,此对象即为一个线程
 * 5、调用Thread类对象的start()方法。
 * 
 * 对比一下继承和实现的方式
 * 1、联系:都实现了Runnable接口。(继承中的THread类,底层也是实现了Runnable接口)
 * 2、哪种方式比较好?实现的方式优于继承的方式》
 * (1)实现的方式避免了java中单继承的局限性(因为一个类继承了Thread之后,就不能再继承其他类了)。
 * (2)比如在售票例子中,多线程操作同一份数据,更适合使用实现的方式。
 */
class PrintNum implements Runnable{
	@Override
	public void run() {
		for(int i = 0 ; i<=100 ; i++){
			if(i%2 == 0){
				System.out.println(Thread.currentThread().getName() +"---"+ i);
			}
		}
	}	
}
public class TestRunnable{
	public static void main(String[] args) {
		PrintNum p = new PrintNum();
		//要想启动一个多线程,必须调用start()方法。
		Thread t1 = new Thread(p);
		t1.start();//启动线程执行Thread对象生成时,构造器形参的对象的run方法。
		//再创建一个线程
		Thread t2 = new Thread(p);
		t2.start();
	}
}

3、线程间同步问题

/*
 * 此程序存在线程的安全问题,打印车票时,会出现重票和错票的问题。
 */
public class TestWindow1 {
	public static void main(String[] args) {		
		Window1 window = new Window1();
		Thread t1 = new Thread(window);
		Thread t2 = new Thread(window);
		Thread t3 = new Thread(window);
		t1.setName("窗口1");
		t2.setName("窗口2");
		t3.setName("窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}
class Window1 implements Runnable {
	int sum = 100;
	@Override
	public void run() {
		while(true){
			if(sum> 0){
				try {
					Thread.currentThread().sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"----"+sum);
				sum--;
			}else if(sum == 0){
				System.out.println("这是最后一张票");
				sum--;
			}else {
				break;
			}
		}	
	}	
}
分别解决多线程继承和实现方式出现的问题:

(1)同步代码块

public class TestWindow3 {
	public static void main(String[] args) {
		Window3 w1 = new Window3();
		Window3 w2 = new Window3();
		Window3 w3 = new Window3();
		w1.setName("窗口1");
		w2.setName("窗口2");
		w3.setName("窗口3");
		w1.start();
		w2.start();
		w3.start();
	}
}
class Window3 extends Thread{
	public static int sum = 100;
	static Object obj = new Object();
	public void run(){
		while(true){
			synchronized (obj) {//在本题中,this表示w1、w2、w3。
				if (sum > 0) {
					try {
						Thread.currentThread().sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()
							+ "----" + sum);
					sum--;
				}else {
					break;
				}
			}
		}
	}
}
/*
 * 此程序存在线程的安全问题,打印车票时,会出现重票和错票的问题。
 * 1、存在的原因是什么?
 *  由于一个线程在操作共享数据未执行完的情况下,另外的数据参与进来,导致共享数据存在安全问题。
 * 
 * 2、如何解决线程的安全问题?
 *  必须让一个操作共享数据完毕以后,其他线程才有机会参与共享数据的操作。
 * 
 * 3、java如何实现线程的安全:线程的同步机制
 * 	方式一:同步代码块
 * 	synchronized(对象(同步监视器)){
 * 	//需要被同步的代码块(即为操作共享数据的代码)
 * }
 * (1)共享数据:多个线程共同操作的同一个数据或者变量
 * (2)同步监视器(锁):由一个对象来充当。哪个线程获取此监视器,谁就能执行大括号里被同步的代码。
 *  注:要求所有的线程必须使用同一把安全锁,才能实现同步。
 *  在实现的方式中可以使用this作为同步锁,但是在继承的方式要慎用。
 * 	方式二:同步方法
 * 	synchronized 还可以放在方法的声明中,表示整个方法为同步方法
 * 例如:public synchronized void show(String name){
 * 	....
 * }
 * 	注: 将操作共享数据的方法声明成synchronized 。即此方法为同步方法,能够保证当其中一个线程执行此方法时,其他线程在外等待,
 *  直至此线程执行完此方法。
 *   》同步方法的锁:就是当前对象this。
 */
public class TestWindow2 {
	public static void main(String[] args) {		
		Window2 window = new Window2();
		Thread t1 = new Thread(window);
		Thread t2 = new Thread(window);
		Thread t3 = new Thread(window);
		t1.setName("窗口1");
		t2.setName("窗口2");
		t3.setName("窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}
class Window2 implements Runnable {
	int sum = 100; //共享数据
	//Animal obj = new Animal();
	@Override
	public void run() {
		while(true){
			synchronized (this) {//在本问题中,this表示当前对象,本题中表示window,只有一个,所以此处可以使用this
				if(sum> 0){
					try {
						Thread.currentThread().sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"----"+sum);
					sum--;
				}else if(sum == 0){
					System.out.println("这是最后一张票");
					sum--;
				}else {
					break;
				}
			}
		}	
	}	
}

注:如果synchronized (this) {}包含住了whiletrue)这个代码块,那么控制台就会出现,只有一个线程在执行售票业务的情况。

(2)同步代码方法

注意:在继承Thread类方法中,不能使用synchronized方法,因为锁对象不是唯一的,所以依旧会出现同步问题。
/*
 * 此程序存在线程的安全问题,打印车票时,会出现重票和错票的问题。
 */
public class TestWindow4 {
	public static void main(String[] args) {		
		Window4 window = new Window4();
		Thread t1 = new Thread(window);
		Thread t2 = new Thread(window);
		Thread t3 = new Thread(window);
		t1.setName("窗口1");
		t2.setName("窗口2");
		t3.setName("窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}
class Window4 implements Runnable {
	int sum = 100;
	@Override
	public void run() {
		while(true){
			show();
		}	
	}
	public synchronized void show(){
			if (sum > 0) {
				try {
					Thread.currentThread().sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()
						+ "----" + sum);
				sum--;
			} 
	}
}

4、单例模式(懒汉式)中关于多线程同步的解决问题

/*
 * 关于懒汉式的线程安全问题,使用同步机制,对于一般的方法内,使用同步代码块,可以考虑使用this作为同步锁。
 * 对于静态方法,使用当前类本身充当锁
 */
public class TestSingleTon {
	public static void main(String[] args) {
		SingleTon s1 = SingleTon.getInstance();
		SingleTon s2 = SingleTon.getInstance();
		System.out.println(s1 == s2);
	}
}
class SingleTon{	
	private SingleTon(){}
	private static  SingleTon instance = null;	
	public static SingleTon getInstance(){
		//加上下边这个if判断语句,程序的效率会提高不少
		if(instance == null){
			synchronized (SingleTon.class) {			
				if(instance == null)
					instance = new SingleTon();
			}
		}
		return instance;
	}
}

5、多线程实例:实现两个人往银行账户中存钱

/*
 * 实现两个人往银行账户中存钱:方式一:采用继承的方法
 */
public class TestAccount {
	public static void main(String[] args) {
		Account2 account2 = new Account2();
		Customer2 a1 = new Customer2(account2);
		Customer2 a2 = new Customer2(account2);
		a1.setName("账户1");
		a2.setName("账户2");
		a1.start();
		a2.start();
	}
}
class Account2{
	private double balance;
	public Account2(){
		
	}
	public synchronized void deposit(double amt){
		this.balance += amt;
		try {
			Thread.currentThread().sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"操作后,"+this.balance);
	}
}
class Customer2 extends Thread{
	Account2 account;
	public Customer2(Account2 account){
		this.account = account;		
	}
	public void run(){			
			for(int i = 0 ;i<3;i++){
				account.deposit(1000);
			}
	}
}
/*
 * 实现两个人往银行账户中存钱:方式二:采用实现的方法
 */
public class TestAccount1 {
	public static void main(String[] args) {
		Account account = new Account();
		Customer c = new Customer(account);
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();		
	}
}
class Customer implements Runnable {
	private Account account = null;
	public Account getAccount() {
		return account;
	}
	public void setAccount(Account account) {
		this.account = account;
	}
	public Customer() {
		super();
	}
	public Customer(Account account) {
		super();
		this.account = account;
	}
	@Override
	public void run() {
		synchronized (this) {
			for (int i = 0; i < 3; i++) {
				account.setMoney(1000);
			}
		}
	}
}
class Account {
	private int money = 0;
	public Account() {
		System.out.println("账户现有:" + money);
	}
	public Account(int money) {
		this.money = money;
		System.out.println("账户现有:" + this.money);
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money += money;
		System.out.println(Thread.currentThread().getName() + "-----" + money);
		System.out.println("账户余额是:" + this.money);
	}
}

6、实现线程之间的通信(使用wait()、notify()、notifyAll())

/*
 * 线程通信:如下的三个关键字使用的话,都得在同步代码块或者同步方法中
 * wait():一旦一个线程执行到wait方法,就释放当前的锁。
 * notify():唤醒wait()的一个线程
 * notifyAll():唤醒wait()的所有线程
 */
class PrintNum implements Runnable{
	int num = 0;
	@Override
	public void run() {
		while(true){
			synchronized (this) {
				notify(); //在此处加notify,意思是,第一个线程执行结束,释放锁,第二个线程进来之后,
				//首先已经获取锁了,然后再唤醒另一个线程(虽然此时唤醒,但是也只能等到第二个线程释放锁之后,第一个线程才能再次开始执行)。
				if(num <= 100){
					try {
						Thread.currentThread().sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "-----"
							+ num);
					num++;
				}
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
		}
	}	
}
public class TestCommunication {
	public static void main(String[] args) {
		PrintNum printNum = new PrintNum();
		Thread t1 = new Thread(printNum);
		Thread t2 = new Thread(printNum);
		t1.start();
		t2.start();		
	}
}
/*
 * 实现两个人往银行账户中存钱:方式一:采用继承的方法
 */
public class TestAccount {
	public static void main(String[] args) {
		Account2 account2 = new Account2();
		Customer2 a1 = new Customer2(account2);
		Customer2 a2 = new Customer2(account2);
		a1.setName("账户1");
		a2.setName("账户2");
		a1.start();
		a2.start();
	}
}
class Account2{
	private double balance;
	public Account2(){
		
	}
	public synchronized void deposit(double amt){
		notify();
		this.balance += amt;
		try {
			Thread.currentThread().sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"操作后,"+this.balance);
		try {
			wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
class Customer2 extends Thread{
	Account2 account;
	public Customer2(Account2 account){
		this.account = account;		
	}
	public void run(){			
			for(int i = 0 ;i<3;i++){
				account.deposit(1000);
			}
	}
}

7、死锁

/*
 * 死锁问题,处理线程同步时,容易出现这个问题
 * 不同的线程分别占用对方的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
 * 如下例:第一个线程在执行的时候,首先握住sb1这个锁,要想释放sb1锁,必须先要执行完里面握住sb2锁的操作执行完毕才可以。但是在这个过程中,另一个线程可能在第一个线程拿到sb2锁,并且第二个线程也需要调用sb1锁才能释放sb2锁,这样就会导致双方都握住对方的锁,但是都不能执行,从而造成死锁。
 */
public class TestDeadLock {
	static StringBuffer sb1 = new StringBuffer();
	static StringBuffer sb2 = new StringBuffer();
	public static void main(String[] args) {
		new Thread(){
			public void run(){
				synchronized (sb1) {
					try {
						Thread.currentThread().sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					sb1.append("A");
					synchronized (sb2) {
						sb2.append("B");
					}
				}
				System.out.println(sb1);
				System.out.println(sb2);
			}
		}.start();		
		new Thread(){
			public void run(){
				synchronized (sb2) {
					try {
						Thread.currentThread().sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					sb2.append("C");
					synchronized (sb1) {
						sb1.append("D");
					}
				}
				System.out.println(sb1);
				System.out.println(sb2);
			}			
		}.start();	
	}
}

8、生产者消费者例子

/*
 * 生产者消费者模式
 * 1、是否涉及多线程;生产者、消费者
 * 2、是否涉及到共享数据?有!
 * 3、此共享数据是谁?即为产品数量
 * 4、是否涉及到线程的通信?存在着生产者与消费者的通信
 */
class Clerk{
	int produtor;
	public synchronized void addProdutor(){
		
		if(produtor>=20){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else {
			produtor++;
			System.out.println(Thread.currentThread().getName()+"生产了第:"+produtor+"个产品");
			notifyAll();
		}
	}
	public synchronized void consumeProductor(){
		if(produtor<=0){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else {
			produtor--;
			System.out.println(Thread.currentThread().getName()+"消费了第:"+produtor+"个产品");
			notifyAll();
		}
	}
}
class Producer implements Runnable{
	Clerk clerk;
	public  Producer(Clerk clerk) {
		this.clerk = clerk;
	}
	@Override
	public void run() {
		System.out.println("生产者开始生产产品");
		while(true){
			try {
				Thread.currentThread().sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			clerk.addProdutor();
		}
	}	
}
class Consumer implements Runnable{
	Clerk clerk;
	public  Consumer(Clerk clerk) {
		this.clerk = clerk;
	}
	@Override
	public void run() {
		while(true){
			try {
				Thread.currentThread().sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			clerk.consumeProductor();
		}
	}	
}
public class TestProduceAndCutomer {
	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		Producer p = new Producer(clerk);
		Consumer c = new Consumer(clerk);
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(c);
		Thread t3 = new Thread(p);
		t1.start();
		t2.start();
		t3.start();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值