JAVA之创建线程的两种方法

时间:2021-11-28 20:06:39

前提:

1:这两种方法都是通过重写run(),在run()方法中实现运行在线程上的代码

2:Runnable相比于Thread更适合多个相同程序代码去处理同一个资源的情况,通常采用Runnable

3:被synchronized 修饰的方法在某一时刻只允许一个线程访问

多线程

 1.什么是线程

        线程是程序执行的一条路径, 一个进程中可以包含多条线程

        多线程并发执行可以提高程序的效率, 可以同时完成多项工作

2. 多线程并行和并发的区别

1) 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)

2) 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。

3. Java程序运行原理

        Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个“主线程” ,然后主线程去调用某个类的main 方法。

4. JVM的启动是多线程的吗

        JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

 

多线程程序实现的方式

1.继承Thread

        * 定义类继承Thread

        * 重写run方法

        * 把新线程要做的事写在run方法中

        * 创建线程对象

        * 开启新线程, 内部会自动执行run方法

classMyThread extends Thread {                           //1,定义类继承Thread

                                public voidrun() {                    //2,重写run方法

                                        for(inti = 0; i < 3000; i++) {     //3,将要执行的代码,写在run方法中

                                                System.out.println("threadrunning");

                                        }

                                }

                        }

main: 

MyThreadmt = new MyThread();           //4,创建自定义类的对象

mt.start();        //5,开启线程                                 

for(inti = 0; i < 3000; i++) {

    System.out.println("main");

  }

2.实现Runnable

classMyRunnable implements Runnable {    //1,自定义类实现Runnable接口

                                @Override

                                public voidrun() {            //2,重写run方法

                                        for(inti = 0; i < 3000; i++) {     //3,将要执行的代码,写在run方法中

                                                System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");

                                        }

                                }

                               

                        }

main:

MyRunnablemr = new MyRunnable();           //4,创建自定义类对象

//Runnabletarget = new MyRunnable();

Thread t= new Thread(mr);            //5,将其当作参数传递给Thread的构造函数

t.start();                                           //6,开启线程

for(inti = 0; i < 3000; i++) {

        System.out.println("bb");

}

两种方式的区别

* 继承Thread

        * 好处是:可以直接使用Thread类中的方法,代码简单

        * 弊端是:如果已经有了父类,就不能用这种方法

* 实现Runnable接口

        * 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的

        * 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

 

匿名内部类实现线程的两种方式

1. 继承Thread类

                new Thread() {         //1,new 类(){}继承这个类

                        public void run() {            //2,重写run方法

                                for(int i = 0; i< 3000; i++) {     //3,将要执行的代码,写在run方法中

                                        System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");

                                }

                        }

                }.start();

 

2. 实现Runnable接口

                new Thread(new Runnable(){         //1,new 接口(){}实现这个接口

                        public void run() {            //2,重写run方法

                                for(int i = 0; i< 3000; i++) {     //3,将要执行的代码,写在run方法中

                                        System.out.println("bb");

                                }

                        }

                }).start();

 

常用方法:

1. 获取名字和设置名字

* 1).获取名字

        * 通过getName()方法获取线程对象的名字

* 2).设置名字

* setName()方法

        * 通过构造函数可以传入String类型的名字

2. 获取当前线程的对象

在Runnable中不能直接使用this.getName(),可以先获取当前正在执行的线程

Thread.currentThread(),主线程也可以获取                      

3. 休眠线程

*Thread.sleep(毫秒,纳秒), windows支持毫秒

 * 使用过程的会抛出异常,必须trycatch。

4. 礼让线程

* yield让出cpu

5. 加入线程

*join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续

*join(int), 可以等待指定的毫秒之后继续

6. 设置线程的优先级

*setPriority()设置线程的优先级

               

同步代码块

* 1.什么情况下需要同步

        * 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.

        * 如果两段代码是同步的, 那么同一时间只能执行一段,在一段代码没执行结束之前, 不会执行另外一段代码.

* 2.同步代码块

        * 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块

        * 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的

 

以火车票售票问题为例:

1. privatestatic int ticket = 10;   车票是静态的

2. 同步锁问题:

1)synchronized(this) :new出来四个线程代表的是4个this,不是一个this。

2) Objectlock = new Object();   synchronized (obj);4个线程,每个线程对象都有自己的成员变量lock,因此可以加static,所有对象共享同一个锁。

3. 处理:

1)synchronized(MyTicket.class),锁的是该类。

