黑马程序员---Java基础---多线程

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


一.创建线程

         1.继承Thread

                   定义类继承Thread类

                   重写Thread类的run()方法

                   创建该类对象

                   调用start()方法即可开启新线程, 新线程会自动执行run()方法

class ThreadDemo1 {
	
	public static void main(String[] args) {
		System.out.println("程序开始");
		
		// 创建线程对象, 调用start()方法, 程序会开启一条新线程, 新线程上执行run()方法
		MyThread mt = new MyThread();
		mt.start();		// 开新线程找run()方法, 自己重写了, 就找自己的
		
		for (int i = 1; i <= 100; i++)
			System.out.println("A " + i);
		
		System.out.println("程序结束");
	}	
	
}

// 定义类继承Thread类
class MyThread extends Thread {
	
	// 重写run方法
	public void run() {
		// 将新线程中要做的事放在run方法中
		for (int i = 1; i <= 100; i++)
			System.out.println("B " + i);
	}
	
}

         2.实现Runnable

                   定义类实现Runnable接口

                   实现Runnable接口的run()方法

                   创建自定义Runnable对象

                   创建Thread对象, 将Runnable对象传入Thread类的构造函数

                   调用start()方法即可开启新线程, 新线程会自动执行Runnable的run()方法

class ThreadDemo2 {
	
	public static void main(String[] args) {
		System.out.println("主线程开始");
		
		// 创建自定义的Runnable, 创建Thread对象将Runnable传入, 调用Thread的start(). 开启新线程, 新线程就会执行Runnable的run()方法
		MyRunnable mr = new MyRunnable();
		Thread t = new Thread(mr);
		t.start();		// 开新线程找run()方法, Thread类的run()方法判断有没有传Runnable, 传了就执行Runnable的run()
		
		for (int i = 1; i <= 100; i++)
			System.out.println("A " + i);
		
		System.out.println("主线程结束");
	}	
	
}

// 定义类实现Runnable接口
class MyRunnable implements Runnable {
	
	// 重写run()方法
	public void run() {
		// 将新线程要做的事写在run()方法中
		for (int i = 1; i <= 100; i++)
			System.out.println("B " + i);
	}
	
}

         3.使用匿名内部类

                   newThread(){

                            publicvoid run(){

                   }}.start();

 

                   newThread(new Runnable(){

                   publicvoid run(){

                   }}).start();

class ThreadDemo3 {
	
	public static void main(String[] args) {

		//
		// 定义Thread类的子类, 创建对象, 调用start()方法
		new Thread(){
			public void run() {
				for (int i = 1; i <= 100; i++)
					System.out.println(getName() + ": A " + i);
			}
		}.start();
		
		// 定义Runnable子类, 创建对象, 将Runnable对象传入Thread类的构造函数, 调用start()方法
		new Thread(new Runnable(){
			public void run() {
				for (int i = 1; i <= 100; i++)
					System.out.println(Thread.currentThread().getName() + ": B " + i);
			}
		}).start();
		
	}	
	
}

二.线程常用方法

         1.currentThread

                   静态方法, 获取当前的线程对象

         2.getName,setName

                   获取和设置线程的名字

class ThreadMethodDemo1 {
	
	public static void main(String[] args) {
		MyRunnable mr = new MyRunnable();
		
		Thread t1 = new Thread(mr, "线程1");
		Thread t2 = new Thread(mr, "线程2");
		
		t1.start();
		t2.start();		
	}	
	
}

class MyRunnable implements Runnable {
	public void run() {
		for (int i = 1; i <= 1000; i++)
			System.out.println(Thread.currentThread().getName() + ": " + i);
	}
}

         3.sleep

                   静态方法, 控制当前线程休眠指定毫秒

class ThreadMethodDemo2 {
	
	public static void main(String[] args) {
		MyRunnable mr = new MyRunnable();
		
		Thread t1 = new Thread(mr, "线程1");
		Thread t2 = new Thread(mr, "线程2");
		
		t1.start();
		t2.start();		
	}	
	
}

class MyRunnable implements Runnable {
	public void run() {
		for (int i = 1; i <= 1000; i++) {
			System.out.println(Thread.currentThread().getName() + ": " + i);
			try {
				Thread.sleep(1000);			// 线程休眠1000毫秒, 由于Runnable中的run()没有声明异常, 所以只能catch
			} catch(Exception e) {
				e.printStackTrace();	
			}
		}
	}
}

         4.setDaemon

                   设置指定线程为守护线程, 守护线程不会单独运行

