进程:一个正在执行的程序,每一个进程执行都有一个顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:进程中的一个独立的控制单元,线程在控制着进程的执行
一个进程至少有一个线程
线程:进程中的一个独立的控制单元,线程在控制着进程的执行
一个进程至少有一个线程
创建线程的两种方法
一、继承Thread类,重写run方法
步骤:1、继承thread类
2、重写run()方法(run方法为线程运行时代码即该线程的功能)
3、 调用线程start()方法(直接调用run()相当于普通的类),作用:启动线程并调用run方法
Thread.currentThread();获取当前线程的对象
getName():获取线程名称
设置线程名称:setName或者通过构造函数
二、第二种方式为实现runnable接口(Java类不能多继承)
1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法,将线程要运行的代码存放到run方法中
3、通过Thread类建立线程对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数
为什么要将Runnable接口的子类对象传递给Thread类的构造函数?
自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象
5、调用Thread类的start方法开启线程调用Runnable接口子类的run方法
两种方式的区别: 实现方式避免了单继承的局限性,建议使用实现方式
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable:线程代码放在接口子类的run方法中
一、继承Thread类,重写run方法
步骤:1、继承thread类
2、重写run()方法(run方法为线程运行时代码即该线程的功能)
3、 调用线程start()方法(直接调用run()相当于普通的类),作用:启动线程并调用run方法
Thread.currentThread();获取当前线程的对象
getName():获取线程名称
设置线程名称:setName或者通过构造函数
二、第二种方式为实现runnable接口(Java类不能多继承)
1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法,将线程要运行的代码存放到run方法中
3、通过Thread类建立线程对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数
为什么要将Runnable接口的子类对象传递给Thread类的构造函数?
自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象
5、调用Thread类的start方法开启线程调用Runnable接口子类的run方法
两种方式的区别: 实现方式避免了单继承的局限性,建议使用实现方式
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable:线程代码放在接口子类的run方法中
多线程安全问题
当多条语句操作同一个线程共享数据时,一个线程对多条语句还没有执行完另一个线程参与进来执行,导致共享数据的错误
当多条语句操作同一个线程共享数据时,一个线程对多条语句还没有执行完另一个线程参与进来执行,导致共享数据的错误
class Ticket implements Runnable{//实现接口
private int t=20;
Object obj = new Object();
public void run(){ //重写run方法
while(true){
synchronized(obj){//使用同步代码块,Obejct对象加锁
if(t>0)
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+t--);
}
}
}
}
解决方法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程都不能参与进来
Java对多线程安全提供的解决方式同步代码块 synchronized(对象){需要被同步的代码}
对象如同锁,持有锁的线程可以在同步中执行,没有锁的线程即使有cpu执行权,也进不去
同步的前提:1、必须要有两个或者两个以上的线程
2、必须是多个线程使用同一个锁
好处:解决了线程安全问题
弊端:多个线程需要判断锁,消耗资源
发现问题:
1.多线程运行的代码
2.多线程操作的共享数据
3.明确多线程哪些是执行共享数据的代码
同步所用的锁即对象:
同步函数用的锁即该函数所在类的对象引用:this
静态同步函数把类的字节码的对象当作锁:类名.class
阻塞状态:放弃CPU 执行权,暂时停止运行状态,先回到就绪状态在到运行状态。sleep()、wait()、未获得锁时都进入阻塞状态
死亡状态:线程退出run方法,结束生命周期
sleep释放资源,不释放锁,释放资源,wait释放锁
线程间通讯:多个线程操作同一个资源,但是操作的动作不同
wait(),notify(),notifyAll()都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程时,都必须要标识它们所操作线程持有的锁只有同一个锁上的被等待线程可以被同一个锁上的notify唤醒。
等待和唤醒必须是同一个锁,而锁可以是任意对象,可以被任意对象调用的方法定义在Object类中
同步的前提:1、必须要有两个或者两个以上的线程
2、必须是多个线程使用同一个锁
好处:解决了线程安全问题
弊端:多个线程需要判断锁,消耗资源
发现问题:
1.多线程运行的代码
2.多线程操作的共享数据
3.明确多线程哪些是执行共享数据的代码
同步所用的锁即对象:
同步函数用的锁即该函数所在类的对象引用:this
静态同步函数把类的字节码的对象当作锁:类名.class
class Bank {
private int sum;//共享数据
//Object obj = new Object();
public synchronized void add(int n){//同步函数
//synchronized(obj){
sum = sum+n;//操作共享数据代码
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("sum="+sum);//操作共享数据代码
//}
}
}
class Cus implements Runnable{
private Bank b = new Bank();//共享数据b,但只执行一次
public void run(){
for(int x=0;x<3;x++){
b.add(100);//操作共享数据
}
}
}
public class BankDemo {
public static void main(String[] args) {
Cus c = new Cus();
Thread t1=new Thread(c);
Thread t2=new Thread(c);
t1.start();
t2.start();
}
}
死锁:同步中嵌套锁
class TicketDemo implements Runnable{
private static int t=300;
public boolean flag=true;//线程切换标志
Object obj = new Object();//定义锁对象
public void run(){
if(flag){
while(true){
synchronized(obj){//使用Object锁
show();//Object锁中嵌套this锁
}
}
}
else
while(true)
show();
}
public static synchronized void show(){ //使用this锁
synchronized(obj){ //this锁中嵌套Object锁
if(t>0){
try{Thread.sleep(10);}catch(Exception e){}//释放执行权
System.out.println(Thread.currentThread().getName()+" "+"ticket=code"+t--);
}
}
}
}
线程运行时状态:创建状态,就绪状态:等待获取CPU执行权,运行状态
阻塞状态:放弃CPU 执行权,暂时停止运行状态,先回到就绪状态在到运行状态。sleep()、wait()、未获得锁时都进入阻塞状态
死亡状态:线程退出run方法,结束生命周期
sleep释放资源,不释放锁,释放资源,wait释放锁
线程间通讯:多个线程操作同一个资源,但是操作的动作不同
wait(),notify(),notifyAll()都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程时,都必须要标识它们所操作线程持有的锁只有同一个锁上的被等待线程可以被同一个锁上的notify唤醒。
等待和唤醒必须是同一个锁,而锁可以是任意对象,可以被任意对象调用的方法定义在Object类中
import java.util.concurrent.locks.*;
class Res{
private String name;
private int count=1;
private boolean flag=false;
/* public synchronized void set(String name){
// if(flag)线程唤醒后不判断标志位就继续向下执行,
while(flag)//线程唤醒后再次判断标志位
try{wait();}catch(Exception e){}
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
flag=true;
// this.notify();
this.notifyAll();
}
*/
//jdk1.5新特性
Lock lock = new ReentrantLock();
Condition con_pro=lock.newCondition();
Condition con_con=lock.newCondition();//消费者和生产者使用不同的唤醒对象
public void set(String name){
lock.lock();//加锁
try{
while(flag)
con_pro.await();//生产者等待
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
flag=true; //改变标记
con_con.signal();//明确唤醒消费者
}catch(Exception e){}
finally{
lock.unlock();//释放锁
}
}
/* public synchronized void out(){
// if(!flag)
while(!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"消费者"+count);
flag=false;
// this.notify();只唤醒本方线程将有可能导致所有线程等待
this.notifyAll();
}*/
public void out(){
lock.lock();//和消费者使用同一个锁
try{
while(!flag)
con_con.await();//消费者等待
System.out.println(Thread.currentThread().getName()+"消费者"+count);
flag=false;
con_pro.signal();//唤醒生产者
}catch(InterruptedException e){
System.out.println("close");
System.exit(0);//都等待强行结束线程
}
finally{
lock.unlock();
}
}
}
class Producer implements Runnable{ //生产者实现Runnable接口
private Res r;
Producer(Res r){
this.r=r;//通过构造函数操作同一个资源
}
public void run(){
while(true){
r.set("商品");
}
}
}
class Consumer implements Runnable{//消费者实现Runnable接口
private Res r;
Consumer(Res r){
this.r=r;
}
public void run(){
while(true){
r.out();
}
}
}
public class Custom {
public static void main(String[] args) {
Res r = new Res();
Thread t1= new Thread(new Producer(r));
Thread t2= new Thread(new Producer(r));
Thread t3= new Thread(new Consumer(r));
Thread t4= new Thread(new Consumer(r));
t1.start();
t2.start();
t3.start();
t4.start();
}
}
停止线程:run方法结束线程即停止.
开启多线程运行,运行代码通常是循环结构,只要控制住循环就可以
让run方法结束,也就是线程停止
通过设置标志可以让线程run方法运行结束
特殊情况:当线程处于冻结状态,就不会读到标记,线程就不会结束
当没有指定的方式(notify等)让冻结线程回复到运行状态,这时需要对冻结状态解除,让线恢复到运行状态,就可以操作标记。
Thread类提供interrupt()方法,抛出中断异常,在异常中改变标记
class StopDemo implements Runnable{
private boolean flag=true;
public void run(){
while(flag){
synchronized(this){
try{
wait();//注意没有notify是为让线程处于冻结状态就不会读取到标记
}catch(InterruptedException e){
flag=false;//在异常处理中改变标志位
}
System.out.println(Thread.currentThread().getName());
}
}
}
public void setFlag(){
flag = false;
}
}
class Demo{
public static void main(String[] args){
StopDemo sd = new StopDemo();
int num=1;
Thread t1= new Thread(sd);
Thread t2= new Thread(sd);
t1.start();
t2.start();
while(true){
if(num++==20){
//sd.setFlag();//改变标志位,让run中代码运行结束停止线程
t1.interrupt();//强制清除线程的等待状态,抛出中断异常,在异常处理中改变标志位
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName());
}
}
}
守护线程:public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
守护线程相当于后台线程,当前台没有线程的时候,后台线程自动退出
join():当A线程执行到了B线程的join方法时,A就会等待。当B线程执行完,A才会执行
join可以用来临时加入线程执行
优先级:一共10级优先级,默认为MIN_PRIORITY . setPriority更改线程的优先级。MAX_PRIORITY代表优先级10,MIN_PRIORITY为1
yield() :暂停当前正在执行的线程对象,并执行其他线程
简化代码,封装线程
public static void main(String[] args){
//匿名内部类,继承Thread
new Thread(){
public void run(){
}
}.start();
//实现Runnable接口
Runnable r = new Runable(){
public void run(){
}
}
new Thread(r).start();
}