2) privatestatic Object lock = new Object();   静态锁。

 

同步方法:

* 使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的

 

//非静态的同步方法的锁对象是?

//答:非静态的同步方法的锁对象是this

//静态的同步方法的锁对象是什么?

//是该类的字节码对象

 

 

死锁

* 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁

        * 尽量不要嵌套使用

 

以前的线程安全的类

        Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)

        * Vector是线程安全的,ArrayList是线程不安全的

        * StringBuffer是线程安全的,StringBuilder是线程不安全的

        * Hashtable是线程安全的,HashMap是线程不安全的



继承Thread类创建多线程

package rjxy;

/*
 * 通过继承Thread类的方式创建两个线程,在Thread构造方法中指定线程的名字,并将这两个线程的名字打印出来。

 */

public class Thread1 {
public static void main(String[] args) {
MyThread t1=new MyThread("我是线程1");
MyThread t2=new MyThread("我是线程2");
t1.start();
t2.start();
}
}


class MyThread extends Thread{

public MyThread() {
super();
}

public MyThread(String name) {
super(name);
}

public void run(){
for (int i = 0; i <10; i++) {
System.out.println(this.getName());
}
}

}

实现Runnable接口创建多线程

package rjxy;

public class Thread2 {
/*
* 通过实现Runnable接口的方式创建一个线程,要求main线程打印100次“main”,新线程打印50次“new”
*/
public static void main(String[] args) {
MyRunnable mr=new MyRunnable();
Thread t1=new Thread(mr);
t1.start();
 
for (int i = 0; i < 100; i++) {
System.out.println("main");
}
}
}

class MyRunnable implements Runnable{

@Override
public void run() {
for (int i = 0; i <50; i++) {
System.out.println("new");
}

}

}


一个栗子


package rjxy;

public class Thread3 { 
/*
* 模拟3个来时同时分发80份笔记,每个老师相当于一个线程。
*/
public static void main(String[] args) {
MyRunnable_paper mrp=new MyRunnable_paper();
//新建是三个线程
new Thread(mrp,"赵老师").start();
new Thread(mrp,"张老师").start();
new Thread(mrp,"李老师").start();
}


}
class MyRunnable_paper implements Runnable{
private static int num=80;

@Override
public void run() {
while(true){
papers();
}
}
public synchronized void papers(){
 
if (num>0) {
System.out.println(Thread.currentThread().getName()+"正在发"+num--);
}else{
System.exit(0);
}

}

}

第二个栗子

package rjxy;


public class Thread4 {

// * 编写10个线程,第一个线程从1加到10, 第2个线程从11加到20,......第10个线程从91加到100,最后把10个线程的结果相加。

public static void main(String[] args) throws InterruptedException {
Thread [] mys=new Thread[10];
//多态
for (int i = 0; i < 10; i++) {
mys[i]=new MyThread_add(i*10+1);
mys[i].start();
}
for (int i = 0; i < 10; i++) {
mys[i].join();
}

System.out.println( MyThread_add.getSum());

}


}
class  MyThread_add extends Thread{
private int startNum;
private static int sum=0;

public MyThread_add() {
super();
// TODO Auto-generated constructor stub
}
//初始化开始值
public MyThread_add(int startNum) {
this.startNum=startNum;
}

public  void run(){
int sum=0;
for (int i = 0; i <10; i++) {
sum+=startNum+i;
}
add(sum);
}
public synchronized void add(int num){
sum+=num;
}
public static int getSum(){
return sum;
}

}


第三个栗子


package yu;

//窗口售票的栗子


public class Main {
public static void main(String[] args) {

MyRunnable task =new MyRunnable();//创建线程的任务类对象

new Thread(task,"窗口 1").start();//创建线程并起名
new Thread(task,"窗口 2").start();//创建线程并起名
new Thread(task,"窗口 3").start();//创建线程并起名
new Thread(task,"窗口 4").start();//创建线程并起名

}
}


class MyRunnable implements Runnable{
static private int tickets=500;//票数

@Override
public void run() {
while(true) {//无数次调用直到system.exit(0)
sendTicket();
}
}

//定义售票的方法,该方法某一时刻允许一个线程访问
public synchronized void sendTicket() {
try {
Thread.sleep(10);//线程休眠10秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//售票
if (tickets>0) {
System.out.println(Thread.currentThread().getName()+"---卖出去的票"+tickets--);
}else {
System.exit(0);
}
}


}