(六)—Thread其它方法

最后更新于:2022-04-01 10:16:32

这篇主要是介绍java API中Thread类的其他方法的作用 ### Thread类 void interrupt() 中断线程。 当线程处于冻结状态时(即,wait()、sleep())执行方法受阻时, 调用该方法,会将处于冻结状态的线程回复到运行状态。会出现一个InterruptException异常 void setPriority() 设置线程的优先级。线程一共有10个优先级,最大的优先级为10,最小为1. 默认为5。 ~~~ class InterRunnable implements Runnable{ private int x; private boolean flag=true; public synchronized void run(){ if(flag){ try{ wait(); //让线程处于等待状态 }catch(InterruptedException e){ System.out.println(Thread.currentThread().getName()+"....exception.."); flag = false; } System.out.println(Thread.currentThread().getName()+"....run.."+x++); } } public void changeFlag(){ this.flag = false; } } public class ThreadMethod{ public static void main(String args[]){ InterRunnable ir = new InterRunnable(); Thread t1 = new Thread(ir); Thread t2 = new Thread(ir); Thread t3 = new Thread(ir); t1.setPriority(Thread.MAX_PRIORITY); //设置最大优先级 t2.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); t3.start(); for(int i=0;i<100;i++){ if(i==60){ t1.interrupt(); t2.interrupt(); t3.interrupt(); break; //当i=60时,就让程序结束 } System.out.println(Thread.currentThread().getName()+"........"+i); } System.out.println("over"); } } ~~~ void setDaemon(boolean on)**将该线程标记为守护线程或用户线程。 该方法又称设置线程为后台线程,当它所依附的前台线程消亡时,它也自动消亡。 ~~~ class InterRunnable implements Runnable{ private int x; private boolean flag=true; public synchronized void run(){ while(flag) System.out.println(Thread.currentThread().getName()+"....run.."+x++); } public void changeFlag(){ this.flag = false; } } public class ThreadMethod{ public static void main(String args[]){ InterRunnable ir = new InterRunnable(); Thread t1 = new Thread(ir); Thread t2 = new Thread(ir); Thread t3 = new Thread(ir); t1.setDaemon(true); //设置为守护线程 t2.setDaemon(true); //设置为守护线程 t3.setDaemon(true); //设置为守护线程 t1.start(); t2.start(); t3.start(); for(int i=0;i<100;i++){ if(i==60){ break; //当i=60时,就让程序结束 } System.out.println(Thread.currentThread().getName()+"........"+i); } System.out.println("over"); } } ~~~ void join()等待该线程终止 当线程A执行到了B线程的join方法时,A就会等待,等B执行完了之后,A才会执行。 join可以用来临时加入线程执行。 ~~~ <span style="font-size:18px;">class InterRunnable implements Runnable{ private int x; private boolean flag=true; public void run(){ for(int i=0;i<50;i++) System.out.println(Thread.currentThread().getName()+"....run.."+i++); } public void changeFlag(){ this.flag = false; } } public class ThreadMethod{ public static void main(String args[])throws Exception{ InterRunnable ir = new InterRunnable(); Thread t1 = new Thread(ir); Thread t3 = new Thread(ir); t1.start(); t1.join(); t3.start(); for(int i=0;i<100;i++){ if(i==60){ break; //当i=60时,就让程序结束 } System.out.println(Thread.currentThread().getName()+"........"+i); } System.out.println("over"); } }</span> ~~~ static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。 ~~~ <span style="font-size:18px;">class InterRunnable implements Runnable{ private int x; private boolean flag=true; public void run(){ for(int i=0;i<50;i++) System.out.println(Thread.currentThread().getName()+"....run.."+i++); Thread.yield(); } public void changeFlag(){ this.flag = false; } } public class ThreadMethod{ public static void main(String args[])throws Exception{ InterRunnable ir = new InterRunnable(); Thread t1 = new Thread(ir); Thread t3 = new Thread(ir); t1.start(); t3.start(); } } </span> ~~~
';

(五)—生产者消费者替换方案

最后更新于:2022-04-01 10:16:30

