java----多线程
线程的5种状态(生命周期)
新生: Thread a = new Thread(()->{});
就绪:调用start(),阻塞事件解除,yield(让出本次cpu),jvm自动切换cpu,这4四种方式都会导致线程处于就绪转态,等待cpu重新来调度
运行:就绪的线程被cpu调用,有系统来控制
阻塞:sleep(),wait()(需要等待启动的线程来通知,否则该线程一直处在堵塞,不会处于就绪,和sleep的区别在于会释放锁),join,read()/write(),这四种方式都会堵塞线程
死亡:stop,suspend,这两种不安全,不推荐使用,我们需要让一个循环的线程正常的执行完毕。所以我们采用interrupt(),来中断,下面有案例示例。
Thread.State state = thread.getState();查看线程的当前状态
NEW 尚未启动的线程处于此状态。
RUNNABLE 在Java虚拟机中执行的线程或处于就绪状态处于此状态。
BLOCKED 被阳塞等待监视器锁定的线程处于此状态。
WAITING 正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED 已退出的线程处于此状态。
线程设置优先级
优先级高不一定先执行
在thread.start()之前设置thread.setPriority(1); //范围只能是1~10
线程:
知识点:
Thread.sleep() 线程睡眠 ,需要try,但是不释放锁
this.wait() 线程进入等待状态(需要notify来唤醒该线程),释放锁 ,this.notify() 换醒线程,利用他可以做线程之间的切换转换 注意:wait需要在同步方法中使用
Thread.yield() 让出本次CPU执行时间片,处于就绪转态,可能cpu还会继续执行该线程
线程.join() 等待线程结束。
创建线程的3种方式
前两种
public class Demo { public static void main(String[] args) { //使用类的继承方法 MyThread1 mt = new MyThread1(); mt.start(); //表示线程处于就绪状态,等待cpu的调用 //使用接口的方式,推荐使用 //将Mythread2这个任务放到Thread这个线程中完成 Thread t = new Thread(new Mythread2()); t.start(); } } class MyThread1 extends Thread{ public void run(){ for (int i = 0; i <100 ; i++) { System.out.println(i+"->"+Thread.currentThread().getName()); } } } class Mythread2 implements Runnable{ public void run() { for (int i = 0; i <100 ; i++) { System.out.println(i+"->"+Thread.currentThread().getName()); } } }
第三种
class A implements Callable { @Override public Object call() throws Exception { return null; } }
补充
class Testxx implements Runnable{ public Testxx() { //是调用这个新线程的父线程 System.out.println(Thread.currentThread().getName()); } @Override public void run() { //这个是新的线程 System.out.println(Thread.currentThread().getName()); } }
线程中断
方式1:
public class Demo { public static void main(String[] args) { Thread t = new Thread(new Mythread2()); t.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } t.interrupt(); //表示给当前的线程打上中断标记,不能真正的中断线程; } } class Mythread2 implements Runnable{ public void run() { for (int i = 0; i <10 ; i++) { if(Thread.interrupted()){//如果线程存在中断标记; break; //return; } try { sleep(500); } catch (InterruptedException e) {//任何线程中断中断当前线程;抛出异常,当前线程的中断状态被清除; e.printStackTrace(); Thread.currentThread().interrupt();//给线程在打上中断标记 } System.out.println(i+"->"+Thread.currentThread().getName()); } System.out.println("线程已经终止"); } }
方式2:使用自定义的标记(推荐)
public class Demo { public static void main(String[] args) { Mythread2 th = new Mythread2(); Thread t = new Thread(th); t.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } th.flag=false; //自定义中断标记 } } class Mythread2 implements Runnable{ public boolean flag = true; public void run() { for (int i = 0; i <10 ; i++) { if(flag==false){ //return; break; } try { sleep(500); } catch (InterruptedException e) {//任何线程中断中断当前线程;抛出异常,当前线程的中断状态被清除; e.printStackTrace(); } System.out.println(i+"->"+Thread.currentThread().getName()); } System.out.println("线程已经终止"); } }
守护线程
线程:分为用户线程和守护线程;
JVM 只关心用户线程,默认所有的设置的线程都是用户线程,当用户线程执行完毕之后,JVM就会退出,如果把一个线程设置为守护线程,那么JVM不在关心这个线程是否执行完毕了
public class Demo { public static void main(String[] args) { Mythread2 th = new Mythread2(); Thread t = new Thread(th); t.setDaemon(true);//把线程设置为守护线程 t.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程退出;"); } } class Mythread2 implements Runnable{ public void run() { for (int i = 0; i <10 ; i++) { try { sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i+"->"+Thread.currentThread().getName()); } } }
线程同步
线程加锁是在每一个new的线程内部加锁
方式一和方式二注意锁住的对象是所有线程共享的一个对象。
方式一: 使用 synchronized 将数据包裹起来; (同步代码块)
public class Demo { public static void main(String[] args) { Mythread2 th = new Mythread2(); for (int i = 0; i <2 ; i++) { new Thread(th).start(); } } } class Mythread2 implements Runnable{ public int ticket=10; private Object obj = new Object(); public void run() { while(true){ synchronized (obj){ //直接写synchronized(this)也可以,没有实际意义 if(ticket>0){ ticket--; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("还有"+ticket+"张票"+"->"+Thread.currentThread().getName()); }else{ break; } } } } }
方法二:使用同步方法
死锁的本质是:线程A获得的锁1,在锁1的内部由需要获得锁2(两把锁的对象是不一样的),此时锁2已经被另一个线程持有了,并且锁2的内部,有需要获得锁1,此时两把锁都得不到释放,造成死锁。对于同一个类中的同步方法 ,并不会造成死锁,原因在于synchronized是可重入的(synchronized(this),this是一样的,就会重入)
注意不管是使用同步方法还是同步代码块,synchronized(Object),其中的object必须是所有的线程共享的,只是同步方法中的Object只能是this。
public class Demo { public static void main(String[] args) { Account account = new Account(100); Drawing drawing = new Drawing(account, 60); //Drawing drawing1 = new Drawing(account, 50); //这两个对象必须是同一个,否则同步方法不生效 new Thread(drawing).start(); new Thread(drawing).start(); } } class Account { public int money; public Account(int money) { this.money = money; } } class Drawing implements Runnable { public Account account; public int num; public Drawing(Account account, int num) { this.account = account; this.num = num; } public synchronized void draw(int num) throws InterruptedException { if (account.money - num < 0) { System.out.println("账户余额不足"); return; } Thread.sleep(100); account.money = account.money - num; System.out.println(account.money); } @Override public void run() { try { draw(num); } catch (InterruptedException e) { e.printStackTrace(); } } }
同步方法不适合的情况
public class Demo { public static void main(String[] args) { Account account = new Account(100); Drawing drawing = new Drawing(account, 60); Drawing drawing1 = new Drawing(account, 50); new Thread(drawing).start(); new Thread(drawing1).start(); } } class Account { public int money; public Account(int money) { this.money = money; } } class Drawing implements Runnable { public Account account; public int num; public Drawing(Account account, int num) { this.account = account; this.num = num; } public void draw(int num) throws InterruptedException { //如果两个对象不一样,只能使用同步代码块,而account对象是两个线程公用的 synchronized (account){ if (account.money - num < 0) { System.out.println("账户余额不足"); return; } Thread.sleep(100); account.money = account.money - num; System.out.println(account.money); } } @Override public void run() { try { draw(num); } catch (InterruptedException e) { e.printStackTrace(); } } }
方法三:使用lock() 更加灵活
注意:ReentrantLock对象也必须是所有的线程共享,如果是不同的对象,需要将锁在main方法实例后,传给每一个线程。
import java.util.concurrent.locks.ReentrantLock; public class Demo { public static void main(String[] args) { Mythread2 th = new Mythread2(); for (int i = 0; i < 2; i++) { new Thread(th).start(); //传入的是同一个th } } } class Mythread2 implements Runnable { public int ticket = 10; private Boolean flag=true; ReentrantLock lock = new ReentrantLock(); public void run() { while (flag) { lock.lock(); try{ ticket--; if (ticket>=0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("还有" + ticket + "张票" + "->" + Thread.currentThread().getName()); }else { flag=false; } }finally {//避免死锁 lock.unlock(); } } } }
生产者消费者模型
其他参考:https://blog.csdn.net/huanyuminhao/article/details/51960256
线程通讯
方式1:管程法
缓冲区:消费者不能直接使用生产者的数据,它们之间有个“缓冲区”;生产者将生产好的数据放入“缓冲区”,消费者从“缓冲区”拿要处理的数据。
方式2:信号灯法(红绿灯)
生产者和消费者线程是需要保持同步的,不可能生产者和消费者方法同时执行,所以wait(),和notiy需要结合synchronized(Obj)来使用
注意事项:
下面的案例中setfood和getfood不可能在同一个时间两个方法都一起执行(synchronized控制了),并且wait()方法执行完毕了,就会释放锁,所以肯定会有线程来执行notify来唤醒他。之前总是错误理解notify会唤醒同一个方法的wait(本身理解就错了,当执行wait方法后,notify就不会被执行了)
执行完毕notify不会立刻释放锁,等synchronized方法执行完毕后,才释放锁。
信号灯法,通过用 flag 来让线程按照执行的顺序执行。
public class Demo { public static void main(String[] args) { //多个线程之间用来传递数据的容器 Food food = new Food(); new Thread(new Chef(food)).start(); new Thread(new Customer(food)).start(); } } class Chef implements Runnable{ private Food food; public Chef(Food food){ this.food = food; } public void setfood(){ this.food.setfood(); } @Override public void run() { setfood(); } } class Customer implements Runnable{ private Food food; public Customer(Food food){ this.food = food; } public void getfood(){ this.food.getfood(); } @Override public void run() { getfood(); } } class Food{ private String name; private Boolean flag = true; public synchronized void setfood(){ //wait() 方法必须在synchronized方法体或方法块中执行 for (int i = 0; i <10 ; i++) { if (!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("包子"+i+"制作完毕"); this.name = "包子"+i; flag = false; this.notify(); } } public synchronized void getfood(){ for (int i = 0; i < 10; i++) { if (flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.name+"已经吃完"); flag=true; this.notify(); } } }
在同步控制方法或同步控制块里调用wait(),notify()和notifyAll()。如果在非同步控制方法里调用这些方法,程序能通过编译,但运行的时候,将得到IllegalMonitorStateException异常,并伴随着一些含糊的消息,比如"当前线程不是拥有者"。消息的意思是,调用wait(),notify()和notifyAll()的线程在调用这些方法前必须"拥有"对象的锁。当前的线程不是此对象锁的所有者,却调用该对象的notify(),notify(),wait()方法时抛出该异常。
public class Demo { public static void main(String[] args) { Food food = new Food(); new Thread(new Chef(food)).start(); new Thread(new Customer(food)).start(); } } class Chef implements Runnable{ private Food food; public Chef(Food food){ this.food = food; } public void setfood(){ this.food.setfood(); } @Override public void run() { setfood(); } } class Customer implements Runnable{ private Food food; public Customer(Food food){ this.food = food; } public void getfood(){ this.food.getfood(); } @Override public void run() { getfood(); } } class Food{ public void setfood(){ //wait() 方法必须在synchronized方法体或方法块中执行 System.out.println("setfood 1"); this.notify(); System.out.println("setfood 2"); } public void getfood(){ try { System.out.println("getfood 1"); this.wait(); System.out.println("getfood 2"); } catch (InterruptedException e) { e.printStackTrace(); } } }
管程法
public class Demo { public static void main(String[] args) { Food food = new Food(); new Thread(new Chef(food)).start(); new Thread(new Customer(food)).start(); } } class Chef implements Runnable { private Food food; public Chef(Food food) { this.food = food; } public void setfood() { for (int i = 0; i < 50; i++) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } this.food.setfood(); } } @Override public void run() { setfood(); } } class Customer implements Runnable { private Food food; public Customer(Food food) { this.food = food; } public void getfood() { while (true){ // try { // Thread.sleep(200); // } catch (InterruptedException e) { // e.printStackTrace(); // } this.food.getfood(); } } @Override public void run() { getfood(); } } class Food { private ArrayList<String> list = new ArrayList<>(); private static int i = 1; public synchronized void setfood() { System.out.println("setfood" + list.size()); if (list.size() >= 10) { System.out.println("不在制作食物"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("开始 制作 食物" + i); list.add(String.valueOf(i)); i++; this.notify(); } public synchronized void getfood() { System.out.println("getfood" + list.size()); if (list.size() == 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String next = iterator.next(); System.out.println("开始 吃 食物" + next); iterator.remove(); } //食物已经吃完可以继续生产食物了 System.out.println("通知.."); this.notify(); } }
线程池
下面的方法本质都是new ThreadPoolExecutor()
public class Demo { public static void main(String[] args) { //创建一个单线程的线程池 //ExecutorService es1 = Executors.newSingleThreadExecutor(); //es1.execute(new Test()); //完成任务之后,线程不会结束; //es1.execute(new Test()); //es1.shutdown(); //结束线程池; //创建一个固定的大小的线程池 //ExecutorService es2 = Executors.newFixedThreadPool(4); //es2.execute(new Test()); //完成任务之后,线程不会结束; //es2.execute(new Test()); //线程池的大小依赖操作系统;并且操作系统会自动对不工作的线程进行回收 //ExecutorService es3 = Executors.newCachedThreadPool(); //任务延迟3秒后执行 ScheduledExecutorService es4 = Executors.newScheduledThreadPool(3); es4.schedule(new Test(),3000, TimeUnit.MILLISECONDS); } } class Test implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(i+"->"+Thread.currentThread().getName()); } } }
线程池扩展1:对线程的处理进行监控
new ThreadPoolExecutor(5, 10,..)第一个5表示核心线程数,当工作的线程满了等于5了,并不会开启一个新的线程来处理任务,即使还没有到达最大的线程数10,会将任务放到workQueue中,如果往workQueue加任务失败(可能满了),此时才会开启一个新的线程(此线程不受核心线程数的限制,受最大线程数的限制),如果线程满了,就采用拒绝策略;
public class ThreadPoolExecutorTest { public static void main(String[] args) { //ExecutorService executor = Executors.newFixedThreadPool(10); //对executor进行扩展 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()) { @Override protected void beforeExecute(Thread t, Runnable r) { //super.beforeExecute(t, r); System.out.println("线程执行前操作....."); } @Override protected void afterExecute(Runnable r, Throwable t) { //super.afterExecute(r, t); System.out.println("线程执行后操作....."); } @Override protected void terminated() { //super.terminated(); System.out.println("线程销毁操作......"); } }; threadPoolExecutor.submit(new Task()); } static class Task implements Runnable{ @Override public void run() { System.out.println("task正在工作....."); } } }
LinkedBlockingQueue:链表的队列
SynchronousQueue:容量为0的队列,只有当从链表中取线程的时候,才可以王线程中放队列
线程池扩展2:拒绝策略
比如我只有10个线程来处理请求,如果此时还有其他的请求到来,一种:方法队列中,等待空余线程来处理,但是如果大量的请求消息放入队列中,会占用大量内存,此时我们的一个解决方案:直接丢弃掉这个请求。对于丢弃策略,以下有不同的处理方式。
1、new ThreadPoolExecutor.DiscardOldestPolicy():直接抛出异常
2、new ThreadPoolExecutor.DiscardPolicy():不处理任何事
3、new ThreadPoolExecutor.CallerRunsPolicy():只要线程池没有关闭,等待空闲线程来处理
4、new ThreadPoolExecutor.DiscardOldestPolicy():丢弃一个最老的
示例1
我们直接使用接口创建自己的策略
public class J8ComFuture3 { public static void main(String[] args) { /** * new SynchronousQueue<Runnable>():存放消息队列的方式 * Executors.defaultThreadFactory():指定线程工厂(线程的创建需要工厂创建) * new RejectedExecutionHandler():指定丢弃策略 */ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Executors.defaultThreadFactory(), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("舍弃......"); } }); Task task = new Task(); for (int i=0;i<10;i++){ threadPoolExecutor.submit(task); } } static class Task implements Runnable{ @Override public void run() { System.out.println("task正在工作....."); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
示例2
线程容量设置4:一次性处理4个线程,队列大小设置了4:表示来不及的请求放到了容量为4的队列中,策略是丢弃最老的:所以当一次10个请求过来,只能处理8个(立刻执行4个,放进队列4个),丢弃2个。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());
线程工厂
可以自己实现 ThreadFactory 接口创建自己的线程工厂
线程实现并发(CountDownLatch)
class T{ public static void main(String[] args) { final CountDownLatch countDownLatch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { Thread t = new Thread(new Runnable() { @Override public void run() { try { System.out.println("wait"); Thread.sleep(2000); countDownLatch.await(); System.out.println("begin"); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); countDownLatch.countDown(); //相当一个计数器,每次执行到这,就会-1, } } }
利用CountDownLatch堵塞所有的子线程
class T{ public static void main(String[] args) throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); System.out.println("begin"); //子线程执行完毕,计数器-1; countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } //等在所有的子线程执行完毕之后在继续执行 countDownLatch.await(); } }
fork/join
fork/join作为一个并发框架在jdk7的时候就加入到了我们的java并发包java.util.concurrent中,并且在java 8 的lambda并行流中充当着底层框架的角色。
fork/join大体的执行过程就如先把一个大任务分解(fork)成许多个独立的小任务,然后起多线程并行去处理这些小任务。处理完得到结果后再进行合并(join)就得到我们的最终结果。显而易见的这个框架是借助了现代计算机多核的优势并行去处理数据。
其实fork/join的最特别之处在于它还运用了一种叫work-stealing(工作窃取)的算法,这种算法的设计思路在于把分解出来的小任务放在多个双端队列中,而线程在队列的头和尾部都可获取任务。当有线程把当前负责队列的任务处理完之后,它还可以从那些还没有处理完的队列的尾部窃取任务来处理,这连线程的空余时间也充分利用了!
参考:https://www.cnblogs.com/linlinismine/p/9295701.html
定义个类继承RecursiveTask,将该类实例化添加到 ForkJoinPool()池中,自动执行compute()方法(在compute()方法中使用fork推送子任务,join聚合子任务);
public
class
CountTask
extends
RecursiveTask<Integer> {}(有返回值)
demo
public class TestForkJoinPool { private static final Integer MAX = 200; static class MyForkJoinTask extends RecursiveTask<Integer> { // 子任务开始计算的值 private Integer startValue; // 子任务结束计算的值 private Integer endValue; public MyForkJoinTask(Integer startValue , Integer endValue) { this.startValue = startValue; this.endValue = endValue; } @Override protected Integer compute() { // 如果条件成立,说明这个任务所需要计算的数值分为足够小了 // 可以正式进行累加计算了 if(endValue - startValue < MAX) { System.out.println("开始计算的部分:startValue = " + startValue + ";endValue = " + endValue); Integer totalValue = 0; for(int index = this.startValue ; index <= this.endValue ; index++) { totalValue += index; } return totalValue; } // 否则再进行任务拆分,拆分成两个任务 else { MyForkJoinTask subTask1 = new MyForkJoinTask(startValue, (startValue + endValue) / 2); subTask1.fork(); MyForkJoinTask subTask2 = new MyForkJoinTask((startValue + endValue) / 2 + 1 , endValue); subTask2.fork(); return subTask1.join() + subTask2.join(); } } } public static void main(String[] args) { // 这是Fork/Join框架的线程池 ForkJoinPool pool = new ForkJoinPool(); ForkJoinTask<Integer> taskFuture = pool.submit(new MyForkJoinTask(1,1001)); try { Integer result = taskFuture.get(); System.out.println("result = " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(System.out); } } }