JavaSe——线程_Part2

A.JDK5之后的Lock锁的概述和使用

l  虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

l  Lock[是一个接口]

•    void lock()

•    void unlock()

l  ReentrantLock[是Lock的一个实现子类]

package com.core.thread.demo14;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class SellTicket implements Runnable {
	private static int ticketNum = 100;
	Lock lock = new ReentrantLock();
	@Override
	public void run() {
		while (true) {
			lock.lock();//加锁
			if (ticketNum > 0) {
				try {
					Thread.currentThread().sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售"
						+ ticketNum + "张票");
				ticketNum--;
			}
			lock.unlock();//释放锁
		}
	}
}

测试类:

package com.core.thread.demo14;

//某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票
//实现Runnable接口
public class Test {

	public static void main(String[] args) {
		SellTicket st = new SellTicket();
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");
		t1.start();
		t2.start();
		t3.start();
	}

}

B.死锁

l同步弊端
效率低
如果出现了同步嵌套,就容易产生死锁问题
l死锁问题及其代码
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
同步代码块的嵌套案例
写一个MyLock

package com.core.thread.demo15;

public class MyLock {
	public static Object objA = new Object();
	public static Object objB = new Object();
}

package com.core.thread.demo15;

public class DeadLock extends Thread{
	
	private boolean flag;
	public DeadLock(boolean flag){
		this.flag = flag;
	}

	@Override
	public void run() {
		if(flag){
			synchronized (MyLock.objA) {
				System.out.println("if objA");
				synchronized (MyLock.objB) {
					System.out.println("if objB");
				}
			}
		}else{
			synchronized (MyLock.objB) {
				System.out.println("else objB");
				synchronized (MyLock.objA) {
					System.out.println("else objA");
				}
			}
		}
	}
	
}

package com.core.thread.demo15;

public class Test {
	public static void main(String[] args) {
		Thread t1 = new DeadLock(true);
		Thread t2 = new DeadLock(false);
		
		t1.start();
		t2.start();
	}
}

C.生存者和消费者问题

线程间通信问题:不同类型的线程间针对同一资源的操作。

案例1:

l  通过设置线程(生产者)和获取线程(消费者)针对同一个学生对象进行操作

package com.core.thread.demo1;

public class Student {
	private String name;
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}


package com.core.thread.demo1;

public class GetThread implements Runnable {
	private Student s;
	public GetThread(Student s){
		this.s = s;
	}
	@Override
	public void run() {
		System.out.println(s.getName()+"----"+s.getAge());
	}

}

package com.core.thread.demo1;

public class SetThread implements Runnable {
	private Student s;
	public SetThread(Student s){
		this.s = s;
	}

	@Override
	public void run() {
		s.setName("大黄");
		s.setAge(20);
	}

}

package com.core.thread.demo1;

public class Test {

	public static void main(String[] args) {
		Student s = new Student();
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t2.start();
		t1.start();
	}

}

另一个例子:

package com.core.thread.demo2;

public class SetThread implements Runnable {
	private Student s;
	private int x = 0;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			if (x % 2 == 0) {
				s.setName("大黄");
				s.setAge(20);
			} else {
				s.setName("小黑");
				s.setAge(18);
			}
			x++;
		}
	}

}

package com.core.thread.demo2;

public class GetThread implements Runnable {
	private Student s;
	public GetThread(Student s){
		this.s = s;
	}
	@Override
	public void run() {
		while(true){
			System.out.println(s.getName()+"----"+s.getAge());
		}
	}

}

线程安全问题:
A.是否是多线程
B.是否有共享数据
C.是否多条语句操作共享数据

解决方法:
不同的线程用同一把锁加锁。
package com.core.thread.demo2;

public class SetThread implements Runnable {
	private Student s;
	private int x = 0;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (s) {
				if (x % 2 == 0) {
					s.setName("大黄");
					s.setAge(20);
				} else {
					s.setName("小黑");
					s.setAge(18);
				}
				x++;
			}
		}
	}

}


package com.core.thread.demo2;

public class GetThread implements Runnable {
	private Student s;
	public GetThread(Student s){
		this.s = s;
	}
	@Override
	public void run() {
		while(true){
			synchronized (s) {
				System.out.println(s.getName()+"----"+s.getAge());
			}
		}
	}

}

D.等待唤醒机制

JavaSe——线程_Part2_第1张图片
也就是说:必须现有生产者再有消费者,生产者生产之后,必须等待消费者消费完之后,才可以继续生产。


如何实现?
通过Java提供的等待唤醒机制解决。
带等唤醒:
Object类中提供了三个方法。
wait():等待
notify():唤醒单个线程
notifyAll():唤醒所有线程

