多线程

在这里插入图片描述

重点

Locksynchronized  :在同一个jvm虚拟机 
目前创建多线程的方式:1)继承Thread(2) 实现Runnable接口  (3)  线程池   (4)  Callable接口  (获取返回值Future)
CallableRunnable主要区别:Callable里的call是有返回值,可以利用Future接口中get可以获取返回
						Runnable 里的run方法是没有返回值
直接调用run()方法和调用start()方法有啥区别?
调用start()方法是在多线程中执行run()方法,直接调用run()方法是在当前线程中执行run()方法
基本状态: 初始状态 ----->就绪状态<--->运行状态-------阻塞、等待状态--->终止状态
join方法():线程加入,让加入的线程执行完毕后,当前线程才会执行
Thread.sleep():线程睡眠,会让出cpu资源
Thread.yield():线程礼让,会再次抢占cpu

线程同步
synchronized:锁的是成员方法,互斥锁对象是this
​ 				锁的是静态方法,互斥锁对象是Class对象
场景:
1:如何让某个线程先不执行,等待另外某个线程执行完后再执行?
  join()
2:如何让两个线程交替执行?
wait()  notify()      notifyAll()        (线程通信)

为什么要用线程池?
当需要用多线程执行多任务时,频繁创建和销毁线程会损耗性能,用线程池可以先开启多个线程,线程可以重用,不用每次都创建新的线程。

实现线程同步的方式: synchronized  : 关键字 
								隐式获取锁,隐式释放锁 
								支持重入锁
								只支持不公平锁,不支持公平锁
				synchronized出现异常会自动释放锁
				synchronized1.6后支持锁升级

lock 锁  :类
			显式获取锁,显式释放锁
			支持重入锁
			支持不公平锁和公平锁,默认不公平锁
			支持tryLock 非阻塞式获取锁
			可以实现读写锁,读写分离 ,提高效率
 synchronized锁升级的四种状态:无锁   偏向锁   轻量级锁(CAS自选锁)    重量级锁                                                        

ConcurrentHashMap :线程安全,效率要比Hashtable高
					  锁一部分代码,锁的是数组一部分不是锁整个数组
					Hashtable锁整个方法

进程和线程

进程:运行中的程序称之为进程
线程:

  • 进程组成部分,线程用进程中,单一的执行的顺序控制流程。同时也是CPU的调度单位
  • 进程是可以有多个线程的,他们之间独立运行。交替执行。称之为多线程
    区别
  • 进程是系统分配资源的单位,线程是CPU的调度单位
  • 一个程序,至少包含一个进程
  • 一个进程至少包含一个线程。线程是不能独立运行的,必须依附在进程中
  • 进程之间是不能共享数据段地址,但是同一个进程下的线程是可以共享的
    线程的组成部分
  • CPU时间片
    + 操作系统会为每个线程分配时间
  • 运行数据
    + 堆空间:存储线程需使用的对象。多个线程可以共享堆中的对象
    + 栈空间:存储线程需使用的局部变量。每个线程都拥有自己的栈空间
  • 线程的逻辑代码

创建线程

创建线程的方式:1、继承Thread类 2、实现Runnable接口 3、线程池 4、实现callable接口

public class Demo1 {
    public static void main(String[] args) {
        //方式1 继承Thread类
        A a = new A();
        a.start();
        //方式2 实现Runnable接口
        B tast = new B();
        Thread thread = new Thread(tast);
        thread.start();
    }
}
//方式1 继承Thread类
class A extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
//方式2 实现Runnable接口
class B implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

两种方式的区别
1、java只支持单继承,所以继承Thread类就没办法继承其他的类,所以使用Runnable接口更灵活
2、继承自Thread类,表示这个类就是一个线程类,可以直接启动线程。实现Runnable接口,表示这个类是一个线程任务,需要创建线程对象从而执行这个线程任务

启动线程需要注意的问题
1.不要调用run方法
2.一个线程只能调用一次start方法

线程常见的方法
1、设置线程名称 (setName、getName、Thread.currentThread获取当前线程对象)
如果没有设置线程名称,那么默认的名称为Thread-0 Thread-N
2、设置线程的优先级 (setPriority、getPriority)
如果没有设置线程的优先级,那么默认的优先级为5
线程有的优先级为1~10之间,设置优先级只是提高了抢占CPU的概率
3、线程休眠 (Thread.sleep(毫秒数)) //让出cpu使用权
让当前线程进入到休眠状态,并让出CPU使用权,直到休眠结束,才会继续抢占CPU
4、线程礼让(Thread.yeild())
让出CPU使用权,但是立马又会去重新抢占CPU
5、线程加入(join()) 控制线程执行顺序
在当前线程中加入另一线程,必须要将另一个线程执行完之后才会继续执行当前线程

