线程的简单学习
-
线程是进程的进一步划分,一个进程代表的是一个程序,一个程序可以运行多个子程序,这些子程序就叫线程
- 在同一段时间内比传统过的进程完成的功能多
- 指的是在同一个时间段内有多个程序在执行,但在一个时间点上只有一个程序
- 一个类只要extends thread则即实现了多线程
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
/ 编写一个类继承自Thread类
class Demo extends Thread
{
// 因为多线程需要一个程序的主体
// 子类要覆写Thread类中的run方法
private String info ;
public Demo(String info)
{
this.info = info ;
}
public void run()
{
for(int i=0;i<10;i++)
{
System.out.println(this.info+":i = "+i) ;
}
}
};
public class ThreadDemo01
{
public static void main(String args[])
{
// 如果此时程序是一个多线程操作,则肯定会交替运行
Demo d1 = new Demo("线程1") ;
Demo d2 = new Demo("线程2") ;
Demo d3 = new Demo("线程3") ;
d1.run() ; //这种调用只是调用类的方法并没有启动多线程,因此结果将是和普通一样输出完d1 ,d2,d3
d2.run() ;
d3.run() ;
}
};
- 修改下,查阅jdk可以看到线程要启动是调用 start()
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
public class ThreadDemo02
{
public static void main(String args[])
{
// 如果此时程序是一个多线程操作,则肯定会交替运行
Demo d1 = new Demo("线程1") ;
Demo d2 = new Demo("线程2") ;
Demo d3 = new Demo("线程3") ;
d1.start() ; //此时会交替运行,start执行的是子类的run()方法
d2.start() ;
d3.start() ;
}
};
- 要是一个类只能继承thread来实现线程,但java中只能继承一个类,因此java又提供了接口Runnable这也可以实现多线程
- 看到Runnable只有一个run()方法,但是Runnable没有提供调用的方法
- 查找文档thread可以看到一个构造方法,
public Thread(Runnable target)
可以看到这样即可调用
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Demo implements Runnable
{
// 因为多线程需要一个程序的主体
// 子类要覆写Thread类中的run方法
private String info ;
public Demo(String info)
{
this.info = info ;
}
public void run()
{
for(int i=0;i<10;i++)
{
System.out.println(this.info+":i = "+i) ;
}
}
};
public class ThreadDemo03
{
public static void main(String args[])
{
// 因为实现的是Runnable接口,所以需要通过Thread类启动多线程
Demo d1 = new Demo("线程1") ;
Demo d2 = new Demo("线程2") ;
Demo d3 = new Demo("线程3") ;
Thread t1 = new Thread(d1) ;
Thread t2 = new Thread(d2) ;
Thread t3 = new Thread(d3) ;
// 启动多线程
t1.start() ;
t2.start() ;
t3.start() ;
}
};
- 建议使用runnable这个是接口,看到thread 其实也是实现了这个接口
- 实现了runnable的接口操作的共享的资源
- 如:卖火车票这个,共有10张票,开4个线程来卖,如继承thread来实现,则一共卖了40张这是不可能的,而用runnalbe则合理
- Runnable 适合多个相干同程序代码去处理统一资源的情况。
- 得到当前线程的方法
public static Thread currentThread()可以看到这个方法是静态的 既可以直接通过类名来访问即:Thread.currentThread()
- 通过getName可以得到当前线程的名字,同样setName可以改名字,及同时可以用构造方法Thread(Runnable target, String name) 这样可以为线程定名字,而且最好线程的名字最好刚开始就给设定
- main方法也是一个线程,
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Demo implements Runnable
{
public void fun()
{
// 得到当前线程的名字
System.out.println(Thread.currentThread().getName()+" --> 在运行。。。") ;
}
public void run()
{
for(int i=0;i<10;i++)
{
this.fun() ;
}
}
};
public class ThreadDemo08
{
public static void main(String args[])
{
Demo d = new Demo() ;
Thread t1 = new Thread(d,"线程1") ;
t1.start() ;
for(int i=0;i<10;i++)
{
d.fun() ;//通过main方法调用的线程是main线程,和t1同时进行
}
}
};
- 线程方法,只能启动一次
线程启动方法,start(),iaAlive()测试线程是否启动和仍然启动
Code
class Demo implements Runnable
{
public void run()
{
System.out.println(Thread.currentThread().getName()+" --> 在运行。。。") ;
}
};
public class ThreadDemo09
{
public static void main(String args[])
{
Demo d = new Demo() ;
Thread t1 = new Thread(d,"线程1") ;
System.out.println("线程启动之前:"+t1.isAlive()) ;
t1.start() ;
System.out.println("线程启动之后:"+t1.isAlive()) ;
// 加入一个for循环
for(int i=0;i<10000000;i++)
{
// 加入一个延迟
;
}
System.out.println("延迟线程之后:"+t1.isAlive()) ;
//因为main方法和是线程和t1交替运行,则t1肯定有被延迟的时候
}
};
- isAlive()方法,此方法在线程之后判断,结果没有固定的内容,因为线程有可能优先执行完,有可能最后执行完。
- sleep()方法,public static void sleep(long millis) throws InterruptedException,看到也是静态方法(毫秒)
- 看到必须用try catch 扑捉
- 线程的强制运行,public final void join(long millis)
throws InterruptedException强制此线程运行完后,别的线程才可以运行
Code
class Demo implements Runnable
{
public void run()
{
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" --> 运行。i = "+i) ;
}
}
};
public class ThreadDemo11
{
public static void main(String args[])
{
Thread t = new Thread(new Demo(),"线程") ;
t.start() ;
for(int i=0;i<100;i++)
{
if(i==10)
{
try
{
t.join() ;
}
catch (Exception e)
{
}
}
System.out.println(Thread.currentThread().getName()+" --> 运行。i = "+i) ;
//System.打印方法是main线程方法和t交替进行,但是当i=10时必须运行完t1后再执行main方法的打印
}
}
};
- 线程的中断方法
public void interrupt()方法
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Demo implements Runnable
{
public void run()
{
System.out.println("1、Demo --> 程序进入休眠状态。") ;
try
{
Thread.sleep(20000) ;
}
catch (Exception e)
{
System.out.println("2、Demo --> 休眠中断") ;
return ;
}
System.out.println("3、Demo --> 程序正常退出。") ;
}
};
public class ThreadDemo12
{
public static void main(String args[])
{
Thread t = new Thread(new Demo(),"AAAA") ;
System.out.println(t.getName()+" --> 线程启动。") ;
t.start() ;
System.out.println("4、MAIN --> 让线程休眠") ;
try
{
// 程序至少可以运行2000毫秒
Thread.sleep(2000) ;
}
catch (Exception e)
{
}
System.out.println("5、MAIN --> 中断线程。") ;
t.interrupt() ;
System.out.println("6、MAIN --> 程序退出。") ;
}
};
- 查看线程是否中断的操作方法 public static boolean interrupted()
- 线程的不同步问题,即延迟造成的。如:
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
class Demo implements Runnable
{
private int ticket = 10 ;
// 加入一个同步方法
public synchronized void fun()
{
// 把需要同步的地方放在同步方法之中,如果没有 synchronized这个关键字,则会出现卖的票为负数
if(this.ticket>0)
{
try
{
Thread.sleep(100) ;
}
catch (Exception e)
{
}
System.out.println(Thread.currentThread().getName()+" --> 卖票:"+this.ticket--) ;
}
}
public void run()
{
while(ticket>0)
{
this.fun() ;
}
}
};
public class ThreadDemo14
{
public static void main(String args[])
{
Demo d = new Demo() ;
Thread t1 = new Thread(d,"售票点 A") ;
Thread t2 = new Thread(d,"售票点 B") ;
Thread t3 = new Thread(d,"售票点 C") ;
t1.start() ;
t2.start() ;
t3.start() ;
}
};
- 同步操作方法既是用synchronized关键字,表示此方法为同步方法
- 还有synchronized的第二种方法,即:同步代码块
1 普通代码块 直接写在方法中的
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
3 静态块 用static关键字括起来的代码块,只执行一次,静态块又要优先构造块执行
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
- 可见同步可用 两种方法 来完成
1 同步方法
2同步代码块
但是同步会产生死锁的问题;
- 死锁就是在多线程编程中,由于处理不当,造成程序停止运行状态
- 线程的通信 同步的经典案例,就是生产者--->消费者
线程的通信问题:一个线程向数据存储空间加入数据(生产者),另一个线程从数据存储空间取出数据(消费者)
1 假设生产者线程刚向数据存储空间加入一个人的姓名,还没有加入性别,但是消费者就将其取走和上一个人的性别联系起来了
2 生产者放入了若干个数据消费者才开始消费,或者消费者取出数据后还没有等生产者在放入数据就把以前的又取走了 重复取出
![](/Images/OutliningIndicators/ContractedBlock.gif)