### java 在1.5之后提供了多线程升级解决方案 其中Synchronized被接口Lock所代替 Object中的wait notify notifyAll被 Condition接口中的方法所替代。 Lock类的介绍:----其中,Lock 替代了 synchronized 方法和语句的使用 lock:获取锁 unlock:释放锁 newCondition:返回Condition实例 Condition类的介绍:----Condition 替代了 Object 监视器方法的使用 await() 是线程等待,类似于wait方法。需要抛 signal() 唤醒一个等待的线程 signalAll() 唤醒所有等待的线程 ### 本例主要演示生产者、消费者这个经典案例的替换方案。 并学习Lock和Condition的方法 描述:生产者负责生产商品,消费者负责消费商品,其中生产者为空时不能,消费者不能进行消费。 实现步骤: 1、定义一个操作类方法:Resources类,属性:name,商品编号 count.标志 flag. 当flag为false时,表示生产者还未生成,这是需要执行生成方法。执行完后将flag设置为true。 并唤醒消费者去执行消费方法  当flag为true时,表示生产者已经生产一个产品,执行等待(await)操作。此时,消费者执行完消 费操作之后,将flag设置为false,并唤醒生产者执行生产方法。 void set(String name)方法,void out();方法。 2、生产者类:Producer实现Runnable接口,覆写run方法。Producer(Resources res),有参构造方法 3、消费者类:Consumer实现Runnabel接口,覆写run方法。Consumer(Resources res),有参构造方法 4、创建多个生产者、消费这线程。用来启动多线程 ~~~ import java.util.concurrent.locks.*; public class ProducerConsumer{ public static void main(String args[]){ Resources res = new Resources(); Producer pro = new Producer(res); Consumer con = new Consumer(res); Thread t1 = new Thread(pro); //生产者.. Thread t2 = new Thread(pro); //生产者.. Thread t3 = new Thread(pro); //生产者.. Thread t4 = new Thread(con); //生产者.. Thread t5 = new Thread(con); //生产者.. Thread t6 = new Thread(con); //生产者.. t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); } } class Resources{ private String name; private int count=1; private boolean flag; private final Lock lock = new ReentrantLock(); //创建一个Lock的实例 private Condition condition_Pro = lock.newCondition(); //创建一个控制Producer线程的Condition实例 private Condition condition_Con = lock.newCondition();//创建一个控制Consumer线程的condition实例 public void set(String name)throws InterruptedException{ lock.lock(); //开启锁 try{ while(flag) //需要持续判断锁,如果为true,需要等待 condition_Pro.await(); //Producer线程等待。 this.name = name+"--"+(++count); System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag = true; condition_Con.signal(); //唤醒Customer的线程 }finally{ lock.unlock(); //无论程序是否执行完,都要释放资源 } } public void out()throws InterruptedException{ lock.lock(); try{ while(!flag) //当flag为false时,没有生成,消费者需要等待 condition_Con.await(); //消费者线程等待 System.out.println(Thread.currentThread().getName()+".......消费者........."+count); flag = false; condition_Pro.signal(); //唤醒Producer的线程 }finally{ lock.unlock(); } } } class Producer implements Runnable{ Resources res; public Producer(Resources res){ this.res = res; } public void run(){ //重复执行生产的方法 try{ while(true) res.set("lzl"); }catch(InterruptedException e){} } } class Consumer implements Runnable{ Resources res; public Consumer(Resources res){ this.res = res; } public void run(){ //重复执行消费的方法 try{ while(true) res.out(); }catch(InterruptedException e){} } } ~~~
';

(四)—线程间的通信

最后更新于:2022-04-01 10:16:27