线程安全
方式一:同步代码块
synchronized(互斥锁对象){ //互斥锁对象 (互斥锁是唯一的java对象就可以)
​ //原子代码
}

方式二:同步方法 锁的是成员方法,互斥锁对象是this ,锁的是静态方法,互斥锁对象是Class对象
public synchronized void sale(){ //互斥锁标记是this对象
​ //原子代码
}
互斥锁对象要求对象是唯一的,如果是多个对象,可以利用唯一的Class 对象

线程死锁
当第一个线程拥有A对象的锁标记,并等待B对象的所标记。同时第二个线程拥有B对象锁标记,同时等待A对象的锁标记时,产生死锁

public class Demo8 {
    public static void main(String[] args) {
        new Thread1().start();
        new Thread2().start();
    }

}

class AA{
    static AA aa1=new AA();
    static AA aa2=new AA();
}

class Thread1 extends Thread{
    @Override
    public void run() {
        synchronized (AA.aa1){
            System.out.println("获取aa1锁");
            synchronized(AA.aa2){
                System.out.println("尝试获取aa2锁");
            }
        }
    }
}

class Thread2 extends Thread{
    @Override
    public void run() {
        synchronized (AA.aa2){
            System.out.println("获取aa2锁");
            synchronized(AA.aa1){
                System.out.println("尝试获取aa1锁");
            }
        }
    }
}

线程通信
wait()

  • 当前线程释放锁对象, 并处于阻塞状态,进入等待队列,直到有别人唤醒
    notify()、notifyAll()
  • 随机唤醒一个正在等待的线程
  • 唤醒所有等待队列中的线程
    [注意:所有的等待、通知方法必须在对加锁的同步代码块中。]

线程池

  • 如果有非常的多的任务需要多线程来完成,且每个线程执行时间不会太长,这样频繁的创建和销毁线程。
  • 频繁创建和销毁线程会比较耗性能。有了线程池就不要创建更多的线程来完成任务,因为线程可以重用
  • 线程池用维护者一个队列,队列中保存着处于等待(空闲)状态的线程。不用每次都创建新的线程。
    线程池中常见的类
    常用的线程池接口和类(所在包java.util.concurrent)。
    Executor:线程池的顶级接口。
    ExecutorService:线程池接口,可通过submit(Runnable task) 提交任务代码。
    Executors工厂类:通过此类可以获得一个线程池。
    newFixedThreadPool(int nThreads) 获取固定数量的线程池。参数:指定线程池中线程的数量。
    newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,无上限
public class TestThreadPool1 {
	public static void main(String[] args) {
		//1、获取线程池对象     线程池数量为3
		ExecutorService es = Executors.newFixedThreadPool(3);
		//2、通过线程池提交并执行线程任务    (线程会自动启动线程并执行线程任务(执行run方法))
		es.submit(new MyTask()); 
		es.submit(new MyTask()); 
		es.submit(new MyTask()); 
		es.submit(new MyTask()); 
		//3、关闭线程池   (当所有的线程任务都执行完成之后关闭)
		es.shutdown();
	}
}

class MyTask implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"--->"+i);
			
		}
	}
}
public class TestThreadPool2 {
	public static void main(String[] args) {
		//1、创建线程池对象     动态个数的线程池  (如果线程任务执行完,会执行下一个线程任务)
		ExecutorService es = Executors.newCachedThreadPool();
		//2、通过线程池对象启动并执行线程任务
		es.submit(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"--->"+i);
				}
			}
		});
		es.submit(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"--->"+i);
				}
			}
		});
		es.submit(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"--->"+i);
				}
			}
		});
		es.submit(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"--->"+i);
				}
			}
		});
		//3、关闭线程池
		es.shutdown();
	}
}

Callable接口

JDK5加入,与Runnable接口类似,实现之后代表一个线程任务
Callable具有泛型返回值、可以声明异常。

