0. 线程概述:
- 进程: 是一个正在执行中的程序. 每一个进程执行都有一个执行顺序. 该顺序是一个执行路径, 或者叫一个控制单元.
- 线程: 就是进程中的一个独立的控制单元. 线程在控制着进程的执行.
- 一个进程中至少有一个线程.
1. 创建线程的方法:
- 继承Thread类: 子类覆盖父类中的run方法, 将线程运行的代码存放在run中. 建立子类对象的同时线程也被创建. 通过调用start方法开启线程(start的两个作用: 启动线程, 调用run方法).
- 实现Runnable接口: 子类覆盖接口中的run方法. 通过Thread类创建线程, 并实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数. Thread类对象调用start方法开启线程.
- * 继承方式和实现方式有什么区别?
实现方式好处: 避免了单继承的局限性. 在定义线程时, 建议使用实现方式. (还可以让多个线程处理同一个问题).
2. 用Runnable接口例子:
/*
* 需求:简单的卖票程序.
* 多个窗口同时卖票.
*/
class Ticket implements Runnable
{
private int tick = 100;
public void run()
{
while(true)
{
if(tick>0)
System.out.println(Thread.currentThread().getName() + " sale : " + tick--);
}
}
}
public class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
3. 线程的四种状态:
新建(New)→就绪(Runnable)→运行(Running)→阻塞(Blocked)→死亡(Dead)
4. 同步代码块
// 格式
synchronized(对象)
{
需要被同步的代码
}
- 对象如同所锁. 持有锁的线程可以再同步中执行, 没有持有锁的线程即使获取cpu的执行权, 也进不去, 因为没有获取锁.
- 好处: 解决了多线程的安全问题. 弊端: 多线程需要判断锁, 较消耗资源.
5. 同步函数
格式: 在函数上加上synchronized修饰符即可.
* 同步函数用的锁是this.
* 静态同步函数用的锁是该方法所在类的"类名.class"字节码文件对象(是Class类的实例).
6. 死锁
定义: 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁.
例程:
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
(true)
{
synchronized(MyLock.locka)
{
System.out.println("if locka");
synchronized(MyLock.lockb)
{
System.out.println("if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)
{
System.out.println("else lockb");
synchronized(MyLock.locka)
{
System.out.println("else locka");
}
}
}
}
}
}
// 提供锁
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
// 测试死锁
public class DeadLockTest {
public static void main(String[] args) {
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
7. * 线程间通信
wait(): 等待.
notify(): 唤醒. 唤醒随机的一个等待线程.
notifyAll(): 唤醒所有.
- 这三个方法都是用在同步中, 因为要对持有监视器(锁)的线程操作. 所以要使用再同步中, 因为只有同步才具有锁.
- 为什么这三个方法不是定义在Thread类中, 而是在Object类中?
答: 因为这些方法在操作同步中线程时, 都必须要表示它们所操作线程持有的锁, 一个锁上的被等待线程, 只可以被同一个锁上的notify唤醒, 不可以被不同锁中的线程唤醒.
也就是说, 等待和唤醒必须是同一个锁.
而锁可以是任意对象, 所以可以被任意对象调用, 定义在Object类中.
例子:
// Res的对象作为被操作的资源和锁
class Res
{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r)
{
if(r.flag)
// 调用锁r的wait()方法
try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}
if(x==0)
{
r.name = "mike";
r.sex = "man";
}
else
{
r.name = "丽丽";
r.sex = "女女女女";
}
x = (x+1)%2;
r.flag = true;
// 调用锁r的notigy()方法
r.notify();
}
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
synchronized(r)
{
if(!r.flag)
// 调用锁r的wait()方法
try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(r.name + "...." + r.sex);
r.flag = false;
// 调用锁r的notigy()方法
r.notify();
}
}
}
}
// 测试
public class InputOutputDemo {
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
上述代码很乱, 优化后是酱紫:
// Res的对象作为被操作的资源
class Res
{
private String name;
private String sex;
boolean flag = false;
public synchronized void set(String name, String sex)
{
if(flag)
try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(name+"......."+sex);
flag = false;
this.notify();
}
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
if(x==0)
r.set("mike", "man");
else
r.set("丽丽", "女女女女");
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
// 测试
public class InputOutputDemo {
public static void main(String[] args) {
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
8. 停止线程
因为开启多线程运行, 运行代码通常是循环结构, 只要控制住循环, 就可以让run方法结束, 也就是线程结束.
就像这样:
class StopThread implements Runnable
{
private boolean flag = true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+".....run");
}
}
public void changeFlag()
{
flag = false;
}
}
public class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while(true)
{
if(num++ == 60)
{
st.changeFlag();
break;
}
System.out.println(Thread.currentThread().getName()+"....."+ num);
}
}
}
* Thread类中提供了interrupt()方法, 用于中断sleep()、wait()和join()等.
9. join()方法
作用: 当A线程执行到了B线程的.join()方法时, A就会等待. 等B线程都执行完, A才会执行. join可以用来临时加入线程执行.
例子:
class Demo1 implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().getName()+"..."+x);
}
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Demo1 d = new Demo1();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
//t1.join(); //t1要申请加入. 就是t1要加进来,抢走申请cpu执行权. 主线程运行到这句, 然后等待t1结束才能继续执行.
t2.start();
t1.join(); //主线程释放执行权给t1. 执行这句后, t1和t2交替执行, 直到t1执行完后主线程又拿到执行权.
for(int x=0; x<80; x++)
{
System.out.println("main....." + x);
}
System.out.println("over");
}
}
10. yield()方法
作用: 暂停正在执行的线程对象, 并执行其他线程.
- yield将线程转为就绪状态. 只是让线程暂停一下, 如果程序中不存在优先级比该线程高或相同的线程, 则此让步的线程继续运行; 如果有, 另算 (给优先级更高的线程让步).
- sleep() 方法声明抛出InterruptedException异常, yield() 方法没抛出异常.
例子:
public class YieldTest extends Thread
{
public YieldTest(String name)
{
super(name);
}
public void run()
{
for (int i = 0; i < 50; i++)
{
System.out.println(getName() + " " + i);
if (i == 20)
{
Thread.yield();
}
}
}
public static void main(String[] args) throws Exception
{
YieldTest yt1 = new YieldTest("HIGH");
//yt1.setPriority(Thread.MAX_PRIORITY);
yt1.start();
YieldTest yt2 = new YieldTest("LOW");
//yt2.setPriority(Thread.MIN_PRIORITY);
yt2.start();
}
}