### 线程之间的通信 描述:存在两个线程,一个线程负责写入信息,另一个线程负责打印信息。 model类Student name sex (私有类) 线程:Input类,Output类。 启动两个线程分别执行打印和写入操作. ~~~ public class InputOutputDemo{ public static void main(String args[]){ Student stu = new Student(); Input in = new Input(stu); Output out = new Output(stu); Thread t1 = new Thread(in); //存入学生信息的线程 Thread t2 = new Thread(out); //打印学生信息的线程 t1.start(); t2.start(); } } class Input implements Runnable{ private Student stu; boolean flag = false; public Input(Student stu){ this.stu = stu; } public void run(){//这个线程的run方法负责将姓名和性别存入 /*在这里定义两个学生切换着存入学生的信息 */ while(true){ if(flag){ stu.name = "xy"; stu.sex = "woman"; flag = false; }else{ stu.name = "李志磊"; stu.sex = "男男男男男男"; flag = true; } } } } class Output implements Runnable{ private Student stu; public Output(Student stu){ //保证传入对象唯一性 this.stu = stu; } public void run(){ while(true) System.out.println(stu.name+"..."+stu.sex); } } class Student{ String name; String sex; } ~~~ 打印信息: ~~~ xy...woman xy...woman xy...男男男男男男 xy...woman 李志磊...woman 李志磊...男男男男男男 李志磊...男男男男男男 李志磊...男男男男男男 ~~~ 当线程执行之后,会出现线程间数据问题。(这里需要使用同步的方法来修改代码) 同步的前提: 1、至少有两个线程,在程序中运行 2、同步时使用同一个锁对象。 ~~~ public class InputOutputDemo{ public static void main(String args[]){ Student stu = new Student(); Input in = new Input(stu); Output out = new Output(stu); Thread t1 = new Thread(in); //存入学生信息的线程 Thread t2 = new Thread(out); //打印学生信息的线程 t1.start(); t2.start(); } } class Input implements Runnable{ private Student stu; boolean flag = false; public Input(Student stu){ this.stu = stu; } public void run(){//这个线程的run方法负责将姓名和性别存入 /*在这里定义两个学生切换着存入学生的信息 */ while(true){ synchronized(stu){ if(flag){ stu.name = "xy"; stu.sex = "woman"; flag = false; }else{ stu.name = "李志磊"; stu.sex = "男男男男男男"; flag = true; } } } } } class Output implements Runnable{ private Student stu; public Output(Student stu){ //保证传入对象唯一性 this.stu = stu; } public void run(){ while(true) synchronized(stu){ System.out.println(stu.name+"..."+stu.sex); } } } class Student{ String name; String sex; } ~~~ ### 唤醒等待机制(重要) java Object类中存在以下方法: wait(): notify(); notifyAll(); 特点:都使用在同步当中,因为要对持有锁(监视器)的对象操作。 所以要在同步中使用,因为同步中才有锁。 描述:要求input类读入一个信息,紧接着output就打印出这条信息。 解决思路: 我们需要将这个类添加一个标识flag。 flag==flase时:表示input没有信息 input开始读入信息,将flag设置为true,并将output唤醒 flag==true时:表示input有信息。 ouput开始打印信息,将flag设置为false,并唤醒input ~~~ public class InputOutputDemo1{ public static void main(String args[]){ Student stu = new Student(); Input in = new Input(stu); Output out = new Output(stu); Thread t1 = new Thread(in); //存入学生信息的线程 Thread t2 = new Thread(out); //打印学生信息的线程 t1.start(); t2.start(); } } class Input implements Runnable{ private Student stu; boolean flag = false; int x=0; public Input(Student stu){ this.stu = stu; } public void run(){//这个线程的run方法负责将姓名和性别存入 /*在这里定义两个学生切换着存入学生的信息 */ while(true){ if(flag){ stu.set("xy","woman"); flag = false; }else{ stu.set("李志磊","男男男男男"); flag = true; } } } } class Output implements Runnable{ private Student stu; public Output(Student stu){ //保证传入对象唯一性 this.stu = stu; } public void run(){ while(true){ stu.out(); } } } class Student{ private String name; private String sex; private boolean flag; //设置标志,默认为false public synchronized void set(String name,String sex){ if(flag) try{this.wait();}catch(Exception e){}; this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out(){ if(!flag){ try{this.wait();}catch(Exception e){}; }else{ System.out.println(name+"-----"+sex); flag = false; this.notify(); } } } ~~~
';

(三)—-死锁(面试常见)

最后更新于:2022-04-01 10:16:25

