1.继承Thread和实现Runnable的区别
线程不安全:
public class EasyThreadA {
static List list=new ArrayList<>();
public static void method(){
for (int i=0;i<10;i++){
list.add(i+"A"+Thread.currentThread().getName());
}
System.out.println(list);
}
public static void main(String[] args) {
Runnable run=EasyThreadA::method; //Lambda表达式表示可以使用这个放法
实现当调用run方法的时候使用method代替
Thread a=new Thread(run);
Thread b=new Thread(run); 当线程使用的是Runnable中的run方法,那么两
个线程公用一个内存
a.start();
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(list.size());
}
}
下面代码中两个主函数,他们分别执行可以看的出一个类继承Thread类的时候,两个线程分别执行操作的是两个不同的内存
但是两个线程共同执行run方法的时候操作的是同一个内存最后的结果汇合在一起
线程安全:
public static void main(String[] args) {
ThreadA a=new ThreadA();
ThreadA b=new ThreadA();
a.start();
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(a.list);
System.out.println(b.list);
}
public static void main(String[] args) {
RunA runA=new RunA();
Thread a=new Thread(runA);
Thread b=new Thread(runA);
a.start();
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(runA.list);
System.out.println(runA.list.size());
}
}
class ThreadA extends Thread{
public List list=new ArrayList<>();
@Override
public void run() {
for (int i=0;i<10;i++){
list.add('a');
}}
}
class RunA implements Runnable{
public List list=new ArrayList<>();
@Override
public void run() {
for (int i=0;i<10;i++){
list.add('a');
}}
}
2.锁对象 Lock
Lock lock=new ReentrantLock();//创建锁对象,非公平锁,但是参数加true就变成公平锁
Reentrant关键字 是可重入的
Lock中主要掌握tryLock lock unlock三个方法分别是尝试加锁,但是不一定成功,加锁一定成功,解锁
public class EasyThreadB {
//锁对象 lock
Lock lock=new ReentrantLock();//创建锁对象,非公平锁,但是参数加true就变成公平锁
//Reentrant 可重入的
public void method() {
//lock.lock();//加锁
//trylock 尝试加锁 加锁成功返回true 失败返回false
if (lock.tryLock()) {
System.out.println(Thread.currentThread().getName() + "进入方法");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "结束方法");
lock.unlock();
}
else {
System.out.println("加锁未成功-----去执行别的代码");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
method();
}
}
public static void main(String[] args) {
Runnable run=new EasyThreadB()::method;
Thread a=new Thread(run);
Thread b=new Thread(run);
a.start();
b.start();
}
}
3.读写锁
读锁是共享锁,不管前面是否结束都可以继续加锁
写锁是独占锁,前一个写锁线程结束才可以加写一个写锁
public class EasyThreadC {
//读锁是共享锁,不管前面是否结束都可以继续加锁
//写锁是独占锁,前一个写锁线程结束才可以加写一个写锁
public static ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock();
public static ReentrantLock rl=new ReentrantLock();
public static void method(){
System.out.println(Thread.currentThread().getName()+"进入方法");
Lock lock=rrwl.readLock();
lock.lock();
System.out.println(Thread.currentThread().getName()+"加锁成功--读锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
lock.unlock();
System.out.println(Thread.currentThread().getName()+"方法结束");
}
public static void main(String[] args) {
Runnable run=EasyThreadC::method;
Runnable runWrite=EasyThreadC::methodWrite;
Thread a=new Thread(run);
Thread b=new Thread(run);
Thread c=new Thread(run);
Thread d=new Thread(run);
a.start();
b.start();
c.start();
d.start();
Thread e=new Thread(runWrite);
Thread f=new Thread(runWrite);
Thread g=new Thread(runWrite);
Thread h=new Thread(runWrite);
e.start();
f.start();
g.start();
h.start();
System.out.println("main线程结束");
}
public static void methodWrite(){
System.out.println(Thread.currentThread().getName()+"进入方法");
Lock lock=rrwl.writeLock();
lock.lock();
System.out.println(Thread.currentThread().getName()+"加锁成功--写锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
lock.unlock();
System.out.println(Thread.currentThread().getName()+"方法结束");
}
}
在上面代码结果中可以发现,读锁的时候可以有不同的线程进入一起读锁,但是一旦有一个线程写锁,就不能有其他线程读锁或者写锁
没有读写锁的情况:
public class EasyThreadE {
public static void main(String[] args) {
EasyList list=new EasyList();
Runnable runSize=()->{list.size();};
Runnable runget=()->{list.get(0);};
Runnable runadd=()->{list.add(12);};
list.add(12);
Thread a=new Thread(runSize);
Thread b=new Thread(runadd);
Thread c=new Thread(runget);
Thread thread=new Thread(runadd);
a.start();
b.start();
thread.start();
}
}
class EasyList{
private int[] values=new int[20];
int size=0;
ReentrantReadWriteLock rrwl=new ReentrantReadWriteLock();
public int size(){
System.out.println(Thread.currentThread().getName()+"Size开始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"Size结束");
return size;
}
public synchronized int get(int index){
System.out.println(Thread.currentThread().getName()+"运行get");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (index>=size){
throw new IndexOutOfBoundsException("index is"+index);
}
System.out.println(Thread.currentThread().getName()+"---------get结束");
return values[index];
}
public boolean add(int item){
System.out.println(Thread.currentThread().getName()+"运行add");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (size> values.length-1){
return false;
}
values[size++]=item;
System.out.println(Thread.currentThread().getName()+"---------add结束");
return true;
}
}
4.自定义锁对象
public class EasyThreadD {
public static final Object OBJ=new Object();//作为锁对象
public static void method(){
System.out.println(Thread.currentThread().getName()+"进入方法");
synchronized (OBJ){
OBJ.notify();//唤醒一条被该锁对象wait的线程
//OBJ.notifyAll();唤醒所有被该锁对象wait的线程
System.out.println(Thread.currentThread().getName()+"进入同步代码块");
try {
try {
System.out.println(Thread.currentThread().getName()+"进入等待");
OBJ.wait();//让执行到该行代码的线程进入等待状态(等待池)
System.out.println(Thread.currentThread().getName()+"重新运行");
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"结束同步代码块");
OBJ.notify();
}
}
public static void main(String[] args) {
Runnable run= EasyThreadD::method;
Thread a=new Thread(run);
a.start();
Thread b=new Thread(run);
b.start();
Thread c=new Thread(run);
c.start();
Thread d=new Thread(run);
d.start();
}
wait和sleep的区别:
wait是Object中定义的方法,可以有锁对象调用,让执行到该行代码的线程进入等待状态
sleep是thread中定义的静态方法,可以让执行到该行代码的线程进入等待状态
区别:1. sleep需要传入一个毫秒数,到达时间之后会自动唤醒,
wait不能自动唤醒,需要调用notify或者notifyAll方法唤醒
2.sleep方法保持锁状态进入等待状态
wait方法会解除锁状态,其他线程可以进入运行
5.线程池
线程池,池就是用来重用的
线程池主要用来线程的创建,重用和销毁的工作
线程池有七个参数:核心线程数,最大线程数,存活时间,时间单位,对象,工作队列,回绝策略
BlockingQueue queue=new ArrayBlockingQueue(12);
ThreadPoolExecutor t=new ThreadPoolExecutor(5,10,10, TimeUnit.SECONDS,queue,Executors.defaultThreadFactory(),new
ThreadPoolExecutor.AbortPolicy());
线程池可以用Runnable和Callable
其中Runnable 可以使用submit和excute两个方法启动线程
Callable只能用submit启动线程
使用submit方法的时候会返回一个Future类型所以需要使用Future类建立对象接受返回的值
public class EasyExecuters {
//池 重用
//完成线程创建和重用,销毁工作
public static void main(String[] args) throws ExecutionException, InterruptedException {
//Executors.
BlockingQueue queue=new ArrayBlockingQueue(12);
ThreadPoolExecutor t=new ThreadPoolExecutor(5,10,10, TimeUnit.SECONDS,queue,Executors.defaultThreadFactory(),new
ThreadPoolExecutor.AbortPolicy());
//线程任务 Runnable Callable
Runnable run=EasyExecuters::method;//Runnable可以用excute或者submit
t.execute(run);
Callable<String> callable=EasyExecuters::methodCall;//只能用submit
Future<String> f= t.submit(callable);
//t.submit(run);
System.out.println(f.get());//会等待线程执行完毕,然后获取
//线程池对象需要关闭
t.shutdown();
}
public static void method(){
System.out.println(Thread.currentThread().getName()+"执行1代码");
}
public static String methodCall(){
System.out.println(Thread.currentThread().getName()+"执行2代码");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "callResult";
}
}
1.工作原理:
线程池工作原理 //1.线程池接受任务后任务会放置在工作队列中,首先查看池中是否有空闲的线程,如果有就让该线程执行任务 // 当工作队列填满之后,如果没有空闲线程,然后看有没有达到核心线程数如果没有达到核心线程数,会创建新的线程执行新的任务 //如果池中没有空闲线程,判断线程数量是否达到核心线程数 //如果没有达到,会创建新的线程执行任务,知道填满核心线程数,如果已经达到,有现在都列中存储 //如果队列填满, //工作队列填满后再添加新的任务,判断是否达到最大线程数,如果没有,创建新的线程执行任务 //知道填满最大线程数,如果已经填满最大线程数,队列也已经填满,没有空闲线程,并没有空闲线程,就执行回绝策略 //线程池中的线程达到核心线程数(或者超过),超出的数量会根据存活时间进行销毁,直到数量达到核心线程数,如果线程的数量少于核心线程数,不会销毁
2.java中内置的线程池对象
java中内置的线程池对象 //1.Executors.newCachedThreadPool() //可以根据工作任务创建线程,如果没有空闲线程就创建新的线程,线程存活时间60s //2.Executors.newfixedThreadpool(10) //设置一个最大线程数量的线程池 //3.Executors.newScheduledThreadPool(10)提供定时运行的处理方案 //4.Executors.newSingleThreadExecutor创建一个具有单个线程的线程池,保障任务队列完全按照顺序顺序执行
3.七个参数
//核心线程数,最大线程数,存活时间,时间单位,对象,工作队列,回绝策略
4.回绝策略
回绝策略
1.AbortPolicy (默认使用)会抛出异常,会放弃该任务
2.CallRunsPolicy 当线程无法执行的时候,让传递任务的线程执行此任务
3.DiscardOldestPolicy (丢弃最久的)放弃队列中时间最长的任务,不会抛出异常,放弃的任务还没有执行
4.DiscardPolicy (直接放弃新的任务)不会抛出异常
BlockingQueue queue=new ArrayBlockingQueue(12);
ThreadPoolExecutor t=new ThreadPoolExecutor(5,8,10, TimeUnit.SECONDS,queue, Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());
Runnable run=()->{
System.out.println(Thread.currentThread().getName()+"执行代码");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"执行ci代码");
};
for (int i=0;i<17;i++){
t.execute(run);
}
t.shutdown();
}
上面代码中在i执行17次之前一直都只有五个线程在执行(五个核心线程,12个任务队列)
在执行第18次之后会加线程,但是最多只能加到8个因为最大数量线程就是8
6.枚举类
在使用枚举类的时候不需要继承Enum类,会默认继承而且加上继承之后会报错
在首行需要写出所有要用到的实例
public enum EasyCorlor {
//枚举类 默认继承Enum类
//首行 需要写出所有实例
RED,YELLOW,GREEN,BLUE,PINK;
public void printCorlor(){
System.out.println(this.name());
System.out.println(this.ordinal());
}
}
class test{
public static void main(String[] args) {
EasyCorlor.RED.printCorlor();
EasyCorlor.GREEN.printCorlor();
}
}
在打印中第一个是打印实例的名字,第二个打印在首行中枚举出的实例的位置