1. 线程的基本概念
2.线程的创建和启动
优先使用接口而不是继承
线程启动(调用Thread Start()方法):
交替输出
方法调用:
被调用的方法执行完再执行main函数的方法
package com.thread;
/**
* 方法一
* */
public class TestThread {
public static void main(String[] args) {
//方法一
Runner1 r1 = new Runner1();
Thread t = new Thread(r1);
t.run(); //执行完调用的方法再继续执行,r1、r2是两个子线程,和main方法后面的代码交替执行
t.start();
//方法二
Runner2 r2 = new Runner2();
r2.start();
for(int i=0;i<100;i++)
System.out.println("main:"+i);
}
}
class Runner1 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++)
System.out.println("Runner1:"+i);
}
}
class Runner2 extends Thread{
public void run() {
for(int i=0;i<100;i++)
System.out.println("Runner2:"+i);
}
}
/*
* 先执行调用的r1的run()方法进行输出
* 然后r1线程、r2线程、main线程轮流交替执行(输出)
* */
3.线程的状态控制
Join相当于方法调用
4.线程同步
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。
当一个线程在使用加锁的方法时,其它线程仍可以访问这个这个对象的其他未加锁方法。synchronized只锁定对象,每个对象只有一个锁(lock)与之相关联,因此同一个对象的不同加锁方法执行顺序有先后。访问同一个资源的所有方法都要仔细考虑是否加锁。
package com.thread;
/*
* 两个线程访问同一个timer对象
* */
public class TestSync implements Runnable{
Timer timer = new Timer();
public static void main(String[] args) {
TestSync test = new TestSync();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
@Override
public void run() {
timer.add(Thread.currentThread().getName());
}
}
class Timer{
private static int num = 0;
public synchronized void add(String name) { //对当前对象加锁
//synchronized(this) {//另一种加锁方式
num++;
try {
Thread.sleep(1); //访问该资源的线程休眠一毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+",你是第"+num+"个使用timer的线程");
//}
}
}
/*
不加锁时
运行可能的结果一(t1 num++后休眠,[t2 num++、休眠、输出],然后t1输出)
t2,你是第2个使用timer的线程
t1,你是第2个使用timer的线程
或结果二
t1,你是第1个使用timer的线程
t2,你是第1个使用timer的线程
或...
加锁时
t1,你是第1个使用timer的线程
t2,你是第2个使用timer的线程
*/
死锁
package com.thread;
public class TestDeadLock implements Runnable{
//用以区分,一个线程类模拟两个线程
public int flag = 0;
//要访问的两个对象(静态公共资源)
static Object o1 = new Object();
static Object o2 = new Object();
public void run() {
System.out.println("flag="+flag);
//线程1对o1加锁,只要再对o2加锁即可完成进程
if (flag == 1) {
synchronized(o1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o2) {
System.out.println("1");
}
}
}
//线程2对o2加锁,只要再对o1加锁即可完成进程
if (flag == 0) {
synchronized(o2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o1) {
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
TestDeadLock td1 = new TestDeadLock();
TestDeadLock td2 = new TestDeadLock();
td1.flag=1;
td2.flag=0;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
//启动两个线程
t1.start();
t2.start();
}
}
/**
flag=1
flag=0
//程序卡住
*/
5.生产者消费者问题
多个线程对同一资源进行生产和消费
package com.thread;
/**
* 生产者消费者问题
*1.往框内放的push()方法要加锁
*2.往筐外取的pop()方法要加锁
*3.食物筐满则停止生产,消费出位置再生产
*4.食物筐空则停止消费,生产出食物载消费
*/
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(c).start();
}
}
/**
* 面包
* */
class Bread{
int id;
Bread(int id){
this.id = id;
}
@Override
public String toString() {
return "Bread [id=" + id + "]";
}
}
/**
* 面包筐
* */
class SyncStack{
Bread[] breads = new Bread[6];
int index = 0;
public synchronized void push(Bread bread) {
while (index == breads.length) { //面包筐生产满了
try {
//访问当前加锁对象的进程进入wait状态(object对象的方法)
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//叫醒正在当前对象内wait的进程(object对象的方法)
this.notifyAll();
breads[index] = bread; //不加锁这里容易出问题
index++;
}
public synchronized Bread pop() {
while (index == 0) { //面包筐消费空了
try {
this.wait();;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--; //不加锁这里容易出问题
return breads[index];
}
}
/**
* 生产者
* */
class Producer implements Runnable{
SyncStack ss = null;
Producer(SyncStack ss){
this.ss = ss;
}
@Override
public void run() {
for(int i=1;i<=20;i++) {
Bread bread = new Bread(i);
ss.push(bread);
System.out.println("生产"+bread);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 消费者
* */
class Consumer implements Runnable{
SyncStack ss = null;
Consumer(SyncStack ss){
this.ss = ss;
}
@Override
public void run() {
for(int i=1;i<=20;i++) {
Bread bread = ss.pop();
System.out.println("消费"+bread);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
生产Bread [id=1]
消费Bread [id=1]
生产Bread [id=2]
消费Bread [id=3]
生产Bread [id=3]
生产Bread [id=4]
生产Bread [id=5]
消费Bread [id=4]
生产Bread [id=6]
消费Bread [id=7]
生产Bread [id=7]
生产Bread [id=8]
消费Bread [id=8]
生产Bread [id=9]
生产Bread [id=10]
消费Bread [id=10]
生产Bread [id=11]
生产Bread [id=12]
消费Bread [id=12]
生产Bread [id=13]
消费Bread [id=13]
生产Bread [id=14]
消费Bread [id=14]
生产Bread [id=15]
消费Bread [id=15]
生产Bread [id=16]
消费Bread [id=16]
生产Bread [id=17]
消费Bread [id=17]
生产Bread [id=18]
消费Bread [id=18]
生产Bread [id=19]
消费Bread [id=19]
生产Bread [id=20]
消费Bread [id=20]
消费Bread [id=11]
消费Bread [id=9]
消费Bread [id=6]
消费Bread [id=5]
消费Bread [id=2]
*/
6.总结
wait sleep 区别
(1)所属类不同
wait() java.lang.object
sleep() java.lang.Thread
(2)wait 时别的线程可以访问锁定对象(调用wait方法必须锁定该对象)
sleep时别的线程不可以访问锁定对象
线程/进程的概念
创建和启动线程的方法
sleep
join
yield
synchronized
wait
notify/notifyAll