### 延迟加载的单例多线程设计模式(懒汉式)   单例模式 是让调用者只产生一个唯一的对象。 分为饿汉式和懒汉式 **饿汉式:** ~~~ class EHan{ private static final EHan e = new EHan(); public eHan(){} public static EHan getInstance(){ return e; } } ~~~ **懒汉式:** ~~~ class LanHan{ private static LanHan e = new LanHan(); public LanHan(){} public static LanHan getInstance(){ if(e == null){ e = new LanHan(); return e; } return e; } } ~~~ 这里懒汉式的写法存在多线程的安全性问题,通过同步锁来解决这个问题。 ~~~ class LanHan{ private static LanHan e = new LanHan(); public LanHan(){} public static LanHan getInstance(){ if(e == null){ //这种写法,减少判断同步锁的次数,并同样达到同步代码的作用。 synchronized(LanHan.class){ if(e == null){ e = new LanHan(); return e; } } } return e; } } ~~~ ### 死锁的产生原因 1)产生原因:同步代码中嵌套同步. 2)当flag为true时,同步代码块(定义为代码块1)拿到objA的锁,执行代码块中的内容。接着要拿到objB的锁, 此时flag为false,值行flag为false的代码(定义为2),同步代码块拿到objB的锁,需要拿到objA的锁,还没有执行完代码块中的代码,并没有释放objB的锁。 因此代码块1拿不到objB的锁,代码快没有执行完,不释放objA的锁。 因此产生了死锁。 ~~~ class SyncRunnable implements Runnable{ public boolean flag = false; public static Object objA = new Object(); public static Object objB = new Object(); public void run(){ if(flag){ while(true){ //定义为:代码块1 synchronized(objA){ //objA锁 System.out.println("if objA----"); synchronized(objB){ //objB锁 System.out.println("if objB----"); } } } }else{ while(true){ // 定义为:代码块 2 synchronized(objB){ //objB锁 System.out.println("else objB----"); synchronized(objA){ //objA锁 System.out.println("else objA----"); } } } } } } public class SingleInstance{ public static void main(String args[]){ SyncRunnable sr1 = new SyncRunnable(); sr1.flag = true; SyncRunnable sr2 = new SyncRunnable(); sr2.flag = false; Thread t1 = new Thread(sr1); Thread t2 = new Thread(sr2); t1.start(); t2.start(); } } ~~~
';

(二)—线程的同步

最后更新于:2022-04-01 10:16:23

我们知道多线程存在一些安全性问题,java为多线程提供给了一个解决多线程安全问题的关键字synchronized。 ### 同步的前提 1、具有两个及以上的线程 2、必须是多个线程使用同一个锁 java提供了Synchronized来解决多线程的安全性问题。 好处:可以解决多线程的安全问题 弊端:每次都要判断锁,耗费系统资源。 ### 同步代码块 synchronized(对象){ //需要加锁的代码块.. } 这个例子演示同步的作用及意义。 ~~~ public class DemoSync{ public static void main(String args[]){ DemoRunnable dr = new DemoRunnable(); Thread t1 = new Thread(dr); Thread t2 = new Thread(dr); Thread t3 = new Thread(dr); Thread t4 = new Thread(dr); t1.start(); t2.start(); t3.start(); t4.start(); } } class DemoRunnable implements Runnable{ private int ticket=100; Object obj = new Object(); //相当于传入的对象锁 public void run(){ while(true){ synchronized(obj){ if(ticket>0){ try{ Thread.sleep(10); }catch(Exception e){} System.out.println(Thread.currentThread().getName()+"--售票:"+ticket--); }else{ System.exit(1); } } } } } ~~~ ### 同步函数的使用 **非静态方法的同步** 即用synchronized修饰非静态的方法。  private synchronized void add(int m) 多线程代码块的对象锁是 this,即当前对象的引用。 **静态方法的同步** 用synchronized修饰静态的方法 因为静态方法进入内存以后并没有产生对象,因此不可能是this, 通过验证,多线程代码块的对象锁是类.class. **问题描述** 有两个储户分别到银行300元,每次存入100元,分3次存储。分析是否存在安全性问题。 如何找问题: 1、明确哪些代码是多线程运行代码 2、明确共享数据 3、明确多线程运行代码中哪些语句是操作共享数据的。 ~~~ public class ThisLockDemo{ public static void main(String args[]){ MyRunnable dr = new MyRunnable(); StaMyRunnable smr = new StaMyRunnable(); Thread t1 = new Thread(smr); Thread t2 = new Thread(smr); t1.start(); t2.start(); } } class MyRunnable implements Runnable{ private int sum = 0; public void run(){ for(int i=0;i<3;i++){ add(100); } } private synchronized void add(int m){ sum += m; System.out.println(Thread.currentThread().getName()+"存储:"+sum); } } class StaMyRunnable implements Runnable{ private static int sum = 0; public void run(){ for(int i=0;i<3;i++){ add(100); } } private static synchronized void add(int m){ sum += m; System.out.println(Thread.currentThread().getName()+"存储:"+sum); } } ~~~
';

