1、线程
进程:正在进行中的程序
线程:就是进程中一个负责程序执行的控制单元(执行路径)
一个进程中可以多执行路径,称之为多线程
一个进程中至少要有一个线程
开启多个线程是为了同时运行多部分代码
每一个线程都有自己运行的内容,这个内容可以称为要执行的任务
多线程的好处:解决了多部分同时运行的问题
多线程的弊端:线程太多会导致效率的降低
其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换时随机的
JVM启动是就启动了多个线程,至少有两个线程可以分析得出来
1、执行main函数的线程
该线程的任务代码都定义在main函数中
2、负责垃圾回收的线程
2、创建线程
创建线程方式之一:继承Thread类
步骤:
1、定义一个类继承Thread类
2、覆盖Thread类中的run方法
3、直接创建Thread的子类对象创建线程
4、调用start方法开启线程并调用线程的任务run方法执行
创建线程的第二种方式:实现Runnable接口
1、定义类实现Runnable接口
2、覆盖接口中的Run方法,将线程的任务代码封装到run方法中
3、通过Thread类创建线程对象,并将Runnable接口中的子类对象作为Thread类的构造函数的参数进行传递
因为线程的任务都封装在Runnable接口子类对象的run方法中,所以要在线程对象创建时就必须明确要运行的任务
4、调用线程对象的start方法开启线程
第一种方式代码:
可以通过Thread的getName()获取线程的名称Thread-编号(从0开始)
主线程的名字就是:main 可以通过Thread.currentThread().getName()获取正在运行的线程
class Demo extends Thread //第一步
{
private String name;
Demo(String name)
{
this.name=name;
}
public void run() //第二步,覆盖run()方法,Thread类中有这个方法
{
for(int x=0;x<10;x++)
{
System.out.println(name+"....x="+x+"...."+getName());//getName()是Thread的方法,用来获取线程的名字
}
}
}
class ThreadDemo2
{
public static void main(String[] args)
{
/*
创建线程的目的是为了开启一条执行路径,去运行制定的代码和其他代码同时执行
而运行的指定代码就是这个执行路径的任务。
jvm创建的主线程的任务都定义在了主函数中
而自定义的主线程的任务都定义在哪儿呢?
Tread类用于描述线程,线程是需要任务的,所以Thread类就是对任务的描述
这个任务就是通过Thread类中的run方法来体现的,也就是说,run 方法就是封装自定义
线程运行任务的函数
run方法中定义的就是线程要运行的任务代码
开启线程是为了运行指定代码,所以只有继承Thread类并覆写run方法并
将运行的代码定义在run方法中即可
*/
Demo d1=new Demo("旺财"); //第三步 创建子类对象
Demo d2=new Demo("xiaoqiang"); //创建线程
d1.start(); //父类Thread中的方法,开启线程并调用run方法
System.out.println("haha...."+Thread.currentThread().getName());
d2.start();
}
}
创建线程的第二种方式代码:
class Demo implements Runnable //准备拓展Demo类的功能,让其中的内容可以作为线程的任务执行
{ //第一步定义类实现Runnable接口 //通过接口的形式完成
public void run()//第二步 覆盖接口中的Run方法,将线程的任务代码封装到run方法中
{
show();
}
public void show()
{
for (int x=0;x<20;x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class ThreadDemo3
{
public static void main(String[] args)
{
Demo d=new Demo();
Thread t1=new Thread(d);//第三步 通过线程类创建对象
Thread t2=new Thread(d);
t1.start();//第四步 调用线程对象的start方法开启线程
t2.start();
}
}
两种方式比较:
实现Runnable接口的好处:
1、将线程的任务从线程的子类中分离出来,进行了单独的封装
按照面向对象的思想将任务封装成对象
2、避免了java单继承的局限性
所以创建线程的第二种方式较为常用
3、线程安全问题
线程安全问题产生的原因:
1、多个线程在操作共享的数据
2、操作共享数据的线程代码有多条
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算就会导致线程安全问题的产生
解决思路:
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候其他线程不可以参与运算。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算
在java中,用同步代码块就可以解决这个问题
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码;
}
对象可以是任意对象
同步的好处:解决了线程的安全问题
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁
同步的前提:同步中必须有多个线程并使用同一个锁
安全问题示例代码:
class Ticket implements Runnable
{
private int num=100;
Object obj=new Object();
public void run()
{
//Object obj=new Object();//放在这个位置的时候就不是使用的同一个锁了,相当于每个线程都有一个锁,每个锁只有一个线程
while(true)
{
/*
下面可能出现线程安全问题,当num=1时,线程1执行完num>0的时候,还没执行下面的语句CPU就切换到线程2(这个时候num还等于1),并执行了
下面的一条语句使得num=0,此时再切换到线程1,num=-1,导致出错
原因:多个线程共享了数据num 操作共享数据的线程代码有两条 num>0 以及下面的num--
if(num>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
*/
synchronized(obj)//相对于一个锁,每次判断下,如果有线程在执行里面的数据,其他线程就进不来
{
if(num>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t=new Ticket();//创建一个线程任务对象
Thread t1=new Thread(t);//创建一个线程任务对象
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步函数和同步代码块实现方式:
class Bank
{
private int sum;
private Object obj=new Object();
/*
解决办法二
*/
public synchronized void add(int num)//同步函数
{
sum=sum+num;
System.out.println("sum="+sum);
}
/*
解决办法一 同步代码块
public void add(int num)
{
synchronized(obj)
{
sum=sum+num;
System.out.println("sum="+sum);
}
}
*/
}
class Cus implements Runnable
{
private Bank b=new Bank();
public void run()
{
for(int x=0;x<3;x++)
{
b.add(100);//add方法也是线程运行的代码,add方法中有两条语句,所以存在线程安全隐患
}
}
}
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();
}
}
同步函数和同步代码块的区别:
同步函数和同步代码块的区别:
同步函数的锁是固定的this对象,同步代码块的锁是任意的对象,建议使用同步代码块。
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass获取
也可以用当前类名.class表示
this.getClass()是非静态的
示例代码:
class Ticket implements Runnable
{
private static int num=100;
boolean flag=true;
public void run()
{
while(true)
{
synchronized(Ticket.class)//this.getClass()
{
if(num>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
public static synchronized void show()//静态同步函数
{
if(num>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
class SynFunctionChronized
{
public static void main(String[] args)
{
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
t2.start();
}
}
单例模式的线程问题
/*
线程下的单例模式,建议使用饿汉式
*/
//饿汉式
class Singele
{
private static final Single s=new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
//懒汉式
class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if(s==null)//可以提高效率
{
synchronized(Single.class)//解决线程的安全问题
{
if(s==null)
s=new Single();
}
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
死锁
死锁案例:
/*
死锁:常见情景之一:同步的嵌套
*/
class Ticket implements Runnable
{
private static int num=100;
boolean flag=true;
Object obj=new Object();
public void run()
{
if(flag)
while(true)
{
synchronized(obj)//该程序死锁了,这个地方拿到了obj锁
{
show();//这是同步函数,拿到的是this锁,同步函数里面又要等待obj锁释放
}
}
else
while(true)
this.show();//这个位置拿着this锁,里面等待着obj锁释放
}
public synchronized void show()
{
synchronized(obj)
{
if(num>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args)
{
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
t.flag=false;
t2.start();
}
}
/*
死锁案例
*/
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run()
{
/*
线程1拿到a锁,等待b锁
线程2拿到b锁,等待a锁时变成了死锁
当然也有可能和谐掉了:线程1拿到a锁并拿到了b锁释放a锁,线程2拿到a锁释放b锁
*/
if(flag)
{
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+"...if locka...");
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"if lockb...");
}
}
}
else
{
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"else lockb...");
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+"else locka...");
}
}
}
}
}
class MyLock
{
public static final Object locka=new Object();
public static final Object lockb=new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Test a=new Test(true);
Test b=new Test(false);
Thread t1=new Thread(a);
Thread t2=new Thread(b);
t1.start();
t2.start();
}
}
生产者消费者问题
/*
生产者,消费者
多生产者,多消费者问题
if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况
while判断标记解决了线程获取执行权后是否要运行
notify只能唤醒一个线程,如果本方唤醒了本方,没有意义,而且while判断标记
+notify会导致死锁
notifyAll解决了本方线程一定会唤醒对方线程的问题
*/
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name)
{
while(flag)//这个位置如果像单生产者,消费者一样用if判断标记则会导致数据错误的情况
try{this.wait();}catch(InterruptedException e){}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
notifyAll();//这个位置如果像但生产者,消费者一样用notify则会导致死锁情况的产生
}
public synchronized void out()
{
while(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...消费者......"+this.name);
flag=false;
notifyAll();
}
}
//生产者
class Producer implements Runnable
{
private Resource r;
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}
class Custumer implements Runnable
{
private Resource r;
Custumer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ProducerCustumerDemo
{
public static void main(String[] args)
{
Resource r=new Resource();
Producer pro=new Producer(r);
Custumer cus=new Custumer(r);
Thread t1=new Thread(pro);
Thread t2=new Thread(cus);
t1.start();
t2.start();
}
}
/*
jdk1.5以后将同步和锁封装成了对象
并将操作锁的隐式方式定义到了该对象中
将隐式动作变成了显示动作
Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式锁操作
变成现实锁操作。同时更为灵活,可以一个锁上加上多组监视器。
lock():获取锁
unlock():释放锁,通常需要定义finally代码块中
Condition接口:出现替代了Object中的wait notify notifyAll方法,
将这些监视器方法单独进行了封装,变成Condition监视器对象
可以任意锁进行组合
await()
signal()
signalAll()
*/
import java.util.concurrent.locks.*;
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
//创建一个锁对象
Lock lock=new ReentrantLock();
//通过已有的锁获取该锁上的监视器对象
//Condition con=lock.newCondition();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者
Condition producer_con=lock.newCondition();
Condition custumer_con=lock.newCondition();
public void set(String name)
{
lock.lock();
try
{
while(flag)
//try{this.wait();}catch(InterruptedException e){}
try{producer_con.await();}catch(InterruptedException e){}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
//notifyAll();
custumer_con.signal();
}
finally
{
lock.unlock();
}
}
public void out()
{
lock.lock();
try
{
while(!flag)
//try{this.wait();}catch(InterruptedException e){}
try{custumer_con.await();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...消费者......"+this.name);
flag=false;
//notifyAll();
producer_con.signal();
}
finally
{
lock.unlock();
}
}
}
//生产者
class Producer implements Runnable
{
private Resource r;
Producer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}
class Custumer implements Runnable
{
private Resource r;
Custumer(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ProducerCustumerDemo2
{
public static void main(String[] args)
{
Resource r=new Resource();
Producer pro=new Producer(r);
Custumer cus=new Custumer(r);
Thread t1=new Thread(pro);
Thread t2=new Thread(cus);
t1.start();
t2.start();
}
}
wait和sleep的区别
wait 和 sleep的区别:
1、wait可以指定时间也可以不指定
sleep必须指定时间
2、在同步中时,对cpu的执行权和锁的处理不同
wait:释放执行权,释放锁
sleep:释放执行权,不释放锁
停止线程
/*
停止线程:
1、stop方法(不推荐使用)
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常用
*/
class StopThread implements Runnable
{
private boolean flag=true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"......");
}
}
public void setFlag()
{
flag=false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st=new StopThread();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
t1.start();
t2.start();
int num=1;
for(;;)
{
if(++num==50)
{
st.setFlag();
break;
}
System.out.println("main...."+num);
}
System.out.println("over");
}
}
/*
任务都会有循环结构,只要控制住循环就可以结束任务
控制循环通常就用定义标记来完成
但是如果线程处于了冻结状态,无法读取标记
可以使用interrupt()方法将线程从冻结状态强制恢复到
运行状态中来,让线程具备cpu的执行资格
但是强制动作会发生了InterruptedException,记得要处理
*/
class StopThread implements Runnable
{
private boolean flag=true;
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(e+"..."+e);
}
System.out.println(Thread.currentThread().getName()+"......+++");
}
}
public void setFlag()
{
flag=false;
}
}
class StopThreadDemo1
{
public static void main(String[] args)
{
StopThread st=new StopThread();
Thread t1=new Thread(st);
Thread t2=new Thread(st);
t1.start();
t2.start();
int num=1;
for(;;)
{
if(++num==50)
{
//st.setFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println("main..."+num);
}
System.out.println("Hello World!");
}
}
多线程误区
class Test implements Runnable //错在这,因为没有覆盖接口中的抽象方法,所以必须Test必须是抽象类,然而并不是
{
public void run(Thread t) //run没有参数
{
}
}
//如果错误,错误发生在哪一行?
class ThreadTest
{
public static void main(String[] args)
{
new Thread(new Runnable()
{
public void run()
{
System.out.println("runnable run");
}
})//括号里面的是任务
//下面的这个类是Thread的子类
//调用顺序:有子类的先以子类为主、任务、自身
//所以结果是:subThread run
{
public void run()
{
System.out.println("subThread run");
}
}.start();
/*
new Thread()
{
public void run()
{
for(int x=0;x<50;x++)
{
System.out.println(Thread.currentThread().getName()+"...x="+x);
}
}
}.start();
for(int x=0;x<50;x++)
{
System.out.println(Thread.currentThread().getName()+"...y="+x);
}
Runnable r=new Runnable()
{
public void run()
{
for(int x=0;x<50;x++)
{
System.out.println(Thread.currentThread().getName()+"...z="+x);
}
}
};
new Thread(r).start();
*/
}
}