- 1.线程的创建(附带代码块)
(1)继承Thread类创建多线程(extends)
(代码块1)
(2)实现Runnable接口创建多线程(implements)
(代码块2)
(代码块1)
class dfds {
public static void main(String[] args)
{
MyThread myThread=new MyThread(); //创建MyThread实例对象
myThread.start();//调用MyThread方法
while(true)
System.out.println("main()方法在运行");
}
}
class MyThread extends Thread
{
public void run(){
while (true) {
System.out.println("MyThread类的run()方法在运行");
}
}
}
(代码块2)
class dfds {
public static void main(String[] args)
{
MyThread myThread=new MyThread(); //创建MyThread的实例对象
Thread thread =new Thread(myThread)
; //创建线程对象
thread.start(); //开启线程执行线程中的run()方法
while(true)
System.out.println("main()方法在运行");
}
}
class
MyThread implements Runnable{
public void run(){
while (true) {
System.out.println("MyThread类的run()方法在运行");
}
}
}
- 2.两种实现多线程方式的对比分析
Thread.currentThread().getName()
(1)继承Thread类创建多线程(extends)
没有共享同一个资源
(2)实现Runnable接口创建多线程(implements)
共
享一个资源
用Thread类创建的多线程没有将100张机票分成四个窗口来出售,而是一个窗口出售一百张
用Runnable接口的则将100张机票分成四个窗口来出售,一个窗口出售25张
- 实现Runnable接口相对于继承Thread类来说,有下列显著的好处:
- 适合多个相同程序的线程去处理同一个资源的情况,吧线程通程序代码,数据有效分离,很好地体现面向对象的设计思想
- 可以避免由于java的单继承带来的局限性,在开发中经常碰见一种情况,就是使用了一个已经继承了某一个类的子类创建线程,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,阿么就只能采用Runnable接口的方式。
- 实际上,大部分 的应用程序都是采用第二种方式来创建多线程,寄实现Runnable接口。
- 3. 后台线程
后台线程最主要的是:进程中只有后台线程运行时,进程就会结束。
注意:要将某个线程设置为后台线程,必须在改线程启动之前,也就是说setDaemon()方法必须子啊start()方法前调用,否则就会引发IllegalThreadStateException异常。
- 4 线程生命周期及状态转换
- 1.新建状态
- 创建一个线程对象后,改线程对象就处于新建状态,此时它不能运行,和其他JAVA对象一样,仅仅由Java虚拟机为其分配了内存,没有表现出任何线程的动态特征。
- 2.就绪状态
- 当对象调用了start()方法后,该线程就进入了就绪状态。处于就绪状态的线程位于可运行池中,此时它只是具备了运行的条件,是否获得CPU的使用权开始运行,还需要等待系统的调度
- 3.运行状态
- 如果处于就绪状态的线程获得了CPU的使用权,开始执行run()方法中的线程执行体,则该线程处于运行状态,当一个线程启动后,它不可能一直处于运行状态(除非他的线程执行体足够短,瞬间就结束了),当使用完系统分配的时间后,系统就会剥夺改线程足够的CPU资源,让其他线程获得执行的机会。需要注意的是,只有处于就绪状态的线程才可能装换为运行状态;
- 4.阻塞状态
- 一个正在执行的线程在某些特殊情况下,如执行耗时的输入/输出操作时,会放弃CPU的使用权,进入阻塞状态。线程进入阻塞状态后,就不能进入排队队列。只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
- 下面列举一下线程由运行状态转换为阻塞状态的原因,以及如何从阻塞状态转化为就绪状态
- 当线程试图获取某个对象的同步锁时,如果该锁被其他线程所持有,则当前线程就会进入阻塞状态,如果想从这个阻塞状态进图就绪状态必须获取到其他线程所持有的锁。
- 当线程调用了一个阻塞式的IO方法时,该线程就会进入阻塞状态,如果想进入就绪状态就必须要等到这个阻塞的IO方法返回。
- 当线程调用了某个对象的wait()方法时,也会使线程进入阻塞状态,如果想进入就绪状态就需要使用notify()方法唤醒该线程
- 当线程调用了Thread的sleep(long millis)方法时,也会使线程进入阻塞状态,在这种情况下,只需要等到线程睡眠时间到了以后,线程就会进入就绪状态。
- 当在一个线程中调用了另一个线程的join()方法时,会使当前线程进入阻塞状态,在这种情况下,需要等到新加入的线程运行结束后才会结束阻塞状态,进入就绪状态;
- 需要注意的是:线程只会从阻塞状态进入就绪状态,而不能直接进入运行状态,也就说结束阻塞后的线程需要进入 可运行池等待系统的调度;
- 5死亡状态
- 线程的run()方法正常执行完毕或者线程抛出一个未捕获的异常、错误,线程就会进入死亡状态,线程将不再拥有运行的资格,也不能转换到其他状态。
- 5.线程的调度
- 1. 线程的优先级
-
- Thread类的静态变量
功能描述
static int MAX_PRIORITY表示线程的最高优先级,相当于值10static int MIN_PRIORITY
表示线程的最高优先级,相当于值1
static int NORM_PRIORITY
表示线程的最高优先级,相当于值5
main线程具有普通的优先级,可以通过Thread类的
setPriority(int newPriority)方法对其进行设置,该方法中的参数newPriority接受的1~10之间的整数或者Thread类的三个静态变量。
代码块解释:
class MaxPriority implement Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"正在输出:"+i);
} } }
//定义类MINpriority实现Runnable接口
class MinPriority implements Runnable{
public void run(){
for(int i=0;i<10;i++)
{System.out.println(Thread.currentThread().getName()+"正在输入:"+i);
} } }
public class Example07{
public static void main(String[] args){
Thread minPriority=new Thread(new MinPriority(),"优先级较低的线程");
Thread maxPriority =new Thread(new MaxPriority(),"优先级较高的线程");
minPrioroty.setPriority(Thread.MIN_PRIORITY);//设置线程的优先级为1
maxPriototy,setPriority(Thread.MAX_PRIORITY);//设置线程的优先级为10
//开启两个线程
maxPriority.start();
minPriority.start();
}
}
注意:有些操作系统并不是支持跟JAVA一样的优先级 记得不要太过依赖优先级,而只是把线程优先级作为一种提高程序效率的手段。
- 2、线程休眠
调用sleep(毫秒数)
代码块例子
public class xiumian {
public static void main(String[] args) {
// TODO 自动生成的方法存根
new Thread(new sleepThread()).start();
for(int i=1;i<=10;i++)
{
if(i==5){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
System.out.println("主线程正在输出:"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} }
}
}
class sleepThread implements Runnable{
public void run(){
for(int i=1;i<=10;i++)
{
if(i==3)
{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println("线程--正在输出:"+i);
try {
Thread.sleep(500);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}}}
- 3. 线程让步
线程让步:是用过用yield()方法来实现的,改方法跟sleep()方法有点类似,都可以让当前正在运行的线程暂停一下,区别在于yield()方法不会阻塞该线程,它只是将线程转换为就绪状态,让系统的调度器再一次重新调度一次。当某个线程调用yield()方法后,只有与当前线程优先级相同或者更高的线程才能获得执行的机会。
代码例子:
class YieldThread extends Thread{
public YieldThread(String name){
super(name);}
public void run(){
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
if(i==3)
System.out.print("线程让步:");
Thread.yield();//线程运行到此,做出让步;
}
}
}
public class rangbu {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成的方法存根
Thread t1=new YieldThread("线程A");
Thread t2=new YieldThread("线程B");
t1.start();
t2.start();
}
}
- 4线程插队
当某个线程中调用其他线程的join()方法时,调用的线程将被阻塞,直到被join()方法加入到线程执行完成后它才继续运行;
代码块示例:
public class chadui {
public static void main(String[] args) throws Exception {
// TODO 自动生成的方法存根
Thread t=new Thread(new EmergencyThread(),"线程一");
t.start();
for(int i=1;i<6;i++)
{
System.out.println(Thread.currentThread().getName()+"输入:"+i);
if(i==2)
{
}
Thread.sleep(500);
}
}
}
class EmergencyThread implements Runnable{
public void run(){
for(int i=1;i<6;i++)
{
System.out.println(Thread.currentThread().getName()+"输入:"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
} }
}
- 5.多线程同步
控制在同个时刻只能被一个线程访问
1.线程安全 同步:
synchronized
代码示例:
public class tongbu {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成的方法存根
SalaThread salaThread=new SalaThread();
new Thread(salaThread,"线程一").start();
new Thread(salaThread,"线程二").start();
new Thread(salaThread,"线程三").start();
new Thread(salaThread,"线程四").start();
}
}
class SalaThread implements Runnable{
private int tickets=100;
Object lock=new Object();
public void run(){
while(true){
synchronized (lock) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"---卖出的票"+tickets--);
}else
{break;}
}
}
}
}
注意:同步代码块中的锁对象可以是任意的类型对象,但是多个线程共享的锁对象必须是唯一的。“任意”说的是共享所对象的类型。所以,锁对象的创建代码不能放到run()方法中,否则每个线程运行到run()方法都会创建一个新对象,这样每个线程都会有一个不同的锁,每个锁都有自己的标志位。线程之间便不能产生同步的效果。
- 6.同步方法
synchronized 返回值类型 方法名([参数1,......]){ }
被synchronize修饰的方法在某一时刻只允许一个线程访问,访问该方法的其他线程都会发生阻塞,知道当前线程访问完毕后,其他线程才有机会执行方法。
代码块示例:
class DeadLockThread implements Runnable{
static Object chopsticks=new Object(); //定义object类型的chopsticks锁对象
static Object knifeAndFork=new Object(); //定义object类型的knifeAndFork锁对象
private boolean flag;//定义boolean类型的变量flag
DeadLockThread(boolean flag) {
this.flag=flag;}
public void run(){
if(flag){
while(true){
synchronized (chopsticks) {//chopsticks锁对象上的同步代码块
System.out.println(Thread.currentThread().getName()+"---if---chopsticks");
synchronized (knifeAndFork) {//knifeAndFork锁对象上的同步代码块
System.out.println(Thread.currentThread().getName()+"---if---knifeAndFork");
}
}
}
}
else{
while (true) {
synchronized (knifeAndFork) {
System.out.println(Thread.currentThread().getName()+"---else---knifeAndFork");
synchronized (chopsticks) {
System.out.println(Thread.currentThread().getName()+"---else---chopsticks");
}
}
}
}
}
}
public class sisuo {
public static void main(String[] args) {
// TODO 自动生成的方法存根
//创建连个DeadLockThread对象
DeadLockThread d1=new DeadLockThread(true);
DeadLockThread d2=new DeadLockThread(false);
//创建并开启两个线程
new Thread(d1,"Chinese").start();
new Thread(d2,"American").start();
}
}
- 7.多线程通信
示例代码:
class Input implements Runnable{
private Storage st;
private int num;
Input(Storage st) {
this.st=st;
}
public void run() {
while(true){
st.put(num++);
}
}
}
class output implements Runnable{
private Storage st;
output(Storage st){
this.st=st;
}
public void run(){
while(true)
st.get();
}
}
class Storage {
//数据存储数组
private int [] cells=new int[10];
//inPos表示存入时数组的下标,outpos表示数组取出时数组的下标
private int inPos,outPos,count;
//定义一个put()方法向数组中存入数据
public synchronized void put(int num){
try {
while(count==cells.length)
{
this.wait();
}
cells[inPos]=num;
System.out.println("在cells["+inPos+"]中放入数据---"+cells[inPos]);
inPos++; //存完元素让位置加1
if(inPos==cells.length)
inPos=0;//当inPos为数据长度时,将其置为0
count++;
this.notify();
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
//定义一个get()方法从数组中取出数据
public synchronized void get()
{
try {
while(count==0){
this.wait();
}
int data =cells[outPos];
System.out.println("从celss["+outPos+"]中取出数据"+data);
outPos++; //取完元素让位置加1
if(outPos==cells.length)
outPos=0;
count--;
this.notify();
} catch (Exception e) {
// TODO: handle exception
}
}
}
public class yunxing {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成的方法存根
Storage st=new Storage();
Input input=new Input(st);
output output=new output(st);
new Thread(input).start();
new Thread(output).start();
}
}