那么这些方法为什么不定义在Thread类中呢?
这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象,所以,这些方法必须定义在Object类中。


案例:
package com.core.thread.demo2;

public class Student {
	public boolean flag = false;
	private String name;
	private int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

package com.core.thread.demo2;

public class SetThread implements Runnable {
	private Student s;
	private int x = 0;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {

			synchronized (s) {
				if (s.flag) {// 如果有,则不用创建
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				if (x % 2 == 0) {
					s.setName("大黄");
					s.setAge(20);
				} else {
					s.setName("小黑");
					s.setAge(18);
				}
				x++;

				s.flag = true;// 修改标记
				s.notify();// 唤醒
			}

		}
	}

}


package com.core.thread.demo2;

public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {

			synchronized (s) {
				if (!s.flag) {// 如果没有,则等待
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println(s.getName() + "----" + s.getAge());
				s.flag = false;// 修改标记
				s.notify();// 唤醒机制
			}

		}
	}

}


案例:

package com.core.thread.demo2;

public class SetThread implements Runnable {
	private Student s;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {

			synchronized (s) {
				if (s.flag) {// 如果有,则不用创建
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				s.setName("大黄");
				s.setAge(20);
				System.out.println("生产一个");
				s.flag = true;// 修改标记
				s.notify();// 唤醒
			}

		}
	}

}


package com.core.thread.demo2;

public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {

			synchronized (s) {
				if (!s.flag) {// 如果没有,则等待
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("消费一个"+s.getName() + "----" + s.getAge());
				s.flag = false;// 修改标记
				s.notify();// 唤醒机制
			}

		}
	}

}

E.线程的状态转换图及常见执行情况

JavaSe——线程_Part2_第2张图片

F.线程组的概述和使用

l  Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

•    默认情况下,所有的线程都属于主线程组。

•    public final ThreadGroup getThreadGroup()

•    我们也可以给线程设置分组

•    Thread(ThreadGroup group, Runnable target,String name) 


package com.core.thread.demo4;

public class Test {

	public static void main(String[] args) {
		MyRunnable runnable = new MyRunnable();
		Thread t1 = new Thread(runnable, "线程1");
		Thread t2 = new Thread(runnable, "线程2");
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();
		System.out.println(tg1.getName());// main,默认都属于主线程组
		System.out.println(tg2.getName());// main
		System.out.println("----------");
		ThreadGroup newTG = new ThreadGroup("新开的一个线程组");
		Thread tg11 = new Thread(newTG, runnable);
		Thread tg22 = new Thread(newTG, runnable);
		System.out.println(tg11.getThreadGroup().getName());// 新开的一个线程组
		System.out.println(tg22.getThreadGroup().getName());// 新开的一个线程组
		// 通过操作线程组可以操作里面的线程状态了。
	}

}



G.生产者消费者之等待唤醒机制代码优化


A:把Student的成员变量私有
B.把获取和设置封装成方法,并加锁。
C.在设置和获取的线程里面,只要调用方法即可


package com.core.thread.demo5;

public class Student {
	private String name;
	private int age;
	private boolean flag = false;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public synchronized void setStudent(String name, int age) {
		if (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		setName(name);
		setAge(age);
		System.out.println("生产了一个");
		this.flag = true;
		this.notify();
	}

	public synchronized void getStudent() {
		if (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("消费了一个" + getName() + "---" + getAge());
		this.flag = false;
		this.notify();
	}
}

package com.core.thread.demo5;

public class SetThread implements Runnable {
	private Student s;
	private int x = 0;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			s.setStudent("张三", 20);
		}
	}

}


package com.core.thread.demo5;

public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			s.getStudent();
		}
	}

}



package com.core.thread.demo5;

public class Test {

	public static void main(String[] args) {
		Student s = new Student();
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t2.start();
		t1.start();
	}

}


H.线程池

l  程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

•    线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

•    在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池


l  JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

•    public static ExecutorService newCachedThreadPool()

•    public static ExecutorService newFixedThreadPool(int nThreads)

•    public static ExecutorService newSingleThreadExecutor()

•    这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法

•    Future submit(Runnable task)

•    Future submit(Callable task)

•    案例演示

•    创建线程池对象

•    创建Runnable实例

•    提交Runnable实例

•    关闭线程池

package com.core.thread.demo6;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"---"+i);
		}
	}

}

测试类:
package com.core.thread.demo6;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	
	public static void main(String[] args) {
		//获得线程池
		ExecutorService pool = Executors.newFixedThreadPool(3);
		
		//可以执行Runnable或者Callable对象代表的线程
		//Runnable对象
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		
		//结束线程池
		pool.shutdown();
	}

}


