什么是多线程?
在计算机编程中,一个基本的概念就是同时对多个任务加以控制。许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。最开始的时候,那些掌握机器低级语言的程序员编写一些“中断服务例程”,主进程的暂停是通过硬件级的中断实现的。尽管这是一种有用的方法,但编出的程序很难移植,由此造成了另一类的代价高昂问题。中断对那些实时性很强的任务来说是很有必要的。但对于其他许多问题,只要求将问题划分进入独立运行的程序片断中,使整个程序能更迅速地响应用户的请求。
最开始,线程只是用于分配单个处理器的处理时间的一种工具。但假如操作系统本身支持多个处理器,那么每个线程都可分配给一个不同的处理器,真正进入“并行运算”状态。从程序设计语言的角度看,多线程操作最有价值的特性之一就是程序员不必关心到底使用了多少个处理器。程序在逻辑意义上被分割为数个线程;假如机器本身安装了多个处理器,那么程序会运行得更快,毋需作出任何特殊的调校。根据前面的论述,大家可能感觉线程处理非常简单。但必须注意一个问题:共享资源!如果有多个线程同时运行,而且它们试图访问相同的资源,就会遇到一个问题。举个例子来说,两个线程不能将信息同时发送给一台打印机。为解决这个问题,对那些可共享的资源来说(比如打机),它们在使用期间必须进入锁定状态。所以一个线程可将资源锁定,在完成了它的任务后,再解开(释放)这个锁,使其他线程可以接着使用同样的资源。
Java对多线程的支持是非常强大的,他屏蔽掉了许多的技术细节,让我们可以轻松的开发多线程的应用程序。
为什么要有多线程?
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。
怎样创建线程?
java创建线程有两种方式:
方式一:继承Thread类
eg:
class PrimThread extends Thread{
public void run(){
for(int i=0;i<60;i++){
System.out.println("demo run");
}
}
}
方式二:实现Runnable接口
步骤:
1,定义实现Runnable接口
2,覆盖Runndable结构中的run方法,将线程要运行的代码放在改run方法。
3,通过Thread类建立线程对象
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
为什么要将Runnable接口的子对象传递给Thread的构造函数?
应为,自定义的run方法所属的对象是Runnable接口的子对象,所以要让线程去制定对象的run方法。就必须明确改子类的run方法所属对象
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
eg:
class Tick implements Runnable{
private int tick = 50;
public void run(){
while(true){
if(tick>0)
System.out.println(Thread.currentThread().getName()+" sall "+tick--);
else
return;
}
}
}
Thread中的重要方法
1,run();
run方法是每个线程对象中必须要有的方法。因为Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法,也就是说thread类中的run方法。用于存储线程要运行的代码。
2,getName();
获取当前线程名称
3,currenThread();
获取当前线程对象
多线程的意义
多线程在显示生活中广泛存在,在计算机中运行着的程序不一定非要等到一个线程执行完后在执行另一个程序,这样会使得程序整体的效率变低,为了解决这个问题,多线程就就提供了解决方案,cpu在执行一段指令后跳转到其他位置执行另一段指令,
cpu在不停的高速切换这线程的执行顺序,比如卖火车票,我们如何在一个窗口卖票50张票要买很长时间,但是我们多开几个窗口,卖50涨票就不需要很长时间了,这个例子就是一个典型的多线程问题每个窗口就相当于一个线程。用代码实现如下:
class Tick implements Runnable{
private int tick = 50;
public void run(){
while(true){
if(tick>0)
System.out.println(Thread.currentThread().getName()+" sall "+tick--);
else
return;
}
}
}
class ThreadDemo{
public static void main(String[] args){
Tick t = new Tick();
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 Tick implements Runnable{
private int tick = 50;
public void run(){
while(true){
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+" sall "+tick--);
}
else
return;
}
}
}
class ThreadDemo{
public static void main(String[] args){
Tick t = new Tick();
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();
}
}
运行结果出现了数据异常:
为了解决安全问题,对多条操作共享数据的语句,只能让一个线程都都执行完,在执行过程中其他线程不能参与执行。Java对多线程的安全问题提供了专业的解决方式---同步代码块---synchronized(对象){}
class Tick implements Runnable{
private int tick = 100;
Object obj = new Object();
public void run(){
while(true){
<span style="color:#ff0000;">synchronized(obj){//obj可以是任意对象没有条件</span>
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+" sall "+tick--);
}
else
return;
}
}
}
}
同步的前提:
1,要有至少2个线程
2,必须多个线程使用的是同一个锁(对象)
3,明确哪些代码需要同步,哪些不需要同步:
eg:
class Bank{
private int sum;
Object obj = new Object();
public void add(int n){
<span style="color:#ff0000;">synchronized(obj){
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){};
System.out.println("sum="+sum);
}</span>
}
}
class Cus implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x=0;x<3;x++)
b.add(100);
}
}
class ThreadDemo{
public static void main(String[] args){
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
在上面一段代码中共享数据是sum而对sum操作的是add函数所以要同步的代码是红色字体的那部分。
同步函数
还是以那个买票的例子来看,共享数据tick将对tick的操作都写到一个函数里面去,让后将函数用synchronized修饰这就构成了同步函数如下:
class Tick implements Runnable{
private int tick = 100;
public void run(){
while(true){
this.show();//此时用的锁是this如果没有this(锁)?
}
}
<span style="color:#ff0000;">public synchronized void show(){
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+" sall "+tick--);
}
}</span>
}
class ThreadDemo{
public static void main(String[] args){
Tick t = new Tick();
Thread t1 = new Thread(t);//创建四个线程并与对象t相关联
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
前面我们提到同步中的锁可以使任意对象,上面的代码中用的锁就是本类对象this
但是我们知道如果同步函数被static修饰后,静态加载进内存时就没本类对象,但是一定有该类对应的字节码文件对象---类名.class,没错这个就是同步的锁
eg:
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s==null){
synchronized(<span style="color:#ff0000;">Single.class</span>){ //同步锁是该类所属的字节码文件对象
if(s==null)
s = new Single();
}
}
return s;
}
}
死锁问题
同步中套套同步可能造成死锁
eg:
class Test implements Runnable{
private boolean flag;
Test(boolean flag){
this.flag = flag;
}
public void run(){
if(flag){
synchronized(MyLock.locka){
System.out.println("if locka");
synchronized(MyLock.lockb){
System.out.println("if lockb");
}
}
}
else{
synchronized(MyLock.lockb){
System.out.println("else lockb");
synchronized(MyLock.locka){
System.out.println("else lokca");
}
}
}
}
}
class MyLock{
static Object locka = new Object();
static Object lockb = new Object();
}
class ThreadDemo{
public static void main(String[] args){
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
如果运行结果如下:
发生死锁:
原因:线程t1运行拿到a锁执行if locka 线程t2运行拿到b锁执行else lockb 线程t1运行拿不到b锁,线程t2运行拿不到a锁 发生死锁
没有发生死锁:
原因:线程t2运行拿到b锁执行else lockb,线程t2运行拿到a锁执行else locka,线程t1运行拿到a锁执行if locka,线程t1运行拿到b锁执行if lockb,没有发生死锁的关键是t2运行后t1没有运行而直接是t2又运行自然能拿到锁继续执行,执行完了后释放了锁
t1才执行。
等待唤醒机制
wait(); notify(); notifuall();
都使用在同步中,应为要对持有的监视器(锁)的线程操作,所以要使用在同步中,应为只有同步中才有锁
为什么这些操作线程的方法一定要定义在Object类中呢?
因为这些方法在操作同步中线程是,都必须要标识他们所操作的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒都必须是同一个锁而锁可以使任意对象,所以可以被任意对象调用的方法定义在Object中
eg:
class Rec{
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 print(){
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+" "+sex);
flag = false;
this.notify();
}
}
class Input implements Runnable{
private Rec r;
Input(Rec r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
if(x==0)
r.set("mike","man");
else
r.set("丽丽","女女");
x = (x+1)%2;
}
}
}
class Output implements Runnable{
private Rec r;
Output(Rec r){
this.r = r;
}
public void run(){
while(true){
r.print();
}
}
}
class ThreadDemo{
public static void main(String[] args){
Rec r = new Rec();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
但是当有多个线程进行相同的操作时可能会产生死锁
eg:生产者消费者问题
class Rec{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name){
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name+"---"+count++;
System.out.println(Thread.currentThread().getName()+"----生产者----"+this.name);
flag = true;
this.notify();
}
public synchronized void print(){
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"----消费者----"+this.name);
flag = false;
this.notify();
}
}
class Producer implements Runnable{
private Rec r;
Producer(Rec r){
this.r = r;
}
public void run(){
while(true){
r.set("商品");
}
}
}
class Consumer implements Runnable{
private Rec r;
Consumer(Rec r){
this.r = r;
}
public void run(){
while(true){
r.print();
}
}
}
class ThreadDemo{
public static void main(String[] args){
Rec r = new Rec();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
}
}
发生死锁:
将notify换成notifyAll();可以解决这个问题,因为需要唤醒对方线程,只用notify,容易出现之唤醒本方线程的情况,导致程序中所有线程都等待。但是notifyAll唤醒所有线程抢占cpu资源,程序运行效率下降。解决方法:JDK1.5有了新的解决方案lock。
如下:
class Rec{
private String name;
private int count = 1;
private boolean flag = false;
<span style="color:#ff0000;">private Lock lock = new ReentrantLock();
private Condition condition_con = lock.newCondition();
private Condition condition_pro = lock.newCondition();</span>
public void set(String name) throws InterruptedException{
lock.lock();
try{
while(flag)
condition_pro.await();
this.name = name+"---"+count++;
System.out.println(Thread.currentThread().getName()+"----生产者----"+this.name);
flag = true;
condition_con.signal();
}
finally{
lock.unlock();
}
}
public void print() throws InterruptedException{
lock.lock();
try{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"----消费者----"+this.name);
flag = false;
condition_pro.signal();
}
finally{
lock.unlock();
}
}
}
线程的结束:
stop已经过时,现在通过结束run方法来结束进程如:
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait();
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+" /Excepion");
<span style="color:#ff0000;">flag = false;</span>
}
System.out.println(Thread.currentThread().getName()+"run");
}
}
}
class ThreadDemo{
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 = 2;
while(true){
if(num++ == 50){
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
}
运行结果: