Day24(线程)
多线程
多线程的引入
A: 什么是线程
* 线程就是程序执行的一条路径, 一个进程中可以包括多条线程
* 多线程并发执行可以提高程序的效率, 可以同时完成多项工作
B: 多线程的应用场景
* 红蜘蛛同时共享屏幕给多个电脑
* 迅雷开启多线程同时下载
* QQ同时和多人一起视频
多线程并行和并发的区别
*并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在运行,如果需要并行运行,需要多核 cpu
*并发就是几个任务在同一时间段内同时运行
Java程序运行原理和JVM的启动是多线程吗?
A: JAVA程序运行原理
* Java命令会启动Java虚拟机, 启动JVM, 等于启动了一个应用程序, 也就是启动了一个进程, 该进程会主动启动一个"主线程" , 然后主线程会去调用一个main方法
B: JVM的启动是多线程的吗?
JVM启动至少启动了垃圾回收线程和主线程, 所以是多线程的
多线程程序的实现方式
多线程程序实现方式1
A: 将类声明成Thread的子类, 然后重写run ( ) 方法
public class Demo2_Thread {
public static void main ( String[ ] args) {
MyThread mt = new MyThread ( ) ;
mt. start ( ) ;
for ( int i = 0 ; i < 1000 ; i++ ) {
System. out. println ( "bb" ) ;
}
}
}
class MyThread extends Thread {
public void run ( ) {
for ( int i = 0 ; i < 1000 ; i++ ) {
System. out. println ( "aaaaaaaaaaaa" ) ;
}
}
}
多线程实现方式2
A: 创建线程的另一种方式声明实现Rannable接口的类. 该类然后实现run方法.
public class Demo3_Thread {
public static void main ( String[ ] args) {
MyRunnable mr = new MyRunnable ( ) ;
Thread t = new Thread ( mr) ;
t. start ( ) ;
for ( int i = 0 ; i < 1000 ; i++ ) {
System. out. println ( "bb" ) ;
}
}
}
class MyRunnable implements Runnable {
@Override
public void run ( ) {
for ( int i = 0 ; i < 1000 ; i++ ) {
System. out. println ( "aaaaaaaaaaaa" ) ;
}
}
}
实现Runnable的原理
*查看原码
*1.看Thread类的构造函数,传递了Runnable接口的引用
*2.通过init()方法找到传递的target给成员变量的target赋值
*3.查看run方法,发现run方法中有判断,如果target不为null就会用Runnable接口子类对象的run方法
两种方式的区别
* 查看源码
* 1. 继承Thread: 由于子类重写了Thread类的run ( ) , 当调用start ( ) 时, 直接找子类的run ( ) 方法;
* 2. 实现Runnable: 构造函数中传入了Runnable的引用, 成员变量记住了它, start ( ) 调用run ( ) 方法时内部判断成员变量Runnable的引用是否为空, 不为空时编译时看的是Runnable的run ( ) 方法, 运行时执行的是子类的run ( ) 方法;
继承Thread类
* 好处是: 可以直接使用Thread类中的方法, 代码简单
* 弊端是: 如果已经有了父类, 就不能用这种方法
实现Runnable接口
* 好处是: 即使自己定义的线程类有了父类也没关系, 因为有了父类也可以实现接口, 而且接口可以多实现的
* 弊端是: 不能直接使用Thread中的方法, 需要先获取线程对象后, 才能得到Thread的方法, 代码复杂
匿名内部类实现多线程的两种方式
* 继承Thread
new Thread ( ) {
public void run ( ) {
for ( int i = 0 ; i < 1000 ; i++ ) {
System. out. println ( "aaaaaaaaaaaaaa" ) ;
}
}
} . start ( ) ;
* 实现Runnable接口
new Thread ( new Runnable ( ) {
public void run ( ) {
for ( int i = 0 ; i < 1000 ; i++ ) {
System. out. println ( "bb" ) ;
}
}
} ) . start ( ) ;
多线程中的一些方法
获取和设置线程对象的名字
A: 获取名字
* 通过getName ( ) 方法获取线程对象的名字;
B: 设置名字:
* 通过构造函数可以传入String类型的名字
public static void demo1 ( ) {
new Thread ( "王五" ) {
@Override
public void run ( ) {
System. out. println ( this . getName ( ) + "....aaaaaaaaa" ) ;
}
} . start ( ) ;
new Thread ( "赵六" ) {
@Override
public void run ( ) {
System. out. println ( this . getName ( ) + "....bb" ) ;
}
} . start ( ) ;
}
* 通过setName ( String) 方法这只线程对象名字
Thread t1 = new Thread ( ) {
@Override
public void run ( ) {
System. out. println ( this . getName ( ) + "....aaaaaaaaaaaaa" ) ;
}
} ;
Thread t2 = new Thread ( ) {
@Override
public void run ( ) {
System. out. println ( this . getName ( ) + "....bb" ) ;
}
} ;
t1. setName ( "张三" ) ;
t2. setName ( "李四" ) ;
t1. start ( ) ;
t2. start ( ) ;
获取当前线程对象
* Thread. currentThread ( ) ,
public static void main ( String[ ] args) {
new Thread ( ) {
public void run ( ) {
System. out. println ( getName ( ) + "....aaaaaa" ) ;
}
} . start ( ) ;
new Thread ( new Runnable ( ) {
public void run ( ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + "...bb" ) ;
}
} ) . start ( ) ;
Thread. currentThread ( ) . setName ( "我是主线程" ) ;
System. out. println ( Thread. currentThread ( ) . getName ( ) ) ;
}
休眠线程
A:Thread.sleep(毫秒,纳秒) //控制当前线程休眠若干毫秒
public static void demo1() throws InterruptedException {
for(int i = 20; i >= 0; i--) {
Thread.sleep(1000);
System.out.println("倒计时第" +i + "秒");
}
}
守护线程
setDaemon(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程执行都执行结束后,自动退出
Thread t1 = new Thread() {
public void run() {
for(int i = 0; i < 2; i++) {
System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaa");
}
}
};
Thread t2 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println(getName() + "...bb");
}
}
};
t2.setDaemon(true); //设置为守护线程
t1.start();
t2.start();
加入线程
* join ( ) , 当前线程暂停, 等待执行的线程执行结束后, 当前线程再继续执行
* join ( int ) , 可以等待指定的毫秒后继续执行
final Thread t1 = new Thread ( ) {
@Override
public void run ( ) {
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( getName ( ) + "...aaaaaaaaaaaaa" ) ;
}
}
} ;
Thread t2 = new Thread ( ) {
@Override
public void run ( ) {
for ( int i = 0 ; i < 10 ; i++ ) {
if ( i == 2 ) {
try {
t1. join ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
System. out. println ( getName ( ) + "...bb" ) ;
}
}
} ;
t1. start ( ) ;
t2. start ( ) ;
礼让线程
* yield让出CPU
设置线程的优先级
*setPrority
同步代码块
A: 什么情况下需要同步代码块?
当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行过程中CPU不要切换到其他线程工作, 这时就需要同步
B: 同步代码块
* 使用synchronized 关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
* 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
public class Demo1_Synchronized {
public static void main ( String[ ] args) {
final Printer p = new Printer ( ) ;
new Thread ( ) {
@Override
public void run ( ) {
while ( true ) {
p. print1 ( ) ;
}
}
} . start ( ) ;
new Thread ( ) {
@Override
public void run ( ) {
while ( true ) {
p. print2 ( ) ;
}
}
} . start ( ) ;
}
}
class Printer {
Demo d = new Demo ( ) ;
public void print1 ( ) {
synchronized ( d) {
System. out. print ( "好" ) ;
System. out. print ( "好" ) ;
System. out. print ( "学" ) ;
System. out. print ( "习" ) ;
System. out. print ( "\r\n" ) ;
}
}
public void print2 ( ) {
synchronized ( d) {
System. out. print ( "天" ) ;
System. out. print ( "天" ) ;
System. out. print ( "向" ) ;
System. out. print ( "上" ) ;
System. out. print ( "\r\n" ) ;
}
}
}
class Demo {
}
同步方法
class Printer2 {
Demo d = new Demo ( ) ;
public static synchronized void print1 ( ) {
System. out. print ( "好" ) ;
System. out. print ( "好" ) ;
System. out. print ( "学" ) ;
System. out. print ( "习" ) ;
System. out. print ( "\r\n" ) ;
}
public static void print2 ( ) {
synchronized ( Printer2. class ) {
System. out. print ( "天" ) ;
System. out. print ( "天" ) ;
System. out. print ( "向" ) ;
System. out. print ( "上" ) ;
System. out. print ( "\r\n" ) ;
}
}
}
线程安全
* 多线程并发操作同一数据时, 就有可能出现线程安全问题
* 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
public class Demo3_Ticket {
public static void main ( String[ ] args) {
new Ticket ( ) . start ( ) ;
new Ticket ( ) . start ( ) ;
new Ticket ( ) . start ( ) ;
new Ticket ( ) . start ( ) ;
}
}
class Ticket extends Thread {
private static int ticket = 100 ;
@Override
public void run ( ) {
while ( true ) {
synchronized ( Ticket. class ) {
if ( ticket <= 0 ) {
break ;
}
try {
Thread. sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( getName ( ) + "...这是第" + ticket-- + "号票" ) ;
}
}
}
}
public class Demo4_Ticket {
public static void main ( String[ ] args) {
MyTicket mt = new MyTicket ( ) ;
new Thread ( mt) . start ( ) ;
new Thread ( mt) . start ( ) ;
new Thread ( mt) . start ( ) ;
new Thread ( mt) . start ( ) ;
}
}
class MyTicket implements Runnable {
private int tickets = 100 ;
@Override
public void run ( ) {
while ( true ) {
synchronized ( this ) {
if ( tickets <= 0 ) {
break ;
}
try {
Thread. sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "...这是第" + tickets-- + "号票" ) ;
}
}
}
}
多线程死锁
* 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
* 尽量不要嵌套使用
public class Demo5_DeadLock {
private static String s1 = "筷子左" ;
private static String s2 = "筷子右" ;
public static void main ( String[ ] args) {
new Thread ( ) {
public void run ( ) {
while ( true ) {
synchronized ( s1) {
System. out. println ( getName ( ) + "...获取" + s1 + "等待" + s2) ;
synchronized ( s2) {
System. out. println ( getName ( ) + "...拿到" + s2 + "开吃" ) ;
}
}
}
}
} . start ( ) ;
new Thread ( ) {
public void run ( ) {
while ( true ) {
synchronized ( s2) {
System. out. println ( getName ( ) + "...获取" + s2 + "等待" + s1) ;
synchronized ( s1) {
System. out. println ( getName ( ) + "...拿到" + s1 + "开吃" ) ;
}
}
}
}
} . start ( ) ;
}
}
线程安全的类回顾
Vector,StringBuffer,Hashtable,Collections.synchronized(xxx)
*Vector是线程安全的,ArrayList是线程不安全的
*StringBuffer是线程安全的,StringBuilder是线程不安全的
Hashtable是线程安全的,HashMap是线程不安全的
Collections.synchronized(xxx)可将不安全的转换成安全的