I .多线程方式3的思路及代码实现


package com.core.thread.demo7;

import java.util.concurrent.Callable;

public class MyCallable implements Callable {

	@Override
	public Object call() throws Exception {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"--"+i);
		}
		return null;
	}

}

package com.core.thread.demo7;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {

	public static void main(String[] args) {
		//创建线程池
		ExecutorService pool = Executors.newFixedThreadPool(3);
		pool.submit(new MyCallable());
		pool.submit(new MyCallable());
		//结束线程池
		pool.shutdown();
	}

}


J.多线程方式3的求和案例

package com.core.thread.demo7;

import java.util.concurrent.Callable;

public class MyCallable implements Callable {
	private int num;
	private int sum;

	public MyCallable(int num) {
		this.num = num;
	}

	@Override
	public Integer call() throws Exception {
		for (int i = 0; i <= num; i++) {
			sum += i;
		}
		return sum;
	}

}


测试类:
package com.core.thread.demo7;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//创建线程池
		ExecutorService pool = Executors.newFixedThreadPool(2);
		
		Future f1 = pool.submit(new MyCallable(100));
		Future f2 = pool.submit(new MyCallable(200));
		
		Integer i1 = f1.get();
		Integer i2 = f2.get();
		System.out.println("i1 =" +i1);
		System.out.println("i2 =" +i2);
		
		//关闭线程
		pool.shutdown();
	}

}



K. 匿名内部类的方式实现多线程程序

package com.core.thread.demo9;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	// 匿名内部类
	public static void main(String[] args) {
		// 1. 继承Thread的方法
		new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					System.out.println("继承Thread的方法" + i);
				}
			}
		}.start();;

		// 2. 实现Runnable接口方法
		new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					System.out.println("实现Runnable接口方法" + i);
				}
			}
		}).start();
		// 3. Callable方法
		ExecutorService pool = Executors.newFixedThreadPool(1);
		pool.submit(new Callable() {

			@Override
			public Object call() throws Exception {
				for (int i = 0; i < 100; i++) {
					System.out.println("Callable方法" + i);
				}
				return null;
			}
		});
		
		//结束线程池
		pool.shutdown();
	}

}


L.定时器

package com.core.thread.timer;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import javax.management.timer.TimerMBean;

public class Test {
	public static void main(String[] args) {
		//创建一个定时器对象
//		Timer t = new Timer();
//		MyTimerTask task = new MyTimerTask();
//		//3秒后执行
//		System.out.println("开始"+new Date());
//		t.schedule(task, 3000);
		//注意上面的方法不能够自动取消任务
		//下面通过参数传递,将定时器t传到TimerTask里面
		
		Timer t = new Timer();
		System.out.println("开始"+new Date());
		t.schedule(new MyTimerTask2(t), 3000);
	}
}

// 这是一个任务
class MyTimerTask extends TimerTask {

	@Override
	public void run() {
		System.out.println("执行一项任务");
		System.out.println("结束"+new Date());
	}
}
class MyTimerTask2 extends TimerTask {
	private Timer t;
	public MyTimerTask2(Timer t){
		this.t = t;
	}
	
	@Override
	public void run() {
		System.out.println("执行一项任务");
		System.out.println("结束"+new Date());
		t.cancel();
	}
}



使用定时器不仅仅可以指定在某个时候执行某个任务,还可以重复做某个任务。
package com.core.thread.timer1;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import javax.management.timer.TimerMBean;

public class Test {
	public static void main(String[] args) {
		
		Timer t = new Timer();
		//3秒后第一次执行,以后每1秒执行一次
		t.schedule(new MyTimerTask(),3000,1000);
	}
}

class MyTimerTask extends TimerTask {
	@Override
	public void run() {
		System.out.println("执行一项任务");
	}
}


练习:定时删除指定目录的所有文件


M.小结




1:多线程有几种实现方案,分别是哪几种?

         两种。

        

         继承Thread类

         实现Runnable接口

        

         扩展一种:实现Callable接口。这个得和线程池结合。

 

2:同步有几种方式,分别是什么?

         两种。

        

         同步代码块

         同步方法

 

3:启动一个线程是run()还是start()?它们的区别?

         start();

        

         run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用

         start():启动线程,并由JVM自动调用run()方法

 

4:sleep()和wait()方法的区别

         sleep():必须指时间;不释放锁。

         wait():可以不指定时间,也可以指定时间;释放锁。

 

5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中

         因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。

         而Object代码任意的对象,所以,定义在这里面。

 

6:线程的生命周期图

         新建 -- 就绪 -- 运行 -- 死亡

         新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡

         建议:画图解释。


你可能感兴趣的:(JavaSe——线程_Part2)