复习
1.线程的三种实现方式
继承线程类java.lang.Thread,重写run方法。在run方法中定义线程的任务。
实现java.lang.Runnable接口,实现run方法,在run 方法中定义任务。
实现Callable 接口。实现call 方法,在call 方法中定义任务。
2 .线程的生命周期
新建 调用 start方法,进入 就绪(可以运行)状态,等待cpu调度执行。当cpu调度执行 进入 运行状态 ,当cpu时间片结束,从运行状态回到就绪状态。在运行状态中,当被阻塞的时候,进入阻塞状态,解除阻塞,进入就绪状态。如果调用了stop方法,或者被中断了,或者run方法结束,那么线程进入死亡状态。
3 方法
优先级,范围1-10
Join() 导致当前线程被阻塞的。
Thread.yield 导致当前线程从运行到就绪。
Thread.sleep 导致当前线程从运行到阻塞,超过指定是时间,解除。
守护线程:如果一个进程中所有的活动的线程都是守护线程,那么jvm杀死该进程。
线程中断。
4 线程安全
只有多线程的情况下,多个线程同时访问同一个数据,才存在线程安全问题。
解决的方法:同步代码块,同步方法,Lock 锁。
第一节监视器对象的选择、Lock
1. 监视器对象选择
如果想实现线程之间的互斥访问,那么监视器对象的选择,必须是唯一的,而且是不可改变的。
当一个类被加载的时候,就会产生一个对象(在堆中),该对象唯一,而且不可变。类型是Class 大class类型。Class 类是描述类的类。该对象通常是作为监视器对象使用的。可以通过 类名.class 来访问该对象。
说明:
(为什么是不可变的呢,假如有一个监视器对象是Object o,有一个线程将它锁住了,但是在线程内部又将这个引用指向了其他对象,那么这个时候,其他线程在进来的时候就可以进来了,所以得是唯一的。)(监视器对象可以是this、也可以是static final Object o 但是还得单独创建一个对象,那么有没有一种东西,已经存在了,而且是唯一的不可变的?有。例如:在下面的代码中的Account 在类加载的时候就会产生一个对象,在类加载的时候其实产生 两部分内存,一部分是类的字节码的源数据会加载到方法区还会产生一个对象,这个涉及到我们后面的内容反射了,这个对象在堆中,类型是Class,Class是描述类的类。如何得到这个Class类的对象呢?类名.class 例如:Account.class。所以以后这个同步代码块在哪个类中用,就用那个类的大Class就行了。)
例子:
synchronized (Account.class) {} |
---|
同步方法:本质上也是使用监视器对象实现线程互斥访问。监视器对象是隐式指定的。
同步的实例方法,它的隐式的监视器对象是:this
同步的静态方法,它的隐式的监视器对象是:当前类的 Class 对象。
2. Lock 锁实现线程的互斥访问
说明:Lock 锁是在jdk1.5之后推出的。是针对高并发,还要追求安全,提出相关的线程同步的内容。Lock 是一个接口。用的是它的实现的子类。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 学习锁
*/
public class TestLock {
public static void main(String[] args) {
AccountRunnable accountRunnable0=new AccountRunnable();
Thread thread1=new Thread(accountRunnable0,"张三");
Thread thread2=new Thread(accountRunnable0,"张三媳妇");
thread1.start();
thread2.start();
}
}
//账户类
class Account{
//账户余额
private int money=1500;
//创建一个可重入锁对象
//传入true代表是一个公平锁,等待请求锁资源线程时间最长的线程
//会被优先获得锁资源,效率会受到一定的影响。
private Lock lock=new ReentrantLock();
//取钱的方法,返回是否取钱成功
public boolean withDraw(int money){
//效率比同步代码块和同步方法效率都高
lock.lock();
try {
if(this.money>=money){
//问题扩大
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.money-=money;
System.out.println(Thread.currentThread().getName()+"取钱成功!余额还剩:"+this.money);
return true;
}
} finally {
lock.unlock();//是必须要执行的
}
System.out.println(Thread.currentThread().getName()+"取钱失败!余额还剩:"+this.money);
return false;
}
}
//任务类,去同一个账户取钱
class AccountRunnable implements Runnable{
Account account=new Account();
@Override
public void run() {
account.withDraw(1000);
}
}
3 .死锁
**
* 死锁的学习
*/
public class TestDeadLock {
public static void main(String[] args) {
DeadLockThread thread0=new DeadLockThread(0);
DeadLockThread thread1=new DeadLockThread(1);
thread0.start();
thread1.start();
}
}
class DeadLockThread extends Thread{
private int id;
public static final Object o1=new Object();
public static final Object o2=new Object();
public DeadLockThread(int id) {
this.id = id;
}
@Override
public void run() {
if(id==0){
//线程-0访问的代码
synchronized (o1){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"--o1--o2--");
///stop here 线程-1 锁住了o1请求,锁o2
synchronized (o2){
System.out.println(getName()+"--o2--o1--");
}
}
}else{
//线程-1访问的代码
synchronized (o2){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"-o2-o1-");
//stop here 线程-2 锁住了o2请求,锁o1
synchronized (o1){
System.out.println(getName()+"-o1-o2-");
}
}
}
}
}
第二节 生产者消费者问题
/**
*测试类
*/
public class Test {
public static void main(String[] args) {
MyStack<Steam> myStack=new MyStack<>();
Consumer consumer=new Consumer(myStack);
Producer producer=new Producer(myStack);
consumer.start();
producer.start();
}
}
import javax.xml.bind.annotation.XmlType;
/**
*wait():第一:让当前线程在当前对象上等待。第二:并对当前对象解锁。
* notify():唤醒当前对象上的某个线程
*/
public class MyStack<E> {
//定义栈的初始容量
private static final int DEFAULT_CAPACITY=3;
//底层使用数组实现
private Object[] elementDate;
//栈顶指针
private int index;
//构造方法
public MyStack() {
elementDate=new Object[DEFAULT_CAPACITY];
}
//压栈操作 生产者线程 需要执行的内容
public synchronized void push(E o){
if(isFull()){
//栈满了
try {
//让生产者线程在this上等待,从运行状态进入阻塞状态
//并释放this上的锁,对this解锁
//当前线程必须对this上锁(当前线程必须拥有此对象监视器)
//监视器对象必须是谁调用了wait谁必须是那个监视器对象。
System.out.println("生产者等待了");
this.wait();//让生产者线程在容器对象上等待。谁调用wait()谁是当前对象,谁执行this.wait()这一行代码,谁是当前线程。
System.out.println("生产者被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产者将商品放入容器的过程
elementDate[index++]=o;
System.out.println(Thread.currentThread()+"生产了"+o);
//通知消费者可以继续消费了。
this.notify();
}
//消费者线程执行的内容
public synchronized E pop(){
//栈空了
if(isEmpty()){
try {
//让消费者线程,在this上等待
//消费者线程从运行状态进入阻塞状态,对this解锁
System.out.println("消费者等待了");
this.wait();
System.out.println("消费者被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//指针下移,将指针走过的数据下移
E o=(E)elementDate[--index];
//避免内存泄漏
elementDate[index]=null;
System.out.println(Thread.currentThread().getName()+"消费了"+o);
//唤醒在this上等待的某一个线程(生产者),从阻塞状态进入到就绪状态。
//消费者消费了一个商品,可以通知生产者继续生产。
this.notify();
return o;
}
//判断栈是否满了
boolean isFull(){
return index==elementDate.length;
}
//判断栈是否空l
boolean isEmpty(){
return index==0;
}
}
**
*生产者
*/
public class Producer extends Thread {
private MyStack<Steam> stack;
public Producer(MyStack<Steam> stack) {
this.stack = stack;
}
@Override
public void run() {
for (int i = 0; i < 6; i++) {
stack.push(new Steam(i));
}
}
}
/**
* 消费者
*/
public class Consumer extends Thread{
private MyStack<Steam> stack;
public Consumer(MyStack<Steam> stack) {
this.stack = stack;
}
@Override
public void run() {
for (int i = 0; i < 6; i++) {
stack.pop();
}
}
}
/**
*商品类
*/
public class Steam {
private int i;
public Steam(int i) {
this.i = i;
}
@Override
public String toString() {
return "馒头{" +
"i=" + i +
'}';
}
}
//如果同步代码中用MyStack.class作为监视器的话,那么在调用wait()方法的时候也必须用MyStack.class
//压栈操作 生产者线程 需要执行的内容
public void push(E o){
synchronized (MyStack.class){
if(isFull()){
//栈满了
try {
//让生产者线程在this上等待,从运行状态进入阻塞状态
//并释放this上的锁,对this解锁
//当前线程必须对this上锁
System.out.println("生产者等待了");
MyStack.class.wait();
System.out.println("生产者被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产者将商品放入容器的过程
elementDate[index++]=o;
}
System.out.println(Thread.currentThread()+"生产了"+o);
//通知消费者可以继续消费了。
this.notify();
}
3 jdk1.5针对死锁问题的解决方案
//这里只是部分代码块,不可运行
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*多个消费者生产者导致的死锁问题解决方案(dk1.5针对死锁问题的解决方案)
*/
public class MyStack<E> {
//定义栈的初始容量
private static final int DEFAULT_CAPACITY=3;
//底层使用数组实现
private Object[] elementDate;
//栈顶指针
private int index;
Lock lock =new ReentrantLock();
//生产者监视器
Condition proCon=lock.newCondition();
//生产者监视器
Condition conCon=lock.newCondition();
//构造方法
public MyStack() {
elementDate=new Object[DEFAULT_CAPACITY];
}
//压栈操作 生产者线程 需要执行的内容
public void push(E o){
lock.lock();
try {
while(isFull()){
try {
System.out.println(Thread.currentThread().getName()+"等待了");
proCon.await();
System.out.println(Thread.currentThread().getName()+"换醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
elementDate[index++]=o;
System.out.println(Thread.currentThread()+"生产了"+o);
conCon.signal();
} finally {
lock.unlock();
}
}
//消费者线程执行的内容
public E pop(){
lock.lock();
try {
while(isEmpty()){
try {
System.out.println(Thread.currentThread().getName()+"等待了");
conCon.await();
System.out.println(Thread.currentThread().getName()+"唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
E o=(E)elementDate[--index];
elementDate[index]=null;
System.out.println(Thread.currentThread().getName()+"消费了"+o);
proCon.signal();
return o;
} finally {
lock.unlock();
}
}
//判断栈是否满了
boolean isFull(){
return index==elementDate.length;
}
//判断栈是否空l
boolean isEmpty(){
return index==0;
}
}
第三节 线程池
Thread pool 只需要用户给线程池对象提供任务即可。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* 线程池
*/
public class TestPool {
public static void main(String[] args) throws Exception{
//test();
test1();
}
//使用线程池技术处理多个Runnable任务
static void test(){
//创建线程池对象
//创建一个只有一个线程的线程池对象
ExecutorService pool= Executors.newSingleThreadExecutor();
//创建一个动态数量的线程池对象,根据任务的数量,动态创建和销毁
ExecutorService pool1=Executors.newCachedThreadPool();
//创建固定数量的线程的线程池
ExecutorService pool2=Executors.newFixedThreadPool(10);
//创建了20个任务
for (int i = 0; i < 20; i++) {
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"\t开始执行任务");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t任务结束了");
}
};
//让Runnable 任务给线程池去执行
pool1.execute(runnable);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//关闭线程池,所有任务都结束之后,关闭
pool1.shutdown();
}
//使用线程池执行大量的Callable任务
static void test1() throws Exception{
ExecutorService pool=Executors.newFixedThreadPool(10);
//使用容器管理,线程执行的结果数据
List<Future<Integer>> list=new ArrayList<>();
for (int i = 0; i < 20; i++) {
//20个任务
Callable<Integer> callable=new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"\t开始执行任务");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"\t任务结束了");
return (int)(Math.random()*100);
}
};
//提交任务
Future<Integer> future=pool.submit(callable);
list.add(future);
}
//获得每一个线程的结果
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).get());
}
//关闭
pool.shutdown();
}
}
第四节单例设计模式
1 .单例模式
需求:希望某一个类只有一个唯一的实例。Singleton。
/**
*单例设计模式
*/
public class TestSingleton {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
System.out.println(MySingleton2.getInstance());
}
}.start();
new Thread(){
@Override
public void run() {
System.out.println(MySingleton2.getInstance());
}
}.start();
}
}
//设计一个单例类 饿汉模式
class MySingleton1{
//instance指向唯一的实例
//类加载的时候执行,只执行一次
//恶汉模式:类加载的时候就创建了唯一的对象,对系统的开销相对较大
//可能会产生影响软件启动的延时问题
private final static MySingleton1 instance =new MySingleton1();
//私有化构造方法
private MySingleton1() {
}
//必须对外提供一个可以访问唯一实例的方法,并返回唯一实例
public static MySingleton1 getInstance(){
return instance;
}
}
//设计一个单例类 懒汉
class MySingleton2{
//instance指向唯一的实例
private static MySingleton2 instance;
//私有化构造方法
private MySingleton2(){}
//必须对外提供一个可以访问唯一实例的方法。并返回该唯一实例
//懒汉模式,就是在第一次调用getInstance()方法的时候创建唯一的实例
public static MySingleton2 getInstance(){
//提高效率,避免每次都进行同步监视器锁的判断
if(instance==null){
// 2stop
synchronized (MySingleton2.class){
// 1stop
if(instance==null){
// stop here
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance=new MySingleton2();
}
}
}
return instance;
}
}
**
* 一道面试题
*/
public class Test9 {
public static void main(String[] args) {
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("222");
}
};
//new Thread(runnable).start();
new Thread(){ //这里是父类引用指向子类对象,重写了run方法 所以执行的是子类中的run 这是多态
@Override
public void run() {
//super.run();
System.out.println("111");
}
}.start();
}
}
问题:new Thread(runnable).start() 为什么把runnable传进去之后就 执行的是Runnable里面的run方法呢?看源码:
//先看Thread类的源码:
public
class Thread implements Runnable {
}
----------------------------------------------------------------------------------------------------------
/* What will be run. */
private Runnable target; //有一个target属性
-----------------------------------------------------------------------------------------------------------
public Thread(Runnable target) { //需要一个Runnable类型的实参出入
init(null, target, "Thread-" + nextThreadNum(), 0);
}
-----------------------------------------------------------------------------------------------------------
//再点init()方法往里看
private void init(ThreadGroup g, Runnable target, String name, //需要一个Runnable类型的实参传入
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target; ///就是这句代码导致的,把runnable传进去之后就 执行的是Runnable里面的run方法
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
/**
* Throws CloneNotSupportedException as a Thread can not be meaningfully
* cloned. Construct a new Thread instead.
*
* @throws CloneNotSupportedException
* always
*/
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
-----------------------------------------------------------------------------------------------------------
每日一练:
0:模拟十个人穿越山洞,只能一个人一个人的通过山洞,打印每个人穿越山洞的次序.
使用线程实现,穿越是线程互斥的.
1:使用wait notify notifyAll 实现功能.
一个线程打印 数字 一个线程打印 A-Z
打印的结果:12A34B56C…
import java.util.concurrent.Callable;
/**
*1.模拟十个人穿越山洞,只能一个人一个人的通过山洞,打印每个人穿越山洞的次序.
*使用线程实现,穿越是线程互斥的.
*/
public class MulThreadTest{
public static void main(String[] args) {
//创建一个山洞(0.5分)
Tunnel tul = new Tunnel();
//创建十个过山洞线程(1.0分)
Thread p1 = new Thread(tul,"p1");
Thread p2 = new Thread(tul,"p2");
Thread p3 = new Thread(tul,"p3");
Thread p4 = new Thread(tul,"p4");
Thread p5 = new Thread(tul,"p5");
Thread p6 = new Thread(tul,"p6");
Thread p7 = new Thread(tul,"p7");
Thread p8 = new Thread(tul,"p8");
Thread p9 = new Thread(tul,"p9");
Thread p10 = new Thread(tul,"p10");
//启动十个线程0.5分
p1.start(); p2.start();
p3.start(); p4.start();
p5.start(); p6.start();
p7.start(); p8.start();
p9.start(); p10.start();
}
}
class Tunnel implements Runnable{
private int crossedNum = 0;//初始人数0.5分
public void run(){//1分
cross();
}
public synchronized void cross(){
try { //每个人通过山洞的时间为5秒(1分)
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//人数计数(0.5分)
crossedNum++;
//显示一下每次通过山洞人的姓名(1分)
System.out.println(Thread.currentThread().getName()+
"通过了山洞,这是第"+crossedNum+"个用户");
}
}
/**这个版本程序没停,有bug,抽时间改下
*使用wait notify notifyAll 实现功能.
*一个线程打印 数字 一个线程打印 A-Z
*打印的结果:12A34B56C78.....
*/
public class TestPrint {
public static void main(String[] args) {
Print print=new Print();
PrintLetter printLetter=new PrintLetter(print);
printLetter.setPriority(Thread.MIN_PRIORITY);
PrintNum printNum=new PrintNum(print);
printNum.setPriority(Thread.MAX_PRIORITY);
printNum.start();
printLetter.start();
}
}
/**
* 使用wait notify notifyAll 实现功能.
*/
public class Print {
private int count=0;
//打印字母
synchronized void letter(){
for (char i = 'A'; i < 'Z' +1 ; i++) {
if(count%2==0){
System.out.println(i);
this.notify();
}
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//打印数字
synchronized void num(){
for (int i = 1; ; i++) {
System.out.println(i);
count++;
if(count%2==0){
try {
this.notify();
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 打印字母A-Z
*/
public class PrintLetter extends Thread{
private Print print;
public PrintLetter(Print print) {
this.print = print;
}
@Override
public void run() {
print.letter();
}
}
import com.bjsxt.first.Producer;
/**
* 打印数字0-9
*/
public class PrintNum extends Thread{
private Print print;
public PrintNum(Print print) {
this.print = print;
}
@Override
public void run() {
print.num();
}
}