(一)—之创建线程的两种方式

最后更新于:2022-04-01 10:16:21

###1、首先来了解一下线程和进程的概念 进程的概念: 进程是操作系统资源管理的最小单位,进程是一个动态的实体,他是程序的一次执行过程。也叫作一个控制单元 线程的概念: 线程是进程中独立的控制单元,线程控制着进程的执行。一个进程中至少有一个线程。 java VM(java虚拟机)在运行时启动了一个进程---java.exe 该进程在执行时,至少有一个线程在控制着java程序的运行,并且这个线程存在于java的main函数中, 该线程称之为java的主线程。 扩展:在JVM运行时,除了main函数中的线程在运行外,还有JVM中负责java垃圾回收的线程在运行。因此,java不是单线程运行程序。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-31_56fcdcef9aa8f.jpg) ### 实现多线程的第一种方式(单继承方式): 1.继承自Thread类实现多线程 步骤: 1)创建一个类继承Thread 2)覆写run方法 目的是存放多线程要执行的自定义代码。 3)在main函数中创建该类 4)使用start()方法调用该线程(start方法有两种含义:1,启动多线程。2,调用线程中的run方法) ~~~ public class Day1{ public static void main(String args[]){ DemoThread dt = new DemoThread(); dt.start(); //用于启动线程,并自动执行run方法。 dt.run(); //只是单纯的对象调用,在主线程中执行,并不开启子线程。 for(int i=0;i<50;i++){ System.out.println("main run-----"+i); } } } class DemoThread extends Thread{ public void run(){ for(int i=0;i<50;i++) System.out.println("demo run-----"+i); } } ~~~ 我们发现运行同一个程序,他们的运行结果却不太相同, 这是多个线程都获取系统的CPU资源,CPU执行到谁,谁就运行。 CPU在某一时刻只能执行一个程序(多核除外),CPU在做着快速的切换,以达到看上去是在同时执行的效果。 我们通过打印输出,来判断到底是哪一个线程抢占到了CPU的资源。 Thread类的意义-----用于描述线程。 该类定义了一个功能,用于存储线程要运行的代码,而这个存储功能就是run方法。 run方法中存储线程要执行的 自定义代码块。 而start方法用于启动线程,并自动执行run方法。 Thread类中提供的常用的方法: static Thread currentThread: 返回当前正在执行的线程对象的引用 String getName();返回当前线程的名称。 当使用Thread的无参构造创建线程实例时,java虚拟机会自动为线程创建一个名字。(以Thread-编号的格式) static void sleep(long time)  使线程休眠time时间。 ### 创建线程的第二种方式(实现Runnable接口): 实现Runnable 接口来实现多线程: 步骤: 1)创建类实现Runnable接口 2)实现Runnable接口中的run方法 3)创建Thread对象 4)将Runnable对象作为实际参数传递给Thread的构造方法 5)调用Thread类的start方法,自动执行Runnable对象中的run方法 ~~~ public class Day1Runable{ public static void main(String args[]){ DemoRunnable dr = new DemoRunnable(); Thread t1 = new Thread(dr); Thread t2 = new Thread(dr); Thread t3 = new Thread(dr); Thread t4 = new Thread(dr); t1.start(); t2.start(); t3.start(); t4.start(); } } class DemoRunnable implements Runnable{ private int ticket=100; public void run(){ while(true){ if(ticket>0) System.out.println(Thread.currentThread().getName()+"--售票:"+ticket--); } } } ~~~ 问题:继承方式与实现方式有什么区别 1、避免了单继承的局限性 2、多线程执行代码位置不同: 继承Thread类:代码存放在Thread类的run方法中 实现Runnable类:代码存放在Runnable接口的run方法中。
';

前言

最后更新于:2022-04-01 10:16:18

> 原文出处:[java多线程学习](http://blog.csdn.net/column/details/java-multithreading.html) 作者:[u011521890](http://blog.csdn.net/u011521890) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # java多线程学习 > 主要是结合课本和视频学习了一下java多线程的用法,这个专利适合于初学者学习多线程知识。
';