public class TestCallable1 {
	public static void main(String[] args) {
		//1、创建线程池对象
		ExecutorService es = Executors.newFixedThreadPool(2);
		//2、通过线程池提交线程并执行任务
		es.submit(new MyCallable());
		//3、关闭线程池
		es.shutdown();
	}
}
class MyCallable implements Callable{//此泛型规定了方法的返回值
	@Override
	public Object call() throws Exception {
		
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"--->"+i);
			
		}
		return null;
	} 
}

Future接口
Future接口表示将要执行完任务的结果。
get()以阻塞形式等待Future中的异步处理结果(call()的返回值)。

Lock锁

JDK5加入,与synchronized比较,显示定义,结构更灵活。
提供更多实用性方法,功能更强大、性能更优越。

public class TestLock {
	public static void main(String[] args) {
		//1、创建线程池对象
		ExecutorService es = Executors.newFixedThreadPool(4);
		//2、通过线程池提交线程任务
		TicketTask task = new TicketTask();
		es.submit(task);
		es.submit(task);
		es.submit(task);
		es.submit(task);
		//3、关闭线程池
		es.shutdown();
	}
}
class TicketTask implements Runnable{
	static int ticket = 30;
	//jdk1.5之后加入
	Lock lock = new ReentrantLock();//重入锁
	Object obj = new Object();
	@Override
	public void run() {
		while(true) {
			try {
				lock.lock();    //上锁
				if(ticket < 0) {
					break;
				}
				//System.out.println(10/0);
				try {
					Thread.sleep(30);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println(Thread.currentThread().getName()+"--->卖出了"+ticket--+"号票");
			}finally {
				lock.unlock();  //释放锁
			}
		}
	}
}

读写锁
ReentrantReadWriteLock: (读写锁)
一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
支持多次分配读锁,使多个读操作可以并发执行。

互斥规则:
写-写:互斥,阻塞。
读-写:互斥,读阻塞写、写阻塞读。
读-读:不互斥、不阻塞。
在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。

public class Demo4 {
    public static void main(String[] args) {
        long l = System.currentTimeMillis();
        User1 u = new User1();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 2; i++) {
            WritTask1 writTask = new WritTask1();
            writTask.setUser(u);
            executorService.submit(writTask);
        }
        for (int i = 0; i < 8; i++) {
            ReadTask1 readTask = new ReadTask1();
            readTask.setUser(u);
            executorService.submit(readTask);

        }

        executorService.shutdown();
        //判断线程池中的任务是否执行结束,如果结束了返回true
        while (!executorService.isTerminated()){

        }
        System.out.println(System.currentTimeMillis()-l);
    }
}

class User1{
    String name;
    ReadWriteLock rrw=new ReentrantReadWriteLock();


    public String getName() {
        Lock readLock=rrw.readLock();
        readLock.lock();
        try {

            Thread.sleep(1000);
            return name;

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            readLock.unlock();
        }

        return null;
    }

    public void setName(String name) {
        Lock lock = rrw.writeLock();
        try {

            lock.lock();
            Thread.sleep(500);
            this.name = name;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }


    }
}

class ReadTask1 implements Runnable{
    User1 user;
    public void  setUser(User1 user){
        this.user=user;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+user.getName());
    }
}

class WritTask1 implements Runnable{
    User1 user;
    public void  setUser(User1 user){
        this.user=user;
    }

    @Override
    public void run() {
        user.setName("zhangsan");
        System.out.println(Thread.currentThread().getName());
    }
}

重入锁
重入锁也叫作递归锁,指的是同一个线程外层函数获取到一把锁后,内层函数同样具有这把锁的控制权限
synchronized和Lock锁都可以实现锁的重入

public class Demo5 {
    public static void main(String[] args) {
        new Thread(new Task2()).start();
    }
}

class Task2 implements Runnable{

    @Override
    public void run() {
        a();
    }

    public synchronized void a(){
        System.out.println("aa");
        b();
    }

    public synchronized void b(){
        System.out.println("bb");
    }
}

公平锁
非公平锁:优先使用上一个线程接着执行下面的线程任务

  • synchronized是非公平锁的实现,无法实现公平锁
  • lock锁默认是非公平锁,如果想要实现公平锁,那么需要在构造方法设置为true
    公平锁:让每个线程都公平去执行线程任务
  • lock锁可以实现公平锁
  • synchronized无法实现公平锁
//Lock锁实现公平锁 参数为true表示是公平锁,默认是false表示非公平锁
Lock lock = new ReentrantLock(true);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值