17 线程

`

线程

线程

进程:一个正在进行的程序
线程定义:轻量级进程,一个程序顺序执行流程
并发原理:CPU分时间片,交替执行 宏观并行 微观串行

CPU时间片:操作系统(OS)负责调度
数据空间: 堆空间共享(对象 等所有线程共享的),栈空间独立(局部变量等 所有线程之间特有的)
代	码:1>实现Runable接口 实现run方法 Runable对象:任务对象  new Thread(任务对象) 启动线程,调用start()方法
    	2>继承Thread类 覆盖run方法 new Thread() 启动线程,调用start()方法

并行 和 并发

并行:多个任务同时运行在同一个时间点(多核CPU执行)
并发:多个任务将请求提交给CPU,CPU分时间片执行

多线程的实现方式

继承类(Thread类)
创建一个线程的步骤:
	1.继承Thread类
	2.重写run方法
	3.将线程要执行的代码写到run方法中
	4.创建自定义类的对象
	5.启动线程
	
实现接口(Runnable接口)
创建线程的步骤:
	1.自定义一个类实现Runnable接口
	2.实现run方法
	3.将线程要运行的代码写到run方法中
	4.创建自定义类的对象(资源对象)
	5.创建一个线程对象(线程对象)
	6.将资源对象交给线程对象
	7.开启线程,调用start()方法

两种创建线程的方式比较

继承Thread类:
	1.代码比较简洁
	2.弊端:当继承Thread类后就不能继承别的类
实现Runnable接口:
	1.代码比较复杂
	2.优点:线程类可以继承别的类,实现Runnable接口
没有哪个更好,选择合适的就可以。

匿名内部类实现线程的方式

匿名内部类:条件是局部内部类必须继承一个类或者实现一个接口
继承Thread类的匿名内部类方式
public class Demo3 {

	public static void main(String[] args) {

		 new Thread(){

			public void run() {

				for (int i = 1; i <= 100; i++) {

					System.out.println("第"+i+"次开头“);

				}

			}

		}.start();;

		
		
		new Thread(){

			public void run() {

				for (int i = 1; i <= 100; i++) {

					System.out.println("第"+i+"次结尾“);

				}

			}

		}.start();;

		
	}

}
实现接口的匿名内部类方式
public class Demo3 {
	public static void main(String[] args) { 
		new Thread(new Runnable(){

			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					System.out.println("第"+i+"次开头“);
				}
				
			}
			
		}).start();;
		
		new Thread(new Runnable(){

			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					System.out.println("第"+i+"次结尾“);
				}
				
			}
			
		}).start();;
		
	}
}

方法

设置线程的名称:
1.通过构造方法设置线程的名称
2.通过setName()设置线程的名称
3.通过getName()获取线程的名称
4.currentThread():获取当前线程对象

Thread.sleep(int) 限时等待 休眠(int 毫秒 )(只能手动处理异常)
t.join() 当线程进入等待终止 才会恢复执行(处理异常)
Thread.yield() 放弃CPU 回到就绪状态(重新等OS选中)
t.setDaemon(true) 设置线程为守护线程,所有的非守护线程都结束时,进程就会结束(应设置在启动线程之前)

案列:
package com.wzx.entity;

public class TestThread2 {
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(new Thread1());
        Thread t2 = new Thread2();
        t1.start();
        //t2.setDaemon(true); //设置为守护(精灵)线程
        t2.start();
      /*  t1.join();//无限期等待状态 当t1 t2线程交替执行完毕 才会执行主线程输出All Thread Over
        t2.join();
        System.out.println( Thread.currentThread().getName());//获取当前执行线程的名字  main
        System.out.println("All Thread Over");*/
    }
}
class Thread1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 51; i++) {
            System.out.println(i);
            try {
                Thread.sleep(100);//有限期等待状态 1000毫秒 = 1秒
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Thread2 extends Thread{
    @Override
    public void run() {
        for(char c = 'A';c<='Z';c++){
            System.out.println(c);
            try {
                Thread.sleep(180);
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程的状态

[外链图片转存失败(img-FdoDotLr-1562821688632)(F:\166课件\线程的状态.png)]

初始状态:创建了一个线程,但是并没有启动
就绪状态:调用start方法之后就有了争夺cup的权利
运行状态:当一个处于就绪状态的线程抢到了cup,就从就绪状态到达运行状态,在同一时间片处于运行状态的线程只有一个
限时等待状态:当一个处于运行状态的线程调用了sleep(毫秒)方法之后,就处于了限时等待状态,当毫秒值一过,就从限时等待状态进入到了就绪状态
终止状态:当一个线程完全执行完了run方法中的代码,由运行状态到达终止状态
无限期等待状态:当一个线程中存在(其他的线程调用join()),那么当前的这个线程就进入到了无限期等待状态。

线程安全

线程安全:所有的线程操作一个对象,会产生混乱的情况,因此才要实现同步
临界资源:多线程并发时,共享的同一个对象
原子操作:不可分割的多步操作,被视为一个整体,其顺序和步骤不可被打乱或缺省。
线程同步的目的:多线程并发时,为了保证临界资源的正确性,而不能破坏程序中的原子操作。

实现线程同步的方式

1.同步代码块
synchronized(临界资源对象){//对临界资源对象加锁
    //原子操作
}
锁对象(临界资源):所有的线程共享一个锁对象,每个锁对象有且只有一个锁标记(钥匙),只有拿到了所标记的线程才能进入到同步代码块中,锁对象可以为任意的一个对象,当一个线程执行完了一个原子操作后,会释放锁标记,所有的线程开始争夺锁标记,当一个线程既拿到了所标记又抢到了cpu的资源才能进入到同步代码块中。
2.同步方法
public synchronized 返回值类型 方法名(参数列表){//this当前对象
	//原子操作
}
1>当同步方法为实例方法,他的锁对象是当前对象的引用this
2>当同步方法是静态方法时,同步方法的锁对象是,当前类的类对象  当前类.class
案列:
package com.wzx.entity;

public class TestThread3 {
    public static void main(String[] args) throws InterruptedException {
        final MyList list = new MyList();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //synchronized (list){
                    list.add("C");
               // }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                //synchronized (list){
                    list.add("D");
               // }
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        list.add("E");
        list.print();
    }
}
class MyList{
    String[] data = {"A","B","","",""};
    int index = 2;
    public synchronized void add(String s){
        data[index] = s;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        index++;
    }
    public void print(){
        for (int i = 0; i < data.length; i++) {
            System.out.println(data[i]);
        }
    }
}

等待通知机制

同步-->死锁-->线程通信(同步会产生死锁,死锁靠线程通信解锁)

线程通信	等待-通知
wait():当一个线程中的锁对象调用了wait方法,这个线程释放锁标记,进入到等待池中,如果没有其他线程去调用notify/notifyAll,会一直处于等待池中
wait(毫秒):当一个线程中的锁对象调用了wait(时间)方法,这个线程释放锁标记,进入到等待池中,当时间一过自动的从等待池中进入到锁池中
notify():是随机的从等待池中挑选一个线程进入到锁池中
notifyAll():将等待池中的所有线程都唤醒到锁池中,所有的线程在锁池中去抢锁标记,当抢到锁标记的线程由锁池到就绪状态,其他没有抢到锁标记的线程又回到了等待池中
以上的几个方法必须在同步代码块中运行,由锁对象调用

sleep(时间):当一个线程调用了sleep方法,进入到了限时等待状态,但是没有释放所标记,那么这个线程共享一把锁的线程都处于锁池中阻塞状态
案例:
package com.wzx.entity;

public class TestThread4 {
    public static void main(String[] args) throws Exception {
        //随便创建一个对象(匿名内部类调用外部类的局部变量需加final JDk1.8默认)
        final Object o = new Object();
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (o){
                    System.out.println("A");
                    System.out.println("B");
                    o.notify();
                    System.out.println("C");
                    System.out.println("D");
                }
            }
        });
        t.start();
        synchronized (o){
            System.out.println("1");
            System.out.println("2");
            o.wait();
            System.out.println("3");
            System.out.println("4");
        }
    }
}

结果:1 2 A B C D 3 4
    
 案列:生产者、消费者(synchronized)
 package com.wzx.entity;

public class TestThread5 {
    public static void main(String[] args) {
        final MyStack stack = new MyStack();
        Runnable task1 = new Runnable(){
            public void run(){
                for(char c='A';c<='Z';c++){
                    stack.push(c+"");
                }
            }
        };
        Runnable task2 = new Runnable() {
            public void run() {
                for (int i = 1; i <= 26; i++) {
                    stack.pop();
                }
            }
        };
        new Thread(task1).start();
        new Thread(task2).start();
    }
}
class MyStack{
    String[] data = {"","","","","",""};
    int index;
    //进栈(生产者)
    public synchronized void push(String s){
        while(data.length == index){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print(s+" pushed     ");
        data[index] = s;
        index++;
        print();
        this.notifyAll();//通知消费者
    }
    //出栈(消费者)
    public synchronized void pop(){
        while(index == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        index--;
        String o = data[index];
        data[index] = "";
        System.out.print(o+" poped      ");
        print();
        this.notifyAll();//通知生产者
    }
    //打印
    public void print(){
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i]+" ");
        }
        System.out.println();
    }
}

同步锁

同步锁    
Lock接口:JDK5的新特性
实现类:ReentrantLock 
Lock lock = new ReentrantLock();
上锁:lock.lock();	解锁:lock.unlock();
方法:newCondition() await() signal()/signalAll()
//生成等待序列,可生成多个(synchronized 只有当前对象的一个等待序列)
Condition full = lock.newCondition();
full.await();//进入等待序列相当于wait()
emply.signalAll();//通知消费者(数组已满)相当于notifyAll()

生产者和消费者问题

有两个线程,共同去操作一个资源,一个线程负责消费,一个线程负责生产,当消费完了,通知生产者去生产,当生产者生产的商品满了,通知消费者去消费。涉及到线程之间的通行问题。
案列:
生产者、消费者(Lock)
package com.wzx.entity;

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

public class TestThread5_1 {
    public static void main(String[] args) {
        final MyStack1 stack = new MyStack1();
        new Thread(){
            public void run(){
                for(char c='A';c<='Z';c++){
                    stack.push(c+"");
                }
            }
        }.start();
        new Thread(){
            public void run(){
                for (int i = 1; i <= 26; i++) {
                    stack.pop();
                }
            }
        }.start();
    }
}
class MyStack1{
    String[] data = {"","","","","",""};
    int index;
    Lock lock = new ReentrantLock();//创建锁lock
    Condition full = lock.newCondition();//生成等待序列
    Condition emply = lock.newCondition();
    //生产者
    public void push(String s){
      try {
          lock.lock();
          while (data.length == index){
              try {
                  full.await();//进入等待序列相当于wait()
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
          System.out.print(s+" pushed   ");
          data[index] = s;
          index++;
          print();
          emply.signalAll();//通知消费者(数组已满)相当于notifyAll()
      }finally {
          lock.unlock();//解锁必须执行
      }
    }
    //消费者
    public void pop(){
       try {
           lock.lock();
           while(index ==0){
               try {
                 emply.await();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           index--;
           String o = data[index];
           data[index] = "";
           System.out.print(o+" poped    ");
           print();
           full.signalAll();//通知生产者
       }finally {
           lock.unlock();
       }
    }
    //打印
    public void print(){
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i]+" ");
        }
        System.out.println();
    }
}

线程池

常量池  串池   锁池  等待池  线程池
线程池:线程容器,可设定线程分配的数量上限,将预先创建线程对象存入池中,并重用线程池中的线程。
线程池好处:减少创建和销毁线程的次数,每个工作线程可用于执行多个任务,只需将任务交给线程,即可重复利用。

获取线程池:
线程池的父接口:Executor
子接口:ExecutorService
线程池的工厂类(工具类):

工厂类中的方法:
1.获取固定数量的线程池:
newFixedThreadPool(int nThreads) :创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
 ExecutorService es = Executors.newFixedThreadPool(2);
2.获取动态数量的线程池:
newCachedThreadPool():创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
 ExecutorService es = Executors.newCachedThreadPool();

Callable接口

Runnable接口:run方法 无法抛出异常  没有返回值
JDK5.0加入Callable线程接口,主要的方法是call方法,相比较之前的run方法,call方法可以抛出任意的异常,并且有返回值。
Future对象call方法的返回结果,可利用Future中的get方法获取结果内容。
案列:
Future<Integer> f = pool.submit(c);
System,out,println(f.get());
案例:
	创建两个线程
	第一个线程用于计算 100以内所有偶数的和
	第二个线程用于计算 100以内所有奇数的和
	
	将两个线程的计算结果进行相加 得出结果 5050
package com.wzx.entity;

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

public class TestThreadPool2 {
    public static void main(String[] args) throws Exception{
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Integer> f1 = es.submit(new Callable<Integer>(){
            public Integer call() throws Exception{
                int result = 0;
                for(int i = 0;i<=100;i+=2){
                    result+=i;
                }
                return result;
            }
        });
        Future<Integer> f2 = es.submit(new Callable<Integer>(){
            public Integer call() throws Exception{
                int result = 0;
                for(int i=1;i<100;i+=2){
                    result+=i;
                }
                return result;
            }
        });
        System.out.println(f1.get()+f2.get());
    }
}

线程安全的集合

加锁的对锁优化的方式
1.线程安全的集合
Vector  Hashtable
集合是用于存储数据的(读数据 写数据)
案列:

案例:
package cn.wzx.Deam;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Demo8 {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();//不安全
		
		List<Integer> synchronizedList = Collections.synchronizedList(list);//安全的集合
		
		Set<Integer> set = new HashSet<>();//不安全
		Set<Integer> synchronizedSet = Collections.synchronizedSet(set);
		
		Collection<Integer> collection = new ArrayList<>();
		Collection<Integer> synchronizedCollection = Collections.synchronizedCollection(collection);
		
		Map<Integer, String> map = new HashMap<>();
		Map<Integer, String> synchronizedMap = Collections.synchronizedMap(map);
	}
}

2.读写锁
读线程 和 读线程 不影响线程安全 不用加锁
读 和 写 必须加锁
写 和 写 必须加锁

在实际的开发中对数据
一个网站	注册(写) 登陆(读)
读锁	写锁
一个线程拿到读锁的锁标记 另一个线程也拿到了读锁的锁标记 不影响
读锁		读锁	朋友
写锁		读锁	死对头
写锁		写锁	死对头
现在中实现一个自己的集合 让集合中的方法 读操作的方法上读锁(size get) 写操作的方法上写锁(add remove clear)
     读       写
读   非阻塞   阻塞
写   阻塞     阻塞
应用场景:当读的操作多于写的操作的时候可以用度写锁解决
读写锁 ReadWriteLock	写锁未分配时,读锁可以反复分配 读写分离
ReadWriteLock lock = new ReentrantReadWriteLock();
    Lock readLock = lock.readLock();//读锁共享锁
    Lock writeLock = lock.writeLock();//写锁互斥锁
案列:package com.wzx.entity;

import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


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

    }
}
class MyList extends ArrayList {
    ReadWriteLock lock = new ReentrantReadWriteLock();
    Lock readLock = lock.readLock();//读锁共享锁
    Lock writeLock = lock.writeLock();//写锁互斥锁
    //读锁
    @Override
    public int size(){
        try{
            readLock.lock();
            return super.size();
        }finally {
            readLock.unlock();
        }
    }
    @Override
    public Object get(int index){
        try{
            readLock.lock();
            return super.get(index);
        }finally {
            readLock.unlock();
        }
    }
    //写锁
    @Override
    public boolean add(Object e){
        try{
            writeLock.lock();
            return super.add(e);
        }finally {
            writeLock.unlock();
        }
    }
    @Override
    public Object remove(int index){
        try{
            writeLock.lock();
            return super.remove(index);
        }finally {
            writeLock.unlock();
        }
    }
    @Override
    public void clear(){
        try{
            writeLock.lock();
            super.clear();
        }finally {
            writeLock.unlock();
        }
    }
}

3.分段锁提高数据存储效率的集合 
ConcurrentHashMap<K,V>

HashSet的底层数据存储的结构  数组链表
数组链表:一个数组的每一个元素是一张链表

原理:分段锁为每个下标位置分配了一把锁,将锁的细粒度减小 从而提高了数据存储的效率和并发
package cn.wzx.Deam;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Demo12 {
	public static void main(String[] args) {
		HashMap<Integer, String> hashMap = new HashMap<>();
		
		Map<Integer, String> synchronizedMap = Collections.synchronizedMap(hashMap);
		ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();//锁的细粒度减小  线程安全的集合	
	}
}


采用无锁的算法实现线程安全的集合
1.CopyOnWriteArrayList 
原理: 当对集合进行读操作的时候 没有锁  当对集合进行写操作的时候 采用的是将原数组进行复制,在复制之后的数组进行写操作,写完之后,将新的数组的地址赋给旧的引用

2.
队列:先进先出
栈:后进先出
ConcurrentLinkedQueue<E>

存储数据实现线程安全的原理:采用的无锁比较算法(cas)

效率都高于加同步:
CopyOnWriteArrayList 利用复制数组的方式实现数组元素的修改 写效率低 读效率高(读操作远高于写操作)
CopyOnWriteArratSet
ConcurrentHashMap 分段锁(分成16段) 性能远远优于Hashtable
ConcurrentLinkedQueue 线程安全的队列(链表实现) 无锁算法(CAS) 利用无锁算法实现线程安全

集合的整理

Collection
	|--List (ArrayList LinkedList Vector CopyOnWriteArrayList)
	|--Set  (HashSet LinkedHashSet CopyOnWriterArraySet)
		|--SortedSet (TreeSet)
	|--Queue (LinkedList ConcurrentLinkedQueue)
		|--BlockingQueue (ArrayBlockingQueue LinkedBlockingQueue)
Map (HashMap LinkedHashMap Hashtable Properties ConcurrentHashMap)
		|--SortedMap (TreeMap)
线程安全(实现类):
Vector CopyOnWriteArrayList CopyOnWriteArraySet ConcurrentLinkedQueue
ArrayBlockingQueue LinkedBlockingQueue Hashtable ConcuerentHashMap

E>

存储数据实现线程安全的原理:采用的无锁比较算法(cas)

效率都高于加同步:
CopyOnWriteArrayList 利用复制数组的方式实现数组元素的修改 写效率低 读效率高(读操作远高于写操作)
CopyOnWriteArratSet
ConcurrentHashMap 分段锁(分成16段) 性能远远优于Hashtable
ConcurrentLinkedQueue 线程安全的队列(链表实现) 无锁算法(CAS) 利用无锁算法实现线程安全


## 集合的整理

```jsva
Collection
	|--List (ArrayList LinkedList Vector CopyOnWriteArrayList)
	|--Set  (HashSet LinkedHashSet CopyOnWriterArraySet)
		|--SortedSet (TreeSet)
	|--Queue (LinkedList ConcurrentLinkedQueue)
		|--BlockingQueue (ArrayBlockingQueue LinkedBlockingQueue)
Map (HashMap LinkedHashMap Hashtable Properties ConcurrentHashMap)
		|--SortedMap (TreeMap)
线程安全(实现类):
Vector CopyOnWriteArrayList CopyOnWriteArraySet ConcurrentLinkedQueue
ArrayBlockingQueue LinkedBlockingQueue Hashtable ConcuerentHashMap
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值