(六)—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多线程的用法,这个专利适合于初学者学习多线程知识。