class ThreadMethodDemo3 {
	
	public static void main(String[] args) {
		Thread t1 = new Thread(){
			public void run() {
				for (int i = 1; i <= 5; i++) {
					System.out.println("A " + i);
					try {
						Thread.sleep(1000);			
					} catch(Exception e) {
						e.printStackTrace();	
					}
				}
			}	
		};
		
		Thread t2 = new Thread(){
			public void run() {
				for (int i = 1; i <= 10; i++) {
					System.out.println("B " + i);
					try {
						Thread.sleep(1000);			
					} catch(Exception e) {
						e.printStackTrace();	
					}
				}
			}	
		};
		
		t2.setDaemon(true);		// 将t2线程设置为守护线程, 如果程序中只剩守护线程了, 程序直接结束
		
		t1.start();
		t2.start();
	}	
	
}

         5.join

                   当前线程暂停, 等待指定线程执行完毕后再继续

class ThreadMethodDemo4 {
	
	public static void main(String[] args) {
		final Thread t1 = new Thread(){
			public void run() {
				for (int i = 1; i <= 5; i++) {
					System.out.println("A " + i);
					try {
						Thread.sleep(1000);			
					} catch(Exception e) {
						e.printStackTrace();	
					}
				}
			}	
		};
		
		Thread t2 = new Thread(){
			public void run() {
				for (int i = 1; i <= 5; i++) {
					System.out.println("B " + i);
					try {
						Thread.sleep(1000);			
						if (i == 2)
							t1.join();			// 当前线程暂停, 等待t1线程执行结束之后再继续
					} catch(Exception e) {
						e.printStackTrace();	
					}
				}
			}	
		};
		
		t1.start();
		t2.start();
	}	
	
}
                   

三.同步

         1.什么是同步

                   在多线程并发访问同一资源时,有可能出现线程安全问题.

                   为了解决这种问题, 我们就可以使用同步技术.

                   将多个线程访问资源的代码进行同步.

         2.同步代码块

                   将需要同步的代码放在synchronized(){} 代码块中, 在小括号中指定锁对象

                   多段使用相同锁的同步代码块,同一时间只能执行一个, 必须等待一个执行结束后其他的才能继续执行

         3.同步方法

                   如果整个方法内的代码都需要同步, 可以在方法前加上synchronized修饰

                   只有在同一个类中的几个方法可以使用同步方法的形式进行同步, 如果不同类中的几个方法想要同步, 必须使用同步代码块

                   因为同步方法默认使用this作为锁对象

         4.死锁

                   多线程并发执行时, 如果多段同步代码嵌套使用,有可能互相冲突产生死锁, 导致程序阻塞.

                   在编程的过程中尽量不要将同步代码嵌套使用, 避免产生死锁.

class DeadLockDemo {
	
	private static Object obj1 = new Object();
	private static Object obj2 = new Object();
	
	public static void main(String[] args) {
		new Thread(){
			public void run() {
				synchronized(obj1) {
					System.out.println("第一条线程锁定obj1");
					synchronized(obj2) {	// 要等21行执行结束
						System.out.println("第一条线程锁定obj2");
					}
				}	
			}	
		}.start();
		
		new Thread(){
			public void run() {
				synchronized(obj2) {
					System.out.println("第二条线程锁定obj2");
					synchronized(obj1) {	// 要等12行执行结束
						System.out.println("第二条线程锁定obj1");
					}
				}	
			}	
		}.start();
	}	
	
}
                   

四.通信

         1.什么是通信

                   在多线程并发执行的时候, 可以使用wait()和notify()方法在多个线程间互相通信

         2.怎么使用

                   通信的代码必须写在同步代码中, 必须使用锁对象来调用wait()和notify()

                   wait()方法可以控制当前线程等待,直到其他线程调用notify()或者notifyAll()方法才被唤醒

 

五.JDK5的同步

         1.怎么同步

                   使用ReentrantLock类的lock()方法开始同步

                   使用ReentrantLock类的unlock()方法结束同步

         2.注意

                   解锁的代码unlock()方法一定要执行, 尽量放在finally中

                  

