前提:
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;
}
}
第三个栗子
//窗口售票的栗子
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);
}
}
}