六.JDK5的通信

         1.怎么通信

                   先用ReentrantLock调用newCondition()方法获得Condition对象

                   使用Condition对象的await()方法等待

                   使用Condition对象的signal()方法唤醒

         2.JDK5和以前通信的区别

                   JDK5之前只能使用锁对象wait()和notify().

                   多个线程只能在同一个对象上等待, 唤醒的时候要么唤醒随机一个, 要么唤醒所有的, 无法指定唤醒其中一个.

                   JDK5之后可以创建多个Condition对象, 不同的线程使用不同的Condition

                   唤醒的时候可以唤醒指定的Condition

七.练习

开启3条线程, 轮流执行打印操作

import java.util.concurrent.locks.*;

/*
	开启3条线程, 轮流执行打印操作, 执行结果如下:
	
	线程1: 1
	线程1: 2
	线程1: 3
	
	线程2: 4
	线程2: 5
	线程2: 6
	
	线程3: 7
	线程3: 8
	线程3: 9
	
	线程1: 10
	线程1: 11
	线程1: 12
	
	线程2: 13
	线程2: 14
	线程2: 15
	
	线程3: 16
	线程3: 17
	线程3: 18
	
	线程1: 19
	线程1: 20
	线程1: 21
	
	线程2: 22
	线程2: 23
	线程2: 24
	
	线程3: 25
	线程3: 26
	线程3: 27
*/
class WorkDemo {
	public static void main(String[] args) {
		final Print p = new Print();

		new Thread(){
			public void run() {
				for(int i = 0; i < 3; i++)
					try {
						p.print1();
					} catch(Exception e) {
						e.printStackTrace();
					}
			}	
		}.start();
		
		new Thread(){
			public void run() {
				for(int i = 0; i < 3; i++)
					try {
						p.print2();
					} catch(Exception e) {
						e.printStackTrace();
					}
			}	
		}.start();	
		
		new Thread(){
			public void run() {
				for(int i = 0; i < 3; i++)
					try {
						p.print3();
					} catch(Exception e) {
						e.printStackTrace();
					}
			}	
		}.start();	
	}
}
class Print {
	/*JDK1.5以前的同步和通信使用
	private static int i = 1;
	private int flag = 1;

	public synchronized void print1() throws Exception {	
		while (flag != 1)		
			this.wait();
		System.out.println("线程1: " + i++);
		System.out.println("线程1: " + i++);
		System.out.println("线程1: " + i++);
		System.out.println();
		flag = 2;
		this.notifyAll();
	}
	public synchronized void print2() throws Exception {
		while (flag != 2)	
			this.wait();
		System.out.println("线程2: " + i++);
		System.out.println("线程2: " + i++);
		System.out.println("线程2: " + i++);
		System.out.println();
		flag = 3;
		this.notifyAll();
	}
	public synchronized void print3() throws Exception {
		while (flag != 3)	
			this.wait();
		System.out.println("线程3: " + i++);
		System.out.println("线程3: " + i++);
		System.out.println("线程3: " + i++);
		System.out.println();
		flag = 1;
		this.notifyAll();
	}
	*/

	//jDK1.5以后的同步和通信使用
	private static int i = 1;
	private int flag = 1;

	private ReentrantLock lock = new ReentrantLock();	//锁对象
	private Condition c1 = lock.newCondition();			//使用lock对象获取一个和当前对象相关联的同步监视器
	private Condition c2 = lock.newCondition();			//同步监视器
	private Condition c3 = lock.newCondition();			//同步监视器

	public void print1() throws Exception {
		lock.lock();			//使用c1上锁
		if (flag != 1)		
			c1.await();			//c1等待
		System.out.println("线程1: " + i++);
		System.out.println("线程1: " + i++);
		System.out.println("线程1: " + i++);
		System.out.println();
		flag = 2;
		c2.signal();			//唤醒c2
		lock.unlock();
	}
	public void print2() throws Exception {
		lock.lock();			//使用c2上锁
		if (flag != 2)	
			c2.await();			//c2等待
		System.out.println("线程2: " + i++);
		System.out.println("线程2: " + i++);
		System.out.println("线程2: " + i++);
		System.out.println();
		flag = 3;
		c3.signal();			//唤醒c3
		lock.unlock();
	}
	public void print3() throws Exception {
		lock.lock();			//使用c3上锁
		if (flag != 3)	
			c3.await();			//c3等待
		System.out.println("线程3: " + i++);
		System.out.println("线程3: " + i++);
		System.out.println("线程3: " + i++);
		System.out.println();
		flag = 1;
		c1.signal();			//唤醒c1
		lock.unlock